Improve test logs (#2826)

- Include hosting logs in some tests that where previously missing them
- Prevent duplicate logs from Mock CallBase an CompositeKestrelTrace
- Log ports used by transport functional tests
- Add file logging to HTTP/2 "unit" tests
This commit is contained in:
Stephen Halter 2018-08-15 18:31:56 -07:00 committed by GitHub
parent 0c2923135b
commit 5becb72107
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 932 additions and 1306 deletions

View File

@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("InMemory.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Sockets.BindTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Libuv.BindTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Kestrel.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("PlatformBenchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -3,7 +3,9 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Libuv.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("InMemory.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Microsoft.AspNetCore.Server.Kestrel.Core.Tests</AssemblyName>
<AssemblyName>Core.Tests</AssemblyName>
<RootNamespace>Microsoft.AspNetCore.Server.Kestrel.Core.Tests</RootNamespace>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -18,10 +18,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
</ItemGroup>

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 Microsoft.Extensions.Logging.Testing;
[assembly: ShortClassName]

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging;
@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
private async Task TestBadRequest(string request, string expectedResponseStatusCode, string expectedExceptionMessage, string expectedAllowHeader = null)
{
BadHttpRequestException loggedException = null;
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
mockKestrelTrace
.Setup(trace => trace.IsEnabled(LogLevel.Information))
.Returns(true);

View File

@ -3,35 +3,25 @@
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
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.Transport.Abstractions.Internal;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class Http2ConnectionTests : IDisposable, IHttpHeadersHandler
public class Http2ConnectionTests : Http2TestBase
{
private static readonly string _largeHeaderValue = new string('a', HPackDecoder.MaxStringOctets);
private static readonly IEnumerable<KeyValuePair<string, string>> _postRequestHeaders = new[]
{
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
@ -49,19 +39,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
new KeyValuePair<string, string>("expect", "100-continue"),
};
private static readonly IEnumerable<KeyValuePair<string, string>> _browserRequestHeaders = new[]
{
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
new KeyValuePair<string, string>("user-agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"),
new KeyValuePair<string, string>("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
new KeyValuePair<string, string>("accept-language", "en-US,en;q=0.5"),
new KeyValuePair<string, string>("accept-encoding", "gzip, deflate, br"),
new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
};
private static readonly IEnumerable<KeyValuePair<string, string>> _requestTrailers = new[]
{
new KeyValuePair<string, string>("trailer-one", "1"),
@ -102,253 +79,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
private static readonly byte[] _noData = new byte[0];
private static readonly byte[] _maxData = Encoding.ASCII.GetBytes(new string('a', Http2Frame.MinAllowedMaxFrameSize));
private readonly TestApplicationErrorLogger _logger;
private readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
private readonly HPackDecoder _hpackDecoder;
private readonly ConcurrentDictionary<int, TaskCompletionSource<object>> _runningStreams = new ConcurrentDictionary<int, TaskCompletionSource<object>>();
private readonly Dictionary<string, string> _receivedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, string> _decodedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<int> _abortedStreamIds = new HashSet<int>();
private readonly object _abortedStreamIdsLock = new object();
private readonly TaskCompletionSource<object> _closingStateReached = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly TaskCompletionSource<object> _closedStateReached = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly RequestDelegate _noopApplication;
private readonly RequestDelegate _readHeadersApplication;
private readonly RequestDelegate _readTrailersApplication;
private readonly RequestDelegate _bufferingApplication;
private readonly RequestDelegate _echoApplication;
private readonly RequestDelegate _echoWaitForAbortApplication;
private readonly RequestDelegate _largeHeadersApplication;
private readonly RequestDelegate _waitForAbortApplication;
private readonly RequestDelegate _waitForAbortFlushingApplication;
private readonly RequestDelegate _waitForAbortWithDataApplication;
private MemoryPool<byte> _memoryPool;
private DuplexPipe.DuplexPipePair _pair;
private Http2ConnectionContext _connectionContext;
private Http2Connection _connection;
private Task _connectionTask;
public Http2ConnectionTests()
{
_noopApplication = context => Task.CompletedTask;
_readHeadersApplication = context =>
{
foreach (var header in context.Request.Headers)
{
_receivedHeaders[header.Key] = header.Value.ToString();
}
return Task.CompletedTask;
};
_readTrailersApplication = async context =>
{
using (var ms = new MemoryStream())
{
// Consuming the entire request body guarantees trailers will be available
await context.Request.Body.CopyToAsync(ms);
}
foreach (var header in context.Request.Headers)
{
_receivedHeaders[header.Key] = header.Value.ToString();
}
};
_bufferingApplication = async context =>
{
var data = new List<byte>();
var buffer = new byte[1024];
var received = 0;
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
data.AddRange(new ArraySegment<byte>(buffer, 0, received));
}
await context.Response.Body.WriteAsync(data.ToArray(), 0, data.Count);
};
_echoApplication = async context =>
{
var buffer = new byte[Http2Frame.MinAllowedMaxFrameSize];
var received = 0;
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await context.Response.Body.WriteAsync(buffer, 0, received);
}
};
_echoWaitForAbortApplication = async context =>
{
var buffer = new byte[Http2Frame.MinAllowedMaxFrameSize];
var received = 0;
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await context.Response.Body.WriteAsync(buffer, 0, received);
}
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
};
_largeHeadersApplication = context =>
{
foreach (var name in new[] { "a", "b", "c", "d", "e", "f", "g", "h" })
{
context.Response.Headers[name] = _largeHeaderValue;
}
return Task.CompletedTask;
};
_waitForAbortApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_waitForAbortFlushingApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
await context.Response.Body.FlushAsync();
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_waitForAbortWithDataApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
await context.Response.Body.WriteAsync(new byte[10], 0, 10);
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize);
_logger = new TestApplicationErrorLogger();
InitializeConnectionFields(KestrelMemoryPool.Create());
}
private void InitializeConnectionFields(MemoryPool<byte> memoryPool)
{
_memoryPool = memoryPool;
// Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing
// Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make
// it easier to verify request frames are processed correctly immediately after sending the them.
var inputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.Inline,
writerScheduler: PipeScheduler.ThreadPool,
useSynchronizationContext: false
);
var outputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.ThreadPool,
writerScheduler: PipeScheduler.Inline,
useSynchronizationContext: false
);
_pair = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions);
var mockKestrelTrace = new Mock<TestKestrelTrace>(_logger)
{
CallBase = true
};
mockKestrelTrace
.Setup(m => m.Http2ConnectionClosing(It.IsAny<string>()))
.Callback(() => _closingStateReached.SetResult(null));
mockKestrelTrace
.Setup(m => m.Http2ConnectionClosed(It.IsAny<string>(), It.IsAny<int>()))
.Callback(() => _closedStateReached.SetResult(null));
_connectionContext = new Http2ConnectionContext
{
ConnectionFeatures = new FeatureCollection(),
ServiceContext = new TestServiceContext()
{
Log = mockKestrelTrace.Object
},
MemoryPool = _memoryPool,
Application = _pair.Application,
Transport = _pair.Transport
};
_connection = new Http2Connection(_connectionContext);
}
public void Dispose()
{
_pair.Application.Input.Complete();
_pair.Application.Output.Complete();
_pair.Transport.Input.Complete();
_pair.Transport.Output.Complete();
_memoryPool.Dispose();
}
void IHttpHeadersHandler.OnHeader(Span<byte> name, Span<byte> value)
{
_decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiStringNonNullCharacters();
}
[Fact]
public async Task Frame_Received_OverMaxSize_FrameError()
{
@ -1025,7 +755,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var closedMessage = CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.DATA, streamId: 1);
var halfClosedMessage = CoreStrings.FormatHttp2ErrorStreamHalfClosedRemote(Http2FrameType.DATA, streamId: 1);
var message = Assert.Single(_logger.Messages, m => m.Exception is Http2ConnectionErrorException);
var message = Assert.Single(TestApplicationErrorLogger.Messages, m => m.Exception is Http2ConnectionErrorException);
Assert.True(message.Exception.Message.IndexOf(closedMessage) >= 0
|| message.Exception.Message.IndexOf(halfClosedMessage) >= 0);
}
@ -1524,7 +1254,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var closedMessage = CoreStrings.FormatHttp2ErrorStreamClosed(Http2FrameType.HEADERS, streamId: 1);
var halfClosedMessage = CoreStrings.FormatHttp2ErrorStreamHalfClosedRemote(Http2FrameType.HEADERS, streamId: 1);
var message = Assert.Single(_logger.Messages, m => m.Exception is Http2ConnectionErrorException);
var message = Assert.Single(TestApplicationErrorLogger.Messages, m => m.Exception is Http2ConnectionErrorException);
Assert.True(message.Exception.Message.IndexOf(closedMessage) >= 0
|| message.Exception.Message.IndexOf(halfClosedMessage) >= 0);
}
@ -3363,7 +3093,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_pair.Application.Output.Complete(new ConnectionResetException(string.Empty));
await StopConnectionAsync(1, ignoreNonGoAwayFrames: false);
Assert.Single(_logger.Messages, m => m.Exception is ConnectionResetException);
Assert.Single(TestApplicationErrorLogger.Messages, m => m.Exception is ConnectionResetException);
}
[Fact]
@ -3375,7 +3105,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var result = await _pair.Application.Input.ReadAsync();
Assert.True(result.IsCompleted);
Assert.DoesNotContain(_logger.Messages, m => m.Exception is ConnectionResetException);
Assert.DoesNotContain(TestApplicationErrorLogger.Messages, m => m.Exception is ConnectionResetException);
}
[Fact]
@ -3557,7 +3287,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(TaskStatus.RanToCompletion, _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication)).Status);
var logMessage = _logger.Messages.Single(m => m.LogLevel >= LogLevel.Information);
var logMessage = TestApplicationErrorLogger.Messages.Single(m => m.LogLevel >= LogLevel.Information);
Assert.Equal(LogLevel.Information, logMessage.LogLevel);
Assert.Equal("Connection id \"(null)\" request processing ended abnormally.", logMessage.Message);
@ -3572,572 +3302,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(TaskStatus.RanToCompletion, _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication)).Status);
var logMessage = _logger.Messages.Single(m => m.LogLevel >= LogLevel.Information);
var logMessage = TestApplicationErrorLogger.Messages.Single(m => m.LogLevel >= LogLevel.Information);
Assert.Equal(LogLevel.Warning, logMessage.LogLevel);
Assert.Equal(CoreStrings.RequestProcessingEndError, logMessage.Message);
Assert.Same(exception, logMessage.Exception);
}
private async Task InitializeConnectionAsync(RequestDelegate application)
{
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application));
await SendPreambleAsync().ConfigureAwait(false);
await SendSettingsAsync();
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 6,
withFlags: 0,
withStreamId: 0);
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK,
withStreamId: 0);
}
private async Task StartStreamAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
var done = _hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = length;
if (done)
{
frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
}
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
while (!done)
{
frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
done = _hpackEncoder.Encode(frame.HeadersPayload, out length);
frame.Length = length;
if (done)
{
frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
}
await SendAsync(frame.Raw);
}
}
private async Task SendHeadersWithPaddingAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED, streamId);
frame.HeadersPadLength = padLength;
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = 1 + length + padLength;
frame.Payload.Slice(1 + length).Fill(0);
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
}
private async Task SendHeadersWithPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte priority, int streamDependency, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PRIORITY, streamId);
frame.HeadersPriority = priority;
frame.HeadersStreamDependency = streamDependency;
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = 5 + length;
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
}
private async Task SendHeadersWithPaddingAndPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, byte priority, int streamDependency, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED | Http2HeadersFrameFlags.PRIORITY, streamId);
frame.HeadersPadLength = padLength;
frame.HeadersPriority = priority;
frame.HeadersStreamDependency = streamDependency;
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = 6 + length + padLength;
frame.Payload.Slice(6 + length).Fill(0);
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
}
private Task WaitForAllStreamsAsync()
{
return Task.WhenAll(_runningStreams.Values.Select(tcs => tcs.Task)).DefaultTimeout();
}
private Task SendAsync(ReadOnlySpan<byte> span)
{
var writableBuffer = _pair.Application.Output;
writableBuffer.Write(span);
return FlushAsync(writableBuffer);
}
private static async Task FlushAsync(PipeWriter writableBuffer)
{
await writableBuffer.FlushAsync();
}
private Task SendPreambleAsync() => SendAsync(new ArraySegment<byte>(Http2Connection.ClientPreface));
private Task SendSettingsAsync()
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
return SendAsync(frame.Raw);
}
private Task SendSettingsAckWithInvalidLengthAsync(int length)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.ACK);
frame.Length = length;
return SendAsync(frame.Raw);
}
private Task SendSettingsWithInvalidStreamIdAsync(int streamId)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
frame.StreamId = streamId;
return SendAsync(frame.Raw);
}
private Task SendSettingsWithInvalidLengthAsync(int length)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
frame.Length = length;
return SendAsync(frame.Raw);
}
private Task SendSettingsWithInvalidParameterValueAsync(Http2SettingsParameter parameter, uint value)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
frame.Length = 6;
frame.Payload[0] = (byte)((ushort)parameter >> 8);
frame.Payload[1] = (byte)(ushort)parameter;
frame.Payload[2] = (byte)(value >> 24);
frame.Payload[3] = (byte)(value >> 16);
frame.Payload[4] = (byte)(value >> 8);
frame.Payload[5] = (byte)value;
return SendAsync(frame.Raw);
}
private Task SendPushPromiseFrameAsync()
{
var frame = new Http2Frame();
frame.Length = 0;
frame.Type = Http2FrameType.PUSH_PROMISE;
frame.StreamId = 1;
return SendAsync(frame.Raw);
}
private async Task<bool> SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, IEnumerable<KeyValuePair<string, string>> headers)
{
var frame = new Http2Frame();
frame.PrepareHeaders(flags, streamId);
var done = _hpackEncoder.BeginEncode(headers, frame.Payload, out var length);
frame.Length = length;
await SendAsync(frame.Raw);
return done;
}
private Task SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, byte[] headerBlock)
{
var frame = new Http2Frame();
frame.PrepareHeaders(flags, streamId);
frame.Length = headerBlock.Length;
headerBlock.CopyTo(frame.HeadersPayload);
return SendAsync(frame.Raw);
}
private Task SendInvalidHeadersFrameAsync(int streamId, int frameLength, byte padLength)
{
Assert.True(padLength >= frameLength, $"{nameof(padLength)} must be greater than or equal to {nameof(frameLength)} to create an invalid frame.");
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.PADDED, streamId);
frame.Payload[0] = padLength;
// Set length last so .Payload can be written to
frame.Length = frameLength;
return SendAsync(frame.Raw);
}
private Task SendIncompleteHeadersFrameAsync(int streamId)
{
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS, streamId);
frame.Length = 3;
// Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
// with an incomplete new name
frame.Payload[0] = 0;
frame.Payload[1] = 2;
frame.Payload[2] = (byte)'a';
return SendAsync(frame.Raw);
}
private async Task<bool> SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags)
{
var frame = new Http2Frame();
frame.PrepareContinuation(flags, streamId);
var done = _hpackEncoder.Encode(frame.Payload, out var length);
frame.Length = length;
await SendAsync(frame.Raw);
return done;
}
private async Task SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags, byte[] payload)
{
var frame = new Http2Frame();
frame.PrepareContinuation(flags, streamId);
frame.Length = payload.Length;
payload.CopyTo(frame.Payload);
await SendAsync(frame.Raw);
}
private Task SendEmptyContinuationFrameAsync(int streamId, Http2ContinuationFrameFlags flags)
{
var frame = new Http2Frame();
frame.PrepareContinuation(flags, streamId);
frame.Length = 0;
return SendAsync(frame.Raw);
}
private Task SendIncompleteContinuationFrameAsync(int streamId)
{
var frame = new Http2Frame();
frame.PrepareContinuation(Http2ContinuationFrameFlags.END_HEADERS, streamId);
frame.Length = 3;
// Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
// with an incomplete new name
frame.Payload[0] = 0;
frame.Payload[1] = 2;
frame.Payload[2] = (byte)'a';
return SendAsync(frame.Raw);
}
private Task SendDataAsync(int streamId, Span<byte> data, bool endStream)
{
var frame = new Http2Frame();
frame.PrepareData(streamId);
frame.Length = data.Length;
frame.DataFlags = endStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE;
data.CopyTo(frame.DataPayload);
return SendAsync(frame.Raw);
}
private Task SendDataWithPaddingAsync(int streamId, Span<byte> data, byte padLength, bool endStream)
{
var frame = new Http2Frame();
frame.PrepareData(streamId, padLength);
frame.Length = data.Length + 1 + padLength;
data.CopyTo(frame.DataPayload);
if (endStream)
{
frame.DataFlags |= Http2DataFrameFlags.END_STREAM;
}
return SendAsync(frame.Raw);
}
private Task SendInvalidDataFrameAsync(int streamId, int frameLength, byte padLength)
{
Assert.True(padLength >= frameLength, $"{nameof(padLength)} must be greater than or equal to {nameof(frameLength)} to create an invalid frame.");
var frame = new Http2Frame();
frame.PrepareData(streamId);
frame.DataFlags = Http2DataFrameFlags.PADDED;
frame.Payload[0] = padLength;
// Set length last so .Payload can be written to
frame.Length = frameLength;
return SendAsync(frame.Raw);
}
private Task SendPingAsync(Http2PingFrameFlags flags)
{
var pingFrame = new Http2Frame();
pingFrame.PreparePing(flags);
return SendAsync(pingFrame.Raw);
}
private Task SendPingWithInvalidLengthAsync(int length)
{
var pingFrame = new Http2Frame();
pingFrame.PreparePing(Http2PingFrameFlags.NONE);
pingFrame.Length = length;
return SendAsync(pingFrame.Raw);
}
private Task SendPingWithInvalidStreamIdAsync(int streamId)
{
Assert.NotEqual(0, streamId);
var pingFrame = new Http2Frame();
pingFrame.PreparePing(Http2PingFrameFlags.NONE);
pingFrame.StreamId = streamId;
return SendAsync(pingFrame.Raw);
}
private Task SendPriorityAsync(int streamId, int streamDependency = 0)
{
var priorityFrame = new Http2Frame();
priorityFrame.PreparePriority(streamId, streamDependency: streamDependency, exclusive: false, weight: 0);
return SendAsync(priorityFrame.Raw);
}
private Task SendInvalidPriorityFrameAsync(int streamId, int length)
{
var priorityFrame = new Http2Frame();
priorityFrame.PreparePriority(streamId, streamDependency: 0, exclusive: false, weight: 0);
priorityFrame.Length = length;
return SendAsync(priorityFrame.Raw);
}
private Task SendRstStreamAsync(int streamId)
{
var rstStreamFrame = new Http2Frame();
rstStreamFrame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
return SendAsync(rstStreamFrame.Raw);
}
private Task SendInvalidRstStreamFrameAsync(int streamId, int length)
{
var frame = new Http2Frame();
frame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
frame.Length = length;
return SendAsync(frame.Raw);
}
private Task SendGoAwayAsync()
{
var frame = new Http2Frame();
frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
return SendAsync(frame.Raw);
}
private Task SendInvalidGoAwayFrameAsync()
{
var frame = new Http2Frame();
frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
frame.StreamId = 1;
return SendAsync(frame.Raw);
}
private Task SendWindowUpdateAsync(int streamId, int sizeIncrement)
{
var frame = new Http2Frame();
frame.PrepareWindowUpdate(streamId, sizeIncrement);
return SendAsync(frame.Raw);
}
private Task SendInvalidWindowUpdateAsync(int streamId, int sizeIncrement, int length)
{
var frame = new Http2Frame();
frame.PrepareWindowUpdate(streamId, sizeIncrement);
frame.Length = length;
return SendAsync(frame.Raw);
}
private Task SendUnknownFrameTypeAsync(int streamId, int frameType)
{
var frame = new Http2Frame();
frame.StreamId = streamId;
frame.Type = (Http2FrameType)frameType;
frame.Length = 0;
return SendAsync(frame.Raw);
}
private async Task<Http2Frame> ReceiveFrameAsync()
{
var frame = new Http2Frame();
while (true)
{
var result = await _pair.Application.Input.ReadAsync();
var buffer = result.Buffer;
var consumed = buffer.Start;
var examined = buffer.End;
try
{
Assert.True(buffer.Length > 0);
if (Http2FrameReader.ReadFrame(buffer, frame, 16_384, out consumed, out examined))
{
return frame;
}
if (result.IsCompleted)
{
throw new IOException("The reader completed without returning a frame.");
}
}
finally
{
_pair.Application.Input.AdvanceTo(consumed, examined);
}
}
}
private async Task<Http2Frame> ExpectAsync(Http2FrameType type, int withLength, byte withFlags, int withStreamId)
{
var frame = await ReceiveFrameAsync();
Assert.Equal(type, frame.Type);
Assert.Equal(withLength, frame.Length);
Assert.Equal(withFlags, frame.Flags);
Assert.Equal(withStreamId, frame.StreamId);
return frame;
}
private Task StopConnectionAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
{
_pair.Application.Output.Complete();
return WaitForConnectionStopAsync(expectedLastStreamId, ignoreNonGoAwayFrames);
}
private Task WaitForConnectionStopAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
{
return WaitForConnectionErrorAsync<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR, expectedErrorMessage: null);
}
private void VerifyGoAway(Http2Frame frame, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
{
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
Assert.Equal(8, frame.Length);
Assert.Equal(0, frame.Flags);
Assert.Equal(0, frame.StreamId);
Assert.Equal(expectedLastStreamId, frame.GoAwayLastStreamId);
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
}
private async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
where TException : Exception
{
var frame = await ReceiveFrameAsync();
if (ignoreNonGoAwayFrames)
{
while (frame.Type != Http2FrameType.GOAWAY)
{
frame = await ReceiveFrameAsync();
}
}
VerifyGoAway(frame, expectedLastStreamId, expectedErrorCode);
if (expectedErrorMessage != null)
{
var message = Assert.Single(_logger.Messages, m => m.Exception is TException);
Assert.Contains(expectedErrorMessage, message.Exception.Message);
}
await _connectionTask;
_pair.Application.Output.Complete();
}
private async Task WaitForStreamErrorAsync(int expectedStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
{
var frame = await ReceiveFrameAsync();
Assert.Equal(Http2FrameType.RST_STREAM, frame.Type);
Assert.Equal(4, frame.Length);
Assert.Equal(0, frame.Flags);
Assert.Equal(expectedStreamId, frame.StreamId);
Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
if (expectedErrorMessage != null)
{
var message = Assert.Single(_logger.Messages, m => m.Exception is Http2StreamErrorException);
Assert.Contains(expectedErrorMessage, message.Exception.Message);
}
}
private void VerifyDecodedRequestHeaders(IEnumerable<KeyValuePair<string, string>> expectedHeaders)
{
foreach (var header in expectedHeaders)
{
Assert.True(_receivedHeaders.TryGetValue(header.Key, out var value), header.Key);
Assert.Equal(header.Value, value, ignoreCase: true);
}
}
public static TheoryData<byte[]> UpperCaseHeaderNameData
{
get

View File

@ -2,11 +2,8 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
@ -15,11 +12,7 @@ using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
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.Transport.Abstractions.Internal;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@ -27,195 +20,8 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class Http2StreamTests : IDisposable, IHttpHeadersHandler
public class Http2StreamTests : Http2TestBase
{
private static readonly string _largeHeaderValue = new string('a', HPackDecoder.MaxStringOctets);
private static readonly IEnumerable<KeyValuePair<string, string>> _browserRequestHeaders = new[]
{
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
new KeyValuePair<string, string>("user-agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"),
new KeyValuePair<string, string>("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
new KeyValuePair<string, string>("accept-language", "en-US,en;q=0.5"),
new KeyValuePair<string, string>("accept-encoding", "gzip, deflate, br"),
new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
};
private MemoryPool<byte> _memoryPool = KestrelMemoryPool.Create();
private DuplexPipe.DuplexPipePair _pair;
private readonly TestApplicationErrorLogger _logger;
private Http2ConnectionContext _connectionContext;
private Http2Connection _connection;
private readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
private readonly HPackDecoder _hpackDecoder;
private readonly ConcurrentDictionary<int, TaskCompletionSource<object>> _runningStreams = new ConcurrentDictionary<int, TaskCompletionSource<object>>();
private readonly Dictionary<string, string> _decodedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<int> _abortedStreamIds = new HashSet<int>();
private readonly object _abortedStreamIdsLock = new object();
private readonly RequestDelegate _noopApplication;
private readonly RequestDelegate _echoMethod;
private readonly RequestDelegate _echoHost;
private readonly RequestDelegate _echoPath;
private readonly RequestDelegate _waitForAbortApplication;
private readonly RequestDelegate _waitForAbortFlushingApplication;
private readonly RequestDelegate _waitForAbortWithDataApplication;
private Task _connectionTask;
public Http2StreamTests()
{
_noopApplication = context => Task.CompletedTask;
_echoMethod = context =>
{
context.Response.Headers["Method"] = context.Request.Method;
return Task.CompletedTask;
};
_echoHost = context =>
{
context.Response.Headers[HeaderNames.Host] = context.Request.Headers[HeaderNames.Host];
return Task.CompletedTask;
};
_echoPath = context =>
{
context.Response.Headers["path"] = context.Request.Path.ToString();
context.Response.Headers["rawtarget"] = context.Features.Get<IHttpRequestFeature>().RawTarget;
return Task.CompletedTask;
};
_waitForAbortApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_waitForAbortFlushingApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
await context.Response.Body.FlushAsync();
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_waitForAbortWithDataApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
await context.Response.Body.WriteAsync(new byte[10], 0, 10);
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize);
_logger = new TestApplicationErrorLogger();
InitializeConnectionFields(KestrelMemoryPool.Create());
}
private void InitializeConnectionFields(MemoryPool<byte> memoryPool)
{
_memoryPool = memoryPool;
// Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing
// Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make
// it easier to verify request frames are processed correctly immediately after sending the them.
var inputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.Inline,
writerScheduler: PipeScheduler.ThreadPool,
useSynchronizationContext: false
);
var outputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.ThreadPool,
writerScheduler: PipeScheduler.Inline,
useSynchronizationContext: false
);
_pair = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions);
_connectionContext = new Http2ConnectionContext
{
ConnectionFeatures = new FeatureCollection(),
ServiceContext = new TestServiceContext()
{
Log = new TestKestrelTrace(_logger),
},
MemoryPool = _memoryPool,
Application = _pair.Application,
Transport = _pair.Transport
};
_connection = new Http2Connection(_connectionContext);
}
public void Dispose()
{
_pair.Application.Input.Complete();
_pair.Application.Output.Complete();
_pair.Transport.Input.Complete();
_pair.Transport.Output.Complete();
_memoryPool.Dispose();
}
void IHttpHeadersHandler.OnHeader(Span<byte> name, Span<byte> value)
{
_decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiStringNonNullCharacters();
}
[Fact]
public async Task HEADERS_Received_EmptyMethod_Reset()
{
@ -1105,7 +911,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);
Assert.Contains(_logger.Messages, m => m.Exception?.Message.Contains("Response Content-Length mismatch: too many bytes written (12 of 11).") ?? false);
Assert.Contains(TestApplicationErrorLogger.Messages, m => m.Exception?.Message.Contains("Response Content-Length mismatch: too many bytes written (12 of 11).") ?? false);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
@ -1182,7 +988,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);
Assert.Contains(_logger.Messages, m => m.Exception?.Message.Contains("Response Content-Length mismatch: too few bytes written (0 of 11).") ?? false);
Assert.Contains(TestApplicationErrorLogger.Messages, m => m.Exception?.Message.Contains("Response Content-Length mismatch: too few bytes written (0 of 11).") ?? false);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
@ -1538,7 +1344,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);
Assert.Contains(_logger.Messages, m => (m.Exception?.Message.Contains("App Faulted") ?? false) && m.LogLevel == LogLevel.Error);
Assert.Contains(TestApplicationErrorLogger.Messages, m => (m.Exception?.Message.Contains("App Faulted") ?? false) && m.LogLevel == LogLevel.Error);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
@ -1859,220 +1665,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.IsType<ConnectionAbortedException>(thrownEx.InnerException);
Assert.Equal(CoreStrings.ConnectionAbortedByApplication, thrownEx.InnerException.Message);
}
private async Task InitializeConnectionAsync(RequestDelegate application)
{
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application));
await SendPreambleAsync().ConfigureAwait(false);
await SendSettingsAsync();
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 6,
withFlags: 0,
withStreamId: 0);
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK,
withStreamId: 0);
}
private async Task StartStreamAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
var done = _hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = length;
if (done)
{
frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
}
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
while (!done)
{
frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
done = _hpackEncoder.Encode(frame.HeadersPayload, out length);
frame.Length = length;
if (done)
{
frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
}
await SendAsync(frame.Raw);
}
}
private Task WaitForAllStreamsAsync()
{
return Task.WhenAll(_runningStreams.Values.Select(tcs => tcs.Task)).DefaultTimeout();
}
private Task SendAsync(ReadOnlySpan<byte> span)
{
var writableBuffer = _pair.Application.Output;
writableBuffer.Write(span);
return FlushAsync(writableBuffer);
}
private static async Task FlushAsync(PipeWriter writableBuffer)
{
await writableBuffer.FlushAsync();
}
private Task SendPreambleAsync() => SendAsync(new ArraySegment<byte>(Http2Connection.ClientPreface));
private Task SendSettingsAsync()
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
return SendAsync(frame.Raw);
}
private async Task<bool> SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, IEnumerable<KeyValuePair<string, string>> headers)
{
var frame = new Http2Frame();
frame.PrepareHeaders(flags, streamId);
var done = _hpackEncoder.BeginEncode(headers, frame.Payload, out var length);
frame.Length = length;
await SendAsync(frame.Raw);
return done;
}
private Task SendDataAsync(int streamId, Span<byte> data, bool endStream)
{
var frame = new Http2Frame();
frame.PrepareData(streamId);
frame.Length = data.Length;
frame.DataFlags = endStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE;
data.CopyTo(frame.DataPayload);
return SendAsync(frame.Raw);
}
private Task SendRstStreamAsync(int streamId)
{
var rstStreamFrame = new Http2Frame();
rstStreamFrame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
return SendAsync(rstStreamFrame.Raw);
}
private async Task<Http2Frame> ReceiveFrameAsync()
{
var frame = new Http2Frame();
while (true)
{
var result = await _pair.Application.Input.ReadAsync();
var buffer = result.Buffer;
var consumed = buffer.Start;
var examined = buffer.End;
try
{
Assert.True(buffer.Length > 0);
if (Http2FrameReader.ReadFrame(buffer, frame, 16_384, out consumed, out examined))
{
return frame;
}
if (result.IsCompleted)
{
throw new IOException("The reader completed without returning a frame.");
}
}
finally
{
_pair.Application.Input.AdvanceTo(consumed, examined);
}
}
}
private async Task<Http2Frame> ExpectAsync(Http2FrameType type, int withLength, byte withFlags, int withStreamId)
{
var frame = await ReceiveFrameAsync().DefaultTimeout();
Assert.Equal(type, frame.Type);
Assert.Equal(withLength, frame.Length);
Assert.Equal(withFlags, frame.Flags);
Assert.Equal(withStreamId, frame.StreamId);
return frame;
}
private Task StopConnectionAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
{
_pair.Application.Output.Complete();
return WaitForConnectionStopAsync(expectedLastStreamId, ignoreNonGoAwayFrames);
}
private Task WaitForConnectionStopAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
{
return WaitForConnectionErrorAsync<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR, expectedErrorMessage: null);
}
private async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
where TException : Exception
{
var frame = await ReceiveFrameAsync();
if (ignoreNonGoAwayFrames)
{
while (frame.Type != Http2FrameType.GOAWAY)
{
frame = await ReceiveFrameAsync();
}
}
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
Assert.Equal(8, frame.Length);
Assert.Equal(0, frame.Flags);
Assert.Equal(0, frame.StreamId);
Assert.Equal(expectedLastStreamId, frame.GoAwayLastStreamId);
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
if (expectedErrorMessage != null)
{
var message = Assert.Single(_logger.Messages, m => m.Exception is TException);
Assert.Contains(expectedErrorMessage, message.Exception.Message);
}
await _connectionTask;
_pair.Application.Output.Complete();
}
private async Task WaitForStreamErrorAsync(int expectedStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
{
var frame = await ReceiveFrameAsync();
Assert.Equal(Http2FrameType.RST_STREAM, frame.Type);
Assert.Equal(4, frame.Length);
Assert.Equal(0, frame.Flags);
Assert.Equal(expectedStreamId, frame.StreamId);
Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
if (expectedErrorMessage != null)
{
Assert.Contains(_logger.Messages, m => m.Exception?.Message.Contains(expectedErrorMessage) ?? false);
}
}
}
}

View File

@ -0,0 +1,867 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
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.Transport.Abstractions.Internal;
using Microsoft.AspNetCore.Testing;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class Http2TestBase : TestApplicationErrorLoggerLoggedTest, IDisposable, IHttpHeadersHandler
{
protected static readonly string _largeHeaderValue = new string('a', HPackDecoder.MaxStringOctets);
protected static readonly IEnumerable<KeyValuePair<string, string>> _browserRequestHeaders = new[]
{
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
new KeyValuePair<string, string>("user-agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"),
new KeyValuePair<string, string>("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
new KeyValuePair<string, string>("accept-language", "en-US,en;q=0.5"),
new KeyValuePair<string, string>("accept-encoding", "gzip, deflate, br"),
new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
};
private readonly MemoryPool<byte> _memoryPool = KestrelMemoryPool.Create();
internal readonly DuplexPipe.DuplexPipePair _pair;
protected readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
protected readonly HPackEncoder _hpackEncoder = new HPackEncoder();
protected readonly HPackDecoder _hpackDecoder;
protected readonly ConcurrentDictionary<int, TaskCompletionSource<object>> _runningStreams = new ConcurrentDictionary<int, TaskCompletionSource<object>>();
protected readonly Dictionary<string, string> _receivedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
protected readonly Dictionary<string, string> _decodedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
protected readonly HashSet<int> _abortedStreamIds = new HashSet<int>();
protected readonly object _abortedStreamIdsLock = new object();
protected readonly TaskCompletionSource<object> _closingStateReached = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
protected readonly TaskCompletionSource<object> _closedStateReached = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
protected readonly RequestDelegate _noopApplication;
protected readonly RequestDelegate _readHeadersApplication;
protected readonly RequestDelegate _readTrailersApplication;
protected readonly RequestDelegate _bufferingApplication;
protected readonly RequestDelegate _echoApplication;
protected readonly RequestDelegate _echoWaitForAbortApplication;
protected readonly RequestDelegate _largeHeadersApplication;
protected readonly RequestDelegate _waitForAbortApplication;
protected readonly RequestDelegate _waitForAbortFlushingApplication;
protected readonly RequestDelegate _waitForAbortWithDataApplication;
protected readonly RequestDelegate _echoMethod;
protected readonly RequestDelegate _echoHost;
protected readonly RequestDelegate _echoPath;
protected Http2ConnectionContext _connectionContext;
protected Http2Connection _connection;
protected Task _connectionTask;
public Http2TestBase()
{
// Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing
// Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make
// it easier to verify request frames are processed correctly immediately after sending the them.
var inputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.Inline,
writerScheduler: PipeScheduler.ThreadPool,
useSynchronizationContext: false
);
var outputPipeOptions = new PipeOptions(
pool: _memoryPool,
readerScheduler: PipeScheduler.ThreadPool,
writerScheduler: PipeScheduler.Inline,
useSynchronizationContext: false
);
_pair = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions);
_hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize);
_noopApplication = context => Task.CompletedTask;
_readHeadersApplication = context =>
{
foreach (var header in context.Request.Headers)
{
_receivedHeaders[header.Key] = header.Value.ToString();
}
return Task.CompletedTask;
};
_readTrailersApplication = async context =>
{
using (var ms = new MemoryStream())
{
// Consuming the entire request body guarantees trailers will be available
await context.Request.Body.CopyToAsync(ms);
}
foreach (var header in context.Request.Headers)
{
_receivedHeaders[header.Key] = header.Value.ToString();
}
};
_bufferingApplication = async context =>
{
var data = new List<byte>();
var buffer = new byte[1024];
var received = 0;
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
data.AddRange(new ArraySegment<byte>(buffer, 0, received));
}
await context.Response.Body.WriteAsync(data.ToArray(), 0, data.Count);
};
_echoApplication = async context =>
{
var buffer = new byte[Http2Frame.MinAllowedMaxFrameSize];
var received = 0;
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await context.Response.Body.WriteAsync(buffer, 0, received);
}
};
_echoWaitForAbortApplication = async context =>
{
var buffer = new byte[Http2Frame.MinAllowedMaxFrameSize];
var received = 0;
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await context.Response.Body.WriteAsync(buffer, 0, received);
}
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
};
_largeHeadersApplication = context =>
{
foreach (var name in new[] { "a", "b", "c", "d", "e", "f", "g", "h" })
{
context.Response.Headers[name] = _largeHeaderValue;
}
return Task.CompletedTask;
};
_waitForAbortApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_waitForAbortFlushingApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
await context.Response.Body.FlushAsync();
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_waitForAbortWithDataApplication = async context =>
{
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
var sem = new SemaphoreSlim(0);
context.RequestAborted.Register(() =>
{
lock (_abortedStreamIdsLock)
{
_abortedStreamIds.Add(streamIdFeature.StreamId);
}
sem.Release();
});
await sem.WaitAsync().DefaultTimeout();
await context.Response.Body.WriteAsync(new byte[10], 0, 10);
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
};
_echoMethod = context =>
{
context.Response.Headers["Method"] = context.Request.Method;
return Task.CompletedTask;
};
_echoHost = context =>
{
context.Response.Headers[HeaderNames.Host] = context.Request.Headers[HeaderNames.Host];
return Task.CompletedTask;
};
_echoPath = context =>
{
context.Response.Headers["path"] = context.Request.Path.ToString();
context.Response.Headers["rawtarget"] = context.Features.Get<IHttpRequestFeature>().RawTarget;
return Task.CompletedTask;
};
}
public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
{
base.Initialize(methodInfo, testMethodArguments, testOutputHelper);
var mockKestrelTrace = new Mock<IKestrelTrace>();
mockKestrelTrace
.Setup(m => m.Http2ConnectionClosing(It.IsAny<string>()))
.Callback(() => _closingStateReached.SetResult(null));
mockKestrelTrace
.Setup(m => m.Http2ConnectionClosed(It.IsAny<string>(), It.IsAny<int>()))
.Callback(() => _closedStateReached.SetResult(null));
_connectionContext = new Http2ConnectionContext
{
ConnectionFeatures = new FeatureCollection(),
ServiceContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object),
MemoryPool = _memoryPool,
Application = _pair.Application,
Transport = _pair.Transport
};
_connection = new Http2Connection(_connectionContext);
}
public override void Dispose()
{
_pair.Application.Input.Complete();
_pair.Application.Output.Complete();
_pair.Transport.Input.Complete();
_pair.Transport.Output.Complete();
_memoryPool.Dispose();
base.Dispose();
}
void IHttpHeadersHandler.OnHeader(Span<byte> name, Span<byte> value)
{
_decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiStringNonNullCharacters();
}
protected async Task InitializeConnectionAsync(RequestDelegate application)
{
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application));
await SendPreambleAsync().ConfigureAwait(false);
await SendSettingsAsync();
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 6,
withFlags: 0,
withStreamId: 0);
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK,
withStreamId: 0);
}
protected async Task StartStreamAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
var done = _hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = length;
if (done)
{
frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
}
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
while (!done)
{
frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
done = _hpackEncoder.Encode(frame.HeadersPayload, out length);
frame.Length = length;
if (done)
{
frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
}
await SendAsync(frame.Raw);
}
}
protected async Task SendHeadersWithPaddingAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED, streamId);
frame.HeadersPadLength = padLength;
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = 1 + length + padLength;
frame.Payload.Slice(1 + length).Fill(0);
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
}
protected async Task SendHeadersWithPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte priority, int streamDependency, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PRIORITY, streamId);
frame.HeadersPriority = priority;
frame.HeadersStreamDependency = streamDependency;
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = 5 + length;
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
}
protected async Task SendHeadersWithPaddingAndPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, byte priority, int streamDependency, bool endStream)
{
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs;
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED | Http2HeadersFrameFlags.PRIORITY, streamId);
frame.HeadersPadLength = padLength;
frame.HeadersPriority = priority;
frame.HeadersStreamDependency = streamDependency;
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
frame.Length = 6 + length + padLength;
frame.Payload.Slice(6 + length).Fill(0);
if (endStream)
{
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
}
await SendAsync(frame.Raw);
}
protected Task WaitForAllStreamsAsync()
{
return Task.WhenAll(_runningStreams.Values.Select(tcs => tcs.Task)).DefaultTimeout();
}
protected Task SendAsync(ReadOnlySpan<byte> span)
{
var writableBuffer = _pair.Application.Output;
writableBuffer.Write(span);
return FlushAsync(writableBuffer);
}
protected static async Task FlushAsync(PipeWriter writableBuffer)
{
await writableBuffer.FlushAsync();
}
protected Task SendPreambleAsync() => SendAsync(new ArraySegment<byte>(Http2Connection.ClientPreface));
protected Task SendSettingsAsync()
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
return SendAsync(frame.Raw);
}
protected Task SendSettingsAckWithInvalidLengthAsync(int length)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.ACK);
frame.Length = length;
return SendAsync(frame.Raw);
}
protected Task SendSettingsWithInvalidStreamIdAsync(int streamId)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
frame.StreamId = streamId;
return SendAsync(frame.Raw);
}
protected Task SendSettingsWithInvalidLengthAsync(int length)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
frame.Length = length;
return SendAsync(frame.Raw);
}
protected Task SendSettingsWithInvalidParameterValueAsync(Http2SettingsParameter parameter, uint value)
{
var frame = new Http2Frame();
frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
frame.Length = 6;
frame.Payload[0] = (byte)((ushort)parameter >> 8);
frame.Payload[1] = (byte)(ushort)parameter;
frame.Payload[2] = (byte)(value >> 24);
frame.Payload[3] = (byte)(value >> 16);
frame.Payload[4] = (byte)(value >> 8);
frame.Payload[5] = (byte)value;
return SendAsync(frame.Raw);
}
protected Task SendPushPromiseFrameAsync()
{
var frame = new Http2Frame();
frame.Length = 0;
frame.Type = Http2FrameType.PUSH_PROMISE;
frame.StreamId = 1;
return SendAsync(frame.Raw);
}
protected async Task<bool> SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, IEnumerable<KeyValuePair<string, string>> headers)
{
var frame = new Http2Frame();
frame.PrepareHeaders(flags, streamId);
var done = _hpackEncoder.BeginEncode(headers, frame.Payload, out var length);
frame.Length = length;
await SendAsync(frame.Raw);
return done;
}
protected Task SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, byte[] headerBlock)
{
var frame = new Http2Frame();
frame.PrepareHeaders(flags, streamId);
frame.Length = headerBlock.Length;
headerBlock.CopyTo(frame.HeadersPayload);
return SendAsync(frame.Raw);
}
protected Task SendInvalidHeadersFrameAsync(int streamId, int frameLength, byte padLength)
{
Assert.True(padLength >= frameLength, $"{nameof(padLength)} must be greater than or equal to {nameof(frameLength)} to create an invalid frame.");
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.PADDED, streamId);
frame.Payload[0] = padLength;
// Set length last so .Payload can be written to
frame.Length = frameLength;
return SendAsync(frame.Raw);
}
protected Task SendIncompleteHeadersFrameAsync(int streamId)
{
var frame = new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS, streamId);
frame.Length = 3;
// Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
// with an incomplete new name
frame.Payload[0] = 0;
frame.Payload[1] = 2;
frame.Payload[2] = (byte)'a';
return SendAsync(frame.Raw);
}
protected async Task<bool> SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags)
{
var frame = new Http2Frame();
frame.PrepareContinuation(flags, streamId);
var done = _hpackEncoder.Encode(frame.Payload, out var length);
frame.Length = length;
await SendAsync(frame.Raw);
return done;
}
protected async Task SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags, byte[] payload)
{
var frame = new Http2Frame();
frame.PrepareContinuation(flags, streamId);
frame.Length = payload.Length;
payload.CopyTo(frame.Payload);
await SendAsync(frame.Raw);
}
protected Task SendEmptyContinuationFrameAsync(int streamId, Http2ContinuationFrameFlags flags)
{
var frame = new Http2Frame();
frame.PrepareContinuation(flags, streamId);
frame.Length = 0;
return SendAsync(frame.Raw);
}
protected Task SendIncompleteContinuationFrameAsync(int streamId)
{
var frame = new Http2Frame();
frame.PrepareContinuation(Http2ContinuationFrameFlags.END_HEADERS, streamId);
frame.Length = 3;
// Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
// with an incomplete new name
frame.Payload[0] = 0;
frame.Payload[1] = 2;
frame.Payload[2] = (byte)'a';
return SendAsync(frame.Raw);
}
protected Task SendDataAsync(int streamId, Span<byte> data, bool endStream)
{
var frame = new Http2Frame();
frame.PrepareData(streamId);
frame.Length = data.Length;
frame.DataFlags = endStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE;
data.CopyTo(frame.DataPayload);
return SendAsync(frame.Raw);
}
protected Task SendDataWithPaddingAsync(int streamId, Span<byte> data, byte padLength, bool endStream)
{
var frame = new Http2Frame();
frame.PrepareData(streamId, padLength);
frame.Length = data.Length + 1 + padLength;
data.CopyTo(frame.DataPayload);
if (endStream)
{
frame.DataFlags |= Http2DataFrameFlags.END_STREAM;
}
return SendAsync(frame.Raw);
}
protected Task SendInvalidDataFrameAsync(int streamId, int frameLength, byte padLength)
{
Assert.True(padLength >= frameLength, $"{nameof(padLength)} must be greater than or equal to {nameof(frameLength)} to create an invalid frame.");
var frame = new Http2Frame();
frame.PrepareData(streamId);
frame.DataFlags = Http2DataFrameFlags.PADDED;
frame.Payload[0] = padLength;
// Set length last so .Payload can be written to
frame.Length = frameLength;
return SendAsync(frame.Raw);
}
protected Task SendPingAsync(Http2PingFrameFlags flags)
{
var pingFrame = new Http2Frame();
pingFrame.PreparePing(flags);
return SendAsync(pingFrame.Raw);
}
protected Task SendPingWithInvalidLengthAsync(int length)
{
var pingFrame = new Http2Frame();
pingFrame.PreparePing(Http2PingFrameFlags.NONE);
pingFrame.Length = length;
return SendAsync(pingFrame.Raw);
}
protected Task SendPingWithInvalidStreamIdAsync(int streamId)
{
Assert.NotEqual(0, streamId);
var pingFrame = new Http2Frame();
pingFrame.PreparePing(Http2PingFrameFlags.NONE);
pingFrame.StreamId = streamId;
return SendAsync(pingFrame.Raw);
}
protected Task SendPriorityAsync(int streamId, int streamDependency = 0)
{
var priorityFrame = new Http2Frame();
priorityFrame.PreparePriority(streamId, streamDependency: streamDependency, exclusive: false, weight: 0);
return SendAsync(priorityFrame.Raw);
}
protected Task SendInvalidPriorityFrameAsync(int streamId, int length)
{
var priorityFrame = new Http2Frame();
priorityFrame.PreparePriority(streamId, streamDependency: 0, exclusive: false, weight: 0);
priorityFrame.Length = length;
return SendAsync(priorityFrame.Raw);
}
protected Task SendRstStreamAsync(int streamId)
{
var rstStreamFrame = new Http2Frame();
rstStreamFrame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
return SendAsync(rstStreamFrame.Raw);
}
protected Task SendInvalidRstStreamFrameAsync(int streamId, int length)
{
var frame = new Http2Frame();
frame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
frame.Length = length;
return SendAsync(frame.Raw);
}
protected Task SendGoAwayAsync()
{
var frame = new Http2Frame();
frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
return SendAsync(frame.Raw);
}
protected Task SendInvalidGoAwayFrameAsync()
{
var frame = new Http2Frame();
frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
frame.StreamId = 1;
return SendAsync(frame.Raw);
}
protected Task SendWindowUpdateAsync(int streamId, int sizeIncrement)
{
var frame = new Http2Frame();
frame.PrepareWindowUpdate(streamId, sizeIncrement);
return SendAsync(frame.Raw);
}
protected Task SendInvalidWindowUpdateAsync(int streamId, int sizeIncrement, int length)
{
var frame = new Http2Frame();
frame.PrepareWindowUpdate(streamId, sizeIncrement);
frame.Length = length;
return SendAsync(frame.Raw);
}
protected Task SendUnknownFrameTypeAsync(int streamId, int frameType)
{
var frame = new Http2Frame();
frame.StreamId = streamId;
frame.Type = (Http2FrameType)frameType;
frame.Length = 0;
return SendAsync(frame.Raw);
}
protected async Task<Http2Frame> ReceiveFrameAsync()
{
var frame = new Http2Frame();
while (true)
{
var result = await _pair.Application.Input.ReadAsync().AsTask().DefaultTimeout();
var buffer = result.Buffer;
var consumed = buffer.Start;
var examined = buffer.End;
try
{
Assert.True(buffer.Length > 0);
if (Http2FrameReader.ReadFrame(buffer, frame, 16_384, out consumed, out examined))
{
return frame;
}
if (result.IsCompleted)
{
throw new IOException("The reader completed without returning a frame.");
}
}
finally
{
_pair.Application.Input.AdvanceTo(consumed, examined);
}
}
}
protected async Task<Http2Frame> ExpectAsync(Http2FrameType type, int withLength, byte withFlags, int withStreamId)
{
var frame = await ReceiveFrameAsync();
Assert.Equal(type, frame.Type);
Assert.Equal(withLength, frame.Length);
Assert.Equal(withFlags, frame.Flags);
Assert.Equal(withStreamId, frame.StreamId);
return frame;
}
protected Task StopConnectionAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
{
_pair.Application.Output.Complete();
return WaitForConnectionStopAsync(expectedLastStreamId, ignoreNonGoAwayFrames);
}
protected Task WaitForConnectionStopAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
{
return WaitForConnectionErrorAsync<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR, expectedErrorMessage: null);
}
protected void VerifyGoAway(Http2Frame frame, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
{
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
Assert.Equal(8, frame.Length);
Assert.Equal(0, frame.Flags);
Assert.Equal(0, frame.StreamId);
Assert.Equal(expectedLastStreamId, frame.GoAwayLastStreamId);
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
}
protected async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
where TException : Exception
{
var frame = await ReceiveFrameAsync();
if (ignoreNonGoAwayFrames)
{
while (frame.Type != Http2FrameType.GOAWAY)
{
frame = await ReceiveFrameAsync();
}
}
VerifyGoAway(frame, expectedLastStreamId, expectedErrorCode);
if (expectedErrorMessage != null)
{
var message = Assert.Single(TestApplicationErrorLogger.Messages, m => m.Exception is TException);
Assert.Contains(expectedErrorMessage, message.Exception.Message);
}
await _connectionTask;
_pair.Application.Output.Complete();
}
protected async Task WaitForStreamErrorAsync(int expectedStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
{
var frame = await ReceiveFrameAsync();
Assert.Equal(Http2FrameType.RST_STREAM, frame.Type);
Assert.Equal(4, frame.Length);
Assert.Equal(0, frame.Flags);
Assert.Equal(expectedStreamId, frame.StreamId);
Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
if (expectedErrorMessage != null)
{
Assert.Contains(TestApplicationErrorLogger.Messages, m => m.Exception?.Message.Contains(expectedErrorMessage) ?? false);
}
}
protected void VerifyDecodedRequestHeaders(IEnumerable<KeyValuePair<string, string>> expectedHeaders)
{
foreach (var header in expectedHeaders)
{
Assert.True(_receivedHeaders.TryGetValue(header.Key, out var value), header.Key);
Assert.Equal(header.Value, value, ignoreCase: true);
}
}
}
}

View File

@ -9,7 +9,6 @@
<ItemGroup>
<Compile Include="..\shared\*.cs" LinkBase="shared" />
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

View File

@ -11,8 +11,8 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
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.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
@ -421,7 +421,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
const string response = "hello, world";
var logTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
mockKestrelTrace
.Setup(trace => trace.ConnectionHeadResponseBodyWrite(It.IsAny<string>(), response.Length))
.Callback<string, long>((connectionId, count) => logTcs.SetResult(null));
@ -610,7 +610,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task WhenAppWritesLessThanContentLengthErrorLogged()
{
var logTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockTrace = new Mock<IKestrelTrace>();
mockTrace
.Setup(trace => trace.ApplicationError(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<InvalidOperationException>()))
.Callback<string, string, Exception>((connectionId, requestId, ex) =>
@ -663,7 +663,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task WhenAppWritesLessThanContentLengthButRequestIsAbortedErrorNotLogged()
{
var requestAborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockTrace = new Mock<IKestrelTrace>();
using (var server = new TestServer(async httpContext =>
{

View File

@ -18,7 +18,11 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
<ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -19,7 +19,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
@ -318,8 +318,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
.Callback<LogLevel, EventId, object, Exception, Func<object, Exception, string>>((logLevel, eventId, state, exception, formatter) =>
{
Logger.Log(logLevel, eventId, state, exception, formatter);
var log = $"Log {logLevel}[{eventId}]: {formatter(state, exception)} {exception}";
TestOutputHelper.WriteLine(log);
if (eventId.Id == _connectionResetEventId)
{
@ -707,10 +705,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets")))
.Returns(mockLogger.Object);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var testContext = new TestServiceContext(mockLoggerFactory.Object)
var mockKestrelTrace = new Mock<IKestrelTrace>();
var testContext = new TestServiceContext(mockLoggerFactory.Object, mockKestrelTrace.Object)
{
Log = mockKestrelTrace.Object,
ServerOptions =
{
Limits =
@ -765,11 +762,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var readTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var appStartedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var testContext = new TestServiceContext()
{
Log = mockKestrelTrace.Object,
};
var mockKestrelTrace = new Mock<IKestrelTrace>();
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object);
var scratchBuffer = new byte[4096];

View File

@ -18,7 +18,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
@ -250,7 +249,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var clientClosedConnection = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var writeTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
var mockLogger = new Mock<ILogger>();
mockLogger
.Setup(logger => logger.IsEnabled(It.IsAny<LogLevel>()))
@ -276,9 +275,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets")))
.Returns(mockLogger.Object);
var testContext = new TestServiceContext(mockLoggerFactory.Object)
var testContext = new TestServiceContext(mockLoggerFactory.Object, mockKestrelTrace.Object)
{
Log = mockKestrelTrace.Object,
ServerOptions =
{
Limits =
@ -470,7 +468,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var requestAborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var appFuncCompleted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
mockKestrelTrace
.Setup(trace => trace.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()))
.Callback(() => responseRateTimeoutMessageLogged.SetResult(null));
@ -478,10 +476,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
.Setup(trace => trace.ConnectionStop(It.IsAny<string>()))
.Callback(() => connectionStopMessageLogged.SetResult(null));
var testContext = new TestServiceContext
var testContext = new TestServiceContext(loggerFactory, mockKestrelTrace.Object)
{
LoggerFactory = loggerFactory,
Log = mockKestrelTrace.Object,
ServerOptions =
{
Limits =
@ -539,7 +535,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
await responseRateTimeoutMessageLogged.Task.DefaultTimeout();
await connectionStopMessageLogged.Task.DefaultTimeout();
await appFuncCompleted.Task.DefaultTimeout();
await AssertStreamAborted(connection.Reader.BaseStream, chunkSize * chunks);
await AssertStreamAborted(connection.Stream, chunkSize * chunks);
sw.Stop();
logger.LogInformation("Connection was aborted after {totalMilliseconds}ms.", sw.ElapsedMilliseconds);
@ -562,7 +558,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var aborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var appFuncCompleted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
mockKestrelTrace
.Setup(trace => trace.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()))
.Callback(() => responseRateTimeoutMessageLogged.SetResult(null));
@ -616,7 +612,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
using (var connection = server.CreateConnection())
{
using (var sslStream = new SslStream(connection.Reader.BaseStream, false, (sender, cert, chain, errors) => true, null))
using (var sslStream = new SslStream(connection.Stream, false, (sender, cert, chain, errors) => true, null))
{
await sslStream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false);
@ -628,7 +624,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
await connectionStopMessageLogged.Task.DefaultTimeout();
await appFuncCompleted.Task.DefaultTimeout();
await AssertStreamAborted(connection.Reader.BaseStream, chunkSize * chunks);
await AssertStreamAborted(connection.Stream, chunkSize * chunks);
}
}
}
@ -647,7 +643,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var requestAborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var copyToAsyncCts = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
mockKestrelTrace
.Setup(trace => trace.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()))
.Callback(() => responseRateTimeoutMessageLogged.SetResult(null));
@ -655,10 +651,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
.Setup(trace => trace.ConnectionStop(It.IsAny<string>()))
.Callback(() => connectionStopMessageLogged.SetResult(null));
var testContext = new TestServiceContext
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
{
LoggerFactory = LoggerFactory,
Log = mockKestrelTrace.Object,
ServerOptions =
{
Limits =
@ -734,11 +728,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var requestAborted = false;
var appFuncCompleted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
var testContext = new TestServiceContext
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
{
Log = mockKestrelTrace.Object,
ServerOptions =
{
Limits =
@ -783,7 +776,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Make sure consuming a single chunk exceeds the 2 second timeout.
var targetBytesPerSecond = chunkSize / 4;
await AssertStreamCompleted(connection.Reader.BaseStream, minTotalOutputSize, targetBytesPerSecond);
await AssertStreamCompleted(connection.Stream, minTotalOutputSize, targetBytesPerSecond);
await appFuncCompleted.Task.DefaultTimeout();
mockKestrelTrace.Verify(t => t.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
@ -803,11 +796,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
var headerStringValues = new StringValues(Enumerable.Repeat(headerValue, headerCount).ToArray());
var requestAborted = false;
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
var mockKestrelTrace = new Mock<IKestrelTrace>();
var testContext = new TestServiceContext
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
{
Log = mockKestrelTrace.Object,
ServerOptions =
{
Limits =
@ -860,7 +852,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
// Make sure consuming a single set of response headers exceeds the 2 second timeout.
var targetBytesPerSecond = responseSize / 4;
await AssertStreamCompleted(connection.Reader.BaseStream, minTotalOutputSize, targetBytesPerSecond);
await AssertStreamCompleted(connection.Stream, minTotalOutputSize, targetBytesPerSecond);
mockKestrelTrace.Verify(t => t.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
mockKestrelTrace.Verify(t => t.ConnectionStop(It.IsAny<string>()), Times.Once());

View File

@ -12,7 +12,6 @@
<Compile Include="..\Kestrel.Transport.BindTests\**\*.cs" />
<Compile Include="..\Kestrel.Transport.Libuv.FunctionalTests\TransportSelector.cs" />
<Compile Include="..\shared\*.cs" LinkBase="shared" />
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

View File

@ -13,7 +13,6 @@
<ItemGroup>
<Compile Include="..\Kestrel.Transport.FunctionalTests\**\*.cs" />
<Compile Include="..\shared\*.cs" LinkBase="shared" />
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

View File

@ -18,9 +18,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -12,7 +12,6 @@
<Compile Include="..\Kestrel.Transport.BindTests\**\*.cs" />
<Compile Include="..\Kestrel.Transport.Sockets.FunctionalTests\TransportSelector.cs" />
<Compile Include="..\shared\*.cs" LinkBase="shared" />
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

View File

@ -12,7 +12,6 @@
<ItemGroup>
<Compile Include="..\Kestrel.Transport.FunctionalTests\**\*.cs" />
<Compile Include="..\shared\*.cs" LinkBase="shared" />
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

View File

@ -1,8 +1,8 @@
// 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 Microsoft.Extensions.Logging.Testing;
using System.Reflection;
using Microsoft.Extensions.Logging.Testing;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Testing

View File

@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
@ -85,11 +86,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
.Build();
_host.Start();
Context.Log.LogDebug($"TestServer is listening on port {Port}");
}
public IPEndPoint EndPoint => _listenOptions.IPEndPoint;
public int Port => _listenOptions.IPEndPoint.Port;
public AddressFamily AddressFamily => _listenOptions.IPEndPoint.AddressFamily;
// Avoid NullReferenceException in the CanListenToOpenTcpSocketHandle test
public int Port => _listenOptions.IPEndPoint?.Port ?? 0;
public TestServiceContext Context { get; }
@ -107,7 +109,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
public TestConnection CreateConnection()
{
return new TestConnection(Port, AddressFamily);
return new TestConnection(Port, _listenOptions.IPEndPoint.AddressFamily);
}
public Task StopAsync(CancellationToken token = default)