Convert TLS connection adapter to connection middleware (#11109)

This commit is contained in:
Justin Kotalik 2019-06-19 20:02:44 -07:00 committed by GitHub
parent 0b5d4baa07
commit 689b052509
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 411 additions and 364 deletions

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
public AdaptedPipeline(IDuplexPipe transport,
Pipe inputPipe,
Pipe outputPipe,
IKestrelTrace log,
ILogger log,
int minAllocBufferSize)
{
TransportStream = new RawStream(transport.Input, transport.Output, throwOnCancelled: true);
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
public Pipe Output { get; }
public IKestrelTrace Log { get; }
public ILogger Log { get; }
PipeReader IDuplexPipe.Input => Input.Reader;

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO.Pipelines;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
@ -96,5 +97,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
_handshakeTimeout = value != Timeout.InfiniteTimeSpan ? value : TimeSpan.MaxValue;
}
}
internal PipeScheduler Scheduler { get; set; } = PipeScheduler.ThreadPool;
internal long? MaxInputBufferSize { get; set; }
internal long? MaxOutputBufferSize { get; set; }
}
}

View File

@ -215,64 +215,90 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
private void StopProcessingNextRequest()
{
ProtocolSelectionState previousState;
lock (_protocolSelectionLock)
{
previousState = _protocolSelectionState;
switch (_protocolSelectionState)
{
case ProtocolSelectionState.Initializing:
CloseUninitializedConnection(new ConnectionAbortedException(CoreStrings.ServerShutdownDuringConnectionInitialization));
_protocolSelectionState = ProtocolSelectionState.Aborted;
break;
case ProtocolSelectionState.Selected:
_requestProcessor.StopProcessingNextRequest();
break;
case ProtocolSelectionState.Aborted:
break;
}
}
switch (previousState)
{
case ProtocolSelectionState.Initializing:
CloseUninitializedConnection(new ConnectionAbortedException(CoreStrings.ServerShutdownDuringConnectionInitialization));
break;
case ProtocolSelectionState.Selected:
_requestProcessor.StopProcessingNextRequest();
break;
case ProtocolSelectionState.Aborted:
break;
}
}
private void OnInputOrOutputCompleted()
{
ProtocolSelectionState previousState;
lock (_protocolSelectionLock)
{
previousState = _protocolSelectionState;
switch (_protocolSelectionState)
{
case ProtocolSelectionState.Initializing:
// OnReader/WriterCompleted callbacks are not wired until after leaving the Initializing state.
Debug.Assert(false);
CloseUninitializedConnection(new ConnectionAbortedException("HttpConnection.OnInputOrOutputCompleted() called while in the ProtocolSelectionState.Initializing state!?"));
_protocolSelectionState = ProtocolSelectionState.Aborted;
break;
case ProtocolSelectionState.Selected:
_requestProcessor.OnInputOrOutputCompleted();
break;
case ProtocolSelectionState.Aborted:
break;
}
}
switch (previousState)
{
case ProtocolSelectionState.Initializing:
// ConnectionClosed callback is not wired up until after leaving the Initializing state.
Debug.Assert(false);
CloseUninitializedConnection(new ConnectionAbortedException("HttpConnection.OnInputOrOutputCompleted() called while in the ProtocolSelectionState.Initializing state!?"));
break;
case ProtocolSelectionState.Selected:
_requestProcessor.OnInputOrOutputCompleted();
break;
case ProtocolSelectionState.Aborted:
break;
}
}
private void Abort(ConnectionAbortedException ex)
{
ProtocolSelectionState previousState;
lock (_protocolSelectionLock)
{
switch (_protocolSelectionState)
{
case ProtocolSelectionState.Initializing:
CloseUninitializedConnection(ex);
break;
case ProtocolSelectionState.Selected:
_requestProcessor.Abort(ex);
break;
case ProtocolSelectionState.Aborted:
break;
}
previousState = _protocolSelectionState;
_protocolSelectionState = ProtocolSelectionState.Aborted;
}
switch (previousState)
{
case ProtocolSelectionState.Initializing:
CloseUninitializedConnection(ex);
break;
case ProtocolSelectionState.Selected:
_requestProcessor.Abort(ex);
break;
case ProtocolSelectionState.Aborted:
break;
}
}
private async Task<Stream> ApplyConnectionAdaptersAsync(RawStream stream)
@ -365,6 +391,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
private void CloseUninitializedConnection(ConnectionAbortedException abortReason)
{
_context.ConnectionContext.Abort(abortReason);
if (_context.ConnectionAdapters.Count > 0)
{
_adaptedTransport.Input.Complete();
_adaptedTransport.Output.Complete();
}
}
public void OnTimeout(TimeoutReason reason)

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security.Authentication;
@ -16,27 +17,27 @@ 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.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
{
internal class HttpsConnectionAdapter : IConnectionAdapter
internal class HttpsConnectionMiddleware
{
private static readonly ClosedAdaptedConnection _closedAdaptedConnection = new ClosedAdaptedConnection();
private readonly ConnectionDelegate _next;
private readonly HttpsConnectionAdapterOptions _options;
private readonly ILogger _logger;
private readonly X509Certificate2 _serverCertificate;
private readonly Func<ConnectionContext, string, X509Certificate2> _serverCertificateSelector;
private readonly ILogger _logger;
public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options)
: this(options, loggerFactory: null)
public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options)
: this(next, options, loggerFactory: NullLoggerFactory.Instance)
{
}
public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options, ILoggerFactory loggerFactory)
public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options, ILoggerFactory loggerFactory)
{
if (options == null)
{
@ -49,6 +50,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
throw new NotSupportedException(CoreStrings.HTTP2NoTlsOsx);
}
_next = next;
// capture the certificate now so it can't be switched after validation
_serverCertificate = options.ServerCertificate;
_serverCertificateSelector = options.ServerCertificateSelector;
@ -69,18 +71,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
}
_options = options;
_logger = loggerFactory?.CreateLogger<HttpsConnectionAdapter>() ?? (ILogger)NullLogger.Instance;
_logger = loggerFactory?.CreateLogger<HttpsConnectionMiddleware>();
}
public bool IsHttps => true;
public Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
public Task OnConnectionAsync(ConnectionContext context)
{
// Don't trust SslStream not to block.
return Task.Run(() => InnerOnConnectionAsync(context));
}
private async Task<IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterContext context)
private async Task InnerOnConnectionAsync(ConnectionContext context)
{
SslStream sslStream;
bool certificateRequired;
@ -88,14 +86,43 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
context.Features.Set<ITlsConnectionFeature>(feature);
context.Features.Set<ITlsHandshakeFeature>(feature);
// TODO: Handle the cases where this can be null
var memoryPoolFeature = context.Features.Get<IMemoryPoolFeature>();
var inputPipeOptions = new PipeOptions
(
pool: memoryPoolFeature.MemoryPool,
readerScheduler: _options.Scheduler,
writerScheduler: PipeScheduler.Inline,
pauseWriterThreshold: _options.MaxInputBufferSize ?? 0,
resumeWriterThreshold: _options.MaxInputBufferSize / 2 ?? 0,
useSynchronizationContext: false,
minimumSegmentSize: memoryPoolFeature.MemoryPool.GetMinimumSegmentSize()
);
var outputPipeOptions = new PipeOptions
(
pool: memoryPoolFeature.MemoryPool,
readerScheduler: PipeScheduler.Inline,
writerScheduler: PipeScheduler.Inline,
pauseWriterThreshold: _options.MaxOutputBufferSize ?? 0,
resumeWriterThreshold: _options.MaxOutputBufferSize / 2 ?? 0,
useSynchronizationContext: false,
minimumSegmentSize: memoryPoolFeature.MemoryPool.GetMinimumSegmentSize()
);
// TODO: eventually make SslDuplexStream : Stream, IDuplexPipe to avoid RawStream allocation and pipe allocations
var adaptedPipeline = new AdaptedPipeline(context.Transport, new Pipe(inputPipeOptions), new Pipe(outputPipeOptions), _logger, memoryPoolFeature.MemoryPool.GetMinimumAllocSize());
var transportStream = adaptedPipeline.TransportStream;
if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
{
sslStream = new SslStream(context.ConnectionStream);
sslStream = new SslStream(transportStream);
certificateRequired = false;
}
else
{
sslStream = new SslStream(context.ConnectionStream,
sslStream = new SslStream(transportStream,
leaveInnerStreamOpen: false,
userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
{
@ -132,69 +159,66 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
certificateRequired = true;
}
var timeoutFeature = context.Features.Get<IConnectionTimeoutFeature>();
timeoutFeature.SetTimeout(_options.HandshakeTimeout);
try
using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout))
using (cancellationTokeSource.Token.UnsafeRegister(state => ((ConnectionContext)state).Abort(), context))
{
// Adapt to the SslStream signature
ServerCertificateSelectionCallback selector = null;
if (_serverCertificateSelector != null)
try
{
selector = (sender, name) =>
// Adapt to the SslStream signature
ServerCertificateSelectionCallback selector = null;
if (_serverCertificateSelector != null)
{
context.Features.Set(sslStream);
var cert = _serverCertificateSelector(context.ConnectionContext, name);
if (cert != null)
selector = (sender, name) =>
{
EnsureCertificateIsAllowedForServerAuth(cert);
}
return cert;
context.Features.Set(sslStream);
var cert = _serverCertificateSelector(context, name);
if (cert != null)
{
EnsureCertificateIsAllowedForServerAuth(cert);
}
return cert;
};
}
var sslOptions = new SslServerAuthenticationOptions
{
ServerCertificate = _serverCertificate,
ServerCertificateSelectionCallback = selector,
ClientCertificateRequired = certificateRequired,
EnabledSslProtocols = _options.SslProtocols,
CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
ApplicationProtocols = new List<SslApplicationProtocol>()
};
// This is order sensitive
if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
{
sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
// https://tools.ietf.org/html/rfc7540#section-9.2.1
sslOptions.AllowRenegotiation = false;
}
if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
{
sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
}
_options.OnAuthenticate?.Invoke(context, sslOptions);
await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
}
var sslOptions = new SslServerAuthenticationOptions
catch (OperationCanceledException)
{
ServerCertificate = _serverCertificate,
ServerCertificateSelectionCallback = selector,
ClientCertificateRequired = certificateRequired,
EnabledSslProtocols = _options.SslProtocols,
CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
ApplicationProtocols = new List<SslApplicationProtocol>()
};
// This is order sensitive
if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
{
sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
// https://tools.ietf.org/html/rfc7540#section-9.2.1
sslOptions.AllowRenegotiation = false;
_logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
sslStream.Dispose();
return;
}
if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
catch (Exception ex) when (ex is IOException || ex is AuthenticationException)
{
sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
_logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
sslStream.Dispose();
return;
}
_options.OnAuthenticate?.Invoke(context.ConnectionContext, sslOptions);
await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
}
catch (OperationCanceledException)
{
_logger.LogDebug(2, CoreStrings.AuthenticationTimedOut);
sslStream.Dispose();
return _closedAdaptedConnection;
}
catch (Exception ex) when (ex is IOException || ex is AuthenticationException)
{
_logger.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
sslStream.Dispose();
return _closedAdaptedConnection;
}
finally
{
timeoutFeature.CancelTimeout();
}
feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
@ -208,7 +232,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
feature.KeyExchangeStrength = sslStream.KeyExchangeStrength;
feature.Protocol = sslStream.SslProtocol;
return new HttpsAdaptedConnection(sslStream);
var original = context.Transport;
try
{
context.Transport = adaptedPipeline;
using (sslStream)
{
try
{
adaptedPipeline.RunAsync(sslStream);
await _next(context);
}
finally
{
await adaptedPipeline.CompleteAsync();
}
}
}
finally
{
// Restore the original so that it gets closed appropriately
context.Transport = original;
}
}
private static void EnsureCertificateIsAllowedForServerAuth(X509Certificate2 certificate)
@ -233,31 +281,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
return new X509Certificate2(certificate);
}
private class HttpsAdaptedConnection : IAdaptedConnection
{
private readonly SslStream _sslStream;
public HttpsAdaptedConnection(SslStream sslStream)
{
_sslStream = sslStream;
}
public Stream ConnectionStream => _sslStream;
public void Dispose()
{
_sslStream.Dispose();
}
}
private class ClosedAdaptedConnection : IAdaptedConnection
{
public Stream ConnectionStream { get; } = new ClosedStream();
public void Dispose()
{
}
}
}
}

View File

@ -256,7 +256,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
}
// EndpointDefaults or configureEndpoint may have added an https adapter.
if (https && !listenOptions.ConnectionAdapters.Any(f => f.IsHttps))
if (https && !listenOptions.IsTls)
{
if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
{

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;

View File

@ -89,23 +89,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
public IServiceProvider ApplicationServices => KestrelServerOptions?.ApplicationServices;
internal string Scheme
{
get
{
if (IsHttp)
{
return IsTls ? "https" : "http";
}
return "tcp";
}
}
internal bool IsHttp { get; set; } = true;
internal bool IsTls { get; set; }
/// <summary>
/// Gets the name of this endpoint to display on command-line when the web server starts.
/// </summary>
internal virtual string GetDisplayName()
{
var scheme = ConnectionAdapters.Any(f => f.IsHttps)
? "https"
: "http";
switch (EndPoint)
{
case IPEndPoint _:
return $"{scheme}://{IPEndPoint}";
return $"{Scheme}://{IPEndPoint}";
case UnixDomainSocketEndPoint _:
return $"{scheme}://unix:{EndPoint}";
return $"{Scheme}://unix:{EndPoint}";
case FileHandleEndPoint _:
return $"{scheme}://<file handle>";
return $"{Scheme}://<file handle>";
default:
throw new InvalidOperationException();
}

View File

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Hosting
{
@ -185,6 +186,7 @@ namespace Microsoft.AspNetCore.Hosting
{
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
}
return listenOptions.UseHttps(options);
}
@ -199,6 +201,7 @@ namespace Microsoft.AspNetCore.Hosting
{
return false;
}
listenOptions.UseHttps(options);
return true;
}
@ -211,10 +214,20 @@ namespace Microsoft.AspNetCore.Hosting
/// <returns>The <see cref="ListenOptions"/>.</returns>
public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions)
{
var loggerFactory = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<ILoggerFactory>();
var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService<ILoggerFactory>() ?? NullLoggerFactory.Instance;
// Set the list of protocols from listen options
httpsOptions.HttpProtocols = listenOptions.Protocols;
listenOptions.ConnectionAdapters.Add(new HttpsConnectionAdapter(httpsOptions, loggerFactory));
httpsOptions.MaxInputBufferSize = listenOptions.KestrelServerOptions?.Limits.MaxRequestBufferSize;
httpsOptions.MaxOutputBufferSize = listenOptions.KestrelServerOptions?.Limits.MaxResponseBufferSize;
listenOptions.IsTls = true;
listenOptions.Use(next =>
{
var middleware = new HttpsConnectionMiddleware(next, httpsOptions, loggerFactory);
return middleware.OnConnectionAsync;
});
return listenOptions;
}
}

View File

@ -28,11 +28,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
/// </summary>
internal override string GetDisplayName()
{
var scheme = ConnectionAdapters.Any(f => f.IsHttps)
? "https"
: "http";
return $"{scheme}://localhost:{IPEndPoint.Port}";
return $"{Scheme}://localhost:{IPEndPoint.Port}";
}
internal override async Task BindAsync(AddressBindContext context)
@ -78,6 +74,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
{
KestrelServerOptions = KestrelServerOptions,
Protocols = Protocols,
IsTls = IsTls
};
options._middleware.AddRange(_middleware);

View File

@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
StartDummyApplication(server);
Assert.True(server.Options.ListenOptions.Any());
Assert.Contains(server.Options.ListenOptions[0].ConnectionAdapters, adapter => adapter.IsHttps);
Assert.True(server.Options.ListenOptions[0].IsTls);
}
}

View File

@ -168,8 +168,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
Assert.True(ran1);
Assert.True(ran2);
Assert.NotNull(serverOptions.ListenOptions[0].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
Assert.Null(serverOptions.ListenOptions[1].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
Assert.True(serverOptions.ListenOptions[0].IsTls);
Assert.False(serverOptions.ListenOptions[1].IsTls);
}
[Fact]
@ -210,8 +210,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
Assert.True(ran2);
// You only get Https once per endpoint.
Assert.NotNull(serverOptions.ListenOptions[0].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
Assert.NotNull(serverOptions.ListenOptions[1].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
Assert.True(serverOptions.ListenOptions[0].IsTls);
Assert.True(serverOptions.ListenOptions[1].IsTls);
}
[Fact]

View File

@ -42,12 +42,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
public TestServer(RequestDelegate app, TestServiceContext context, ListenOptions listenOptions)
: this(app, context, listenOptions, _ => { })
: this(app, context, options => options.ListenOptions.Add(listenOptions), _ => { })
{
}
public TestServer(RequestDelegate app, TestServiceContext context, ListenOptions listenOptions, Action<IServiceCollection> configureServices)
: this(app, context, options => options.ListenOptions.Add(listenOptions), configureServices)
public TestServer(RequestDelegate app, TestServiceContext context, Action<ListenOptions> configureListenOptions)
: this(app, context, options =>
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
{
KestrelServerOptions = options
};
configureListenOptions(listenOptions);
options.ListenOptions.Add(listenOptions);
}, _ => { })
{
}

View File

@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
var ex = Assert.Throws<NotSupportedException>(() => new TestServer(context =>
{
throw new NotImplementedException();
}, new TestServiceContext(LoggerFactory),
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
kestrelOptions =>
{
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
}, new TestServiceContext(LoggerFactory),
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
kestrelOptions =>
{
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
}, new TestServiceContext(LoggerFactory),
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
kestrelOptions =>
{
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>

View File

@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
.Setup(m => m.Http2ConnectionClosing(It.IsAny<string>()))
.Callback(() => requestStopping.SetResult(null));
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object);
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object) { ExpectedConnectionMiddlewareCount = 1 };
testContext.InitializeHeartbeat();
@ -111,7 +111,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
var testContext = new TestServiceContext(LoggerFactory)
{
MemoryPoolFactory = memoryPoolFactory.Create
MemoryPoolFactory = memoryPoolFactory.Create,
ExpectedConnectionMiddlewareCount = 1
};
TestApplicationErrorLogger.ThrowOnUngracefulShutdown = false;

View File

@ -583,18 +583,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
MinResponseDataRate = new MinDataRate(bytesPerSecond: 1024 * 1024, gracePeriod: TimeSpan.FromSeconds(2))
}
}
},
ExpectedConnectionMiddlewareCount = 1
};
testContext.InitializeHeartbeat();
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = certificate })
}
};
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = certificate });
}
using (var server = new TestServer(async context =>
{
@ -621,7 +619,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await aborted.Task.DefaultTimeout();
}
}, testContext, listenOptions))
}, testContext, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.Http2
private static X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();
[ConditionalFact]
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/7000 ")]
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/7000")]
public async Task TlsHandshakeRejectsTlsLessThan12()
{
using (var server = new TestServer(context =>
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.Http2
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
},
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1},
listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;

View File

@ -13,6 +13,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
@ -34,15 +35,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task CanReadAndWriteWithHttpsConnectionAdapter()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 })
}
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
};
await using (var server = new TestServer(App, new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(App, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
var result = await server.HttpClientSlim.PostAsync($"https://localhost:{server.Port}/",
new FormUrlEncodedContent(new[] {
@ -57,12 +55,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task HandshakeDetailsAreAvailable()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 })
}
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
};
await using (var server = new TestServer(context =>
@ -78,7 +73,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
Assert.True(tlsFeature.KeyExchangeStrength >= 0, "KeyExchangeStrength"); // May be 0 on mac
return context.Response.WriteAsync("hello world");
}, new TestServiceContext(LoggerFactory), listenOptions))
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
var result = await server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/", validateCertificate: false);
Assert.Equal("hello world", result);
@ -88,20 +83,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task RequireCertificateFailsWhenNoCertificate()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
ConnectionAdapters =
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate
})
}
};
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate
});
await using (var server = new TestServer(App, new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(App, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, listenOptions))
{
await Assert.ThrowsAnyAsync<Exception>(
() => server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/"));
@ -111,17 +100,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task AllowCertificateContinuesWhenNoCertificate()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.AllowCertificate
})
}
};
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.AllowCertificate
});
}
await using (var server = new TestServer(context =>
{
@ -129,7 +115,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
Assert.NotNull(tlsFeature);
Assert.Null(tlsFeature.ClientCertificate);
return context.Response.WriteAsync("hello world");
}, new TestServiceContext(LoggerFactory), listenOptions))
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
var result = await server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/", validateCertificate: false);
Assert.Equal("hello world", result);
@ -139,7 +125,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public void ThrowsWhenNoServerCertificateIsProvided()
{
Assert.Throws<ArgumentException>(() => new HttpsConnectionAdapter(
Assert.Throws<ArgumentException>(() => new HttpsConnectionMiddleware(context => Task.CompletedTask,
new HttpsConnectionAdapterOptions())
);
}
@ -147,15 +133,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task UsesProvidedServerCertificate()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 })
}
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
};
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -173,24 +156,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task UsesProvidedServerCertificateSelector()
{
var selectorCalled = 0;
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
ServerCertificateSelector = (connection, name) =>
{
ServerCertificateSelector = (connection, name) =>
{
Assert.NotNull(connection);
Assert.NotNull(connection.Features.Get<SslStream>());
Assert.Equal("localhost", name);
selectorCalled++;
return _x509Certificate2;
}
})
}
};
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
Assert.NotNull(connection);
Assert.NotNull(connection.Features.Get<SslStream>());
Assert.Equal("localhost", name);
selectorCalled++;
return _x509Certificate2;
}
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -209,28 +190,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task UsesProvidedServerCertificateSelectorEachTime()
{
var selectorCalled = 0;
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
ServerCertificateSelector = (connection, name) =>
{
ServerCertificateSelector = (connection, name) =>
Assert.NotNull(connection);
Assert.NotNull(connection.Features.Get<SslStream>());
Assert.Equal("localhost", name);
selectorCalled++;
if (selectorCalled == 1)
{
Assert.NotNull(connection);
Assert.NotNull(connection.Features.Get<SslStream>());
Assert.Equal("localhost", name);
selectorCalled++;
if (selectorCalled == 1)
{
return _x509Certificate2;
}
return _x509Certificate2NoExt;
return _x509Certificate2;
}
})
}
};
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
return _x509Certificate2NoExt;
}
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -259,21 +238,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task UsesProvidedServerCertificateSelectorValidatesEkus()
{
var selectorCalled = 0;
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
ServerCertificateSelector = (features, name) =>
{
ServerCertificateSelector = (features, name) =>
{
selectorCalled++;
return TestResources.GetTestCertificate("eku.code_signing.pfx");
}
})
}
};
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
selectorCalled++;
return TestResources.GetTestCertificate("eku.code_signing.pfx");
}
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -292,25 +269,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task UsesProvidedServerCertificateSelectorOverridesServerCertificate()
{
var selectorCalled = 0;
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
ServerCertificate = _x509Certificate2NoExt,
ServerCertificateSelector = (connection, name) =>
{
ServerCertificate = _x509Certificate2NoExt,
ServerCertificateSelector = (connection, name) =>
{
Assert.NotNull(connection);
Assert.NotNull(connection.Features.Get<SslStream>());
Assert.Equal("localhost", name);
selectorCalled++;
return _x509Certificate2;
}
})
}
};
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
Assert.NotNull(connection);
Assert.NotNull(connection.Features.Get<SslStream>());
Assert.Equal("localhost", name);
selectorCalled++;
return _x509Certificate2;
}
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -329,21 +304,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task UsesProvidedServerCertificateSelectorFailsIfYouReturnNull()
{
var selectorCalled = 0;
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
ServerCertificateSelector = (features, name) =>
{
ServerCertificateSelector = (features, name) =>
{
selectorCalled++;
return null;
}
})
}
};
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
selectorCalled++;
return null;
}
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -363,19 +336,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[InlineData(HttpProtocols.Http1AndHttp2)] // Make sure Http/1.1 doesn't regress with Http/2 enabled.
public async Task CertificatePassedToHttpContext(HttpProtocols httpProtocols)
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
Protocols = httpProtocols,
ConnectionAdapters =
listenOptions.Protocols = httpProtocols;
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
})
}
};
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
});
}
await using (var server = new TestServer(context =>
{
@ -384,7 +354,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
Assert.NotNull(tlsFeature.ClientCertificate);
Assert.NotNull(context.Connection.ClientCertificate);
return context.Response.WriteAsync("hello world");
}, new TestServiceContext(LoggerFactory), listenOptions))
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -401,15 +371,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task HttpsSchemePassedToRequestFeature()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 })
}
};
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
}
await using (var server = new TestServer(context => context.Response.WriteAsync(context.Request.Scheme), new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(context => context.Response.WriteAsync(context.Request.Scheme), new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
var result = await server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/", validateCertificate: false);
Assert.Equal("https", result);
@ -419,20 +386,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task DoesNotSupportTls10()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
})
}
};
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
});
}
await using (var server = new TestServer(context => context.Response.WriteAsync("hello world"), new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(context => context.Response.WriteAsync("hello world"), new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
// SslStream is used to ensure the certificate is actually passed to the server
// HttpClient might not send the certificate because it is invalid or it doesn't match any
@ -453,26 +418,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public async Task ClientCertificateValidationGetsCalledWithNotNullParameters(ClientCertificateMode mode)
{
var clientCertificateValidationCalled = false;
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
ServerCertificate = _x509Certificate2,
ClientCertificateMode = mode,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) =>
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = mode,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) =>
{
clientCertificateValidationCalled = true;
Assert.NotNull(certificate);
Assert.NotNull(chain);
return true;
}
})
}
};
clientCertificateValidationCalled = true;
Assert.NotNull(certificate);
Assert.NotNull(chain);
return true;
}
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -490,20 +452,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[InlineData(ClientCertificateMode.RequireCertificate)]
public async Task ValidationFailureRejectsConnection(ClientCertificateMode mode)
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = mode,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => false
})
}
};
ServerCertificate = _x509Certificate2,
ClientCertificateMode = mode,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => false
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -519,19 +478,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[InlineData(ClientCertificateMode.RequireCertificate)]
public async Task RejectsConnectionOnSslPolicyErrorsWhenNoValidation(ClientCertificateMode mode)
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = mode
})
}
};
ServerCertificate = _x509Certificate2,
ClientCertificateMode = mode
});
}
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
using (var connection = server.CreateConnection())
{
@ -545,18 +501,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
[Fact]
public async Task CertificatePassedToHttpContextIsNotDisposed()
{
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
void ConfigureListenOptions(ListenOptions listenOptions)
{
ConnectionAdapters =
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
})
}
};
ServerCertificate = _x509Certificate2,
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
});
}
RequestDelegate app = context =>
{
@ -568,7 +521,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
return context.Response.WriteAsync("hello world");
};
await using (var server = new TestServer(app, new TestServiceContext(LoggerFactory), listenOptions))
await using (var server = new TestServer(app, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
{
// SslStream is used to ensure the certificate is actually passed to the server
// HttpClient might not send the certificate because it is invalid or it doesn't match any
@ -591,7 +544,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
var cert = new X509Certificate2(certPath, "testPassword");
Assert.Empty(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
{
ServerCertificate = cert,
});
@ -609,7 +562,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
var eku = Assert.Single(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
Assert.NotEmpty(eku.EnhancedKeyUsages);
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
{
ServerCertificate = cert,
});
@ -628,7 +581,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
Assert.NotEmpty(eku.EnhancedKeyUsages);
var ex = Assert.Throws<InvalidOperationException>(() =>
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
{
ServerCertificate = cert,
}));

View File

@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
LoggerFactory.AddProvider(loggerProvider);
await using (var server = new TestServer(context => Task.CompletedTask,
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(TestResources.GetTestCertificate());
@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
LoggerFactory.AddProvider(loggerProvider);
await using (var server = new TestServer(context => Task.CompletedTask,
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(TestResources.GetTestCertificate());
@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
}
}
},
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(TestResources.GetTestCertificate());
@ -237,7 +237,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
tcs.SetException(ex);
}
},
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(TestResources.GetTestCertificate());
@ -268,7 +268,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
LoggerFactory.AddProvider(loggerProvider);
await using (var server = new TestServer(context => Task.CompletedTask,
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(TestResources.GetTestCertificate());
@ -294,7 +294,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
LoggerFactory.AddProvider(loggerProvider);
await using (var server = new TestServer(context => Task.CompletedTask,
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(TestResources.GetTestCertificate());
@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
var loggerProvider = new HandshakeErrorLoggerProvider();
LoggerFactory.AddProvider(loggerProvider);
var testContext = new TestServiceContext(LoggerFactory);
var testContext = new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 };
var heartbeatManager = new HeartbeatManager(testContext.ConnectionManager);
var handshakeStartedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
@ -326,7 +326,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
listenOptions.UseHttps(o =>
{
o.ServerCertificate = new X509Certificate2(TestResources.GetTestCertificate());
o.OnAuthenticate = (_, __) => handshakeStartedTcs.SetResult(null);
o.OnAuthenticate = (_, __) =>
{
handshakeStartedTcs.SetResult(null);
};
handshakeTimeout = o.HandshakeTimeout;
});
@ -358,7 +361,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
LoggerFactory.AddProvider(loggerProvider);
await using (var server = new TestServer(context => Task.CompletedTask,
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(TestResources.GetTestCertificate());
@ -390,7 +393,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
var onAuthenticateCalled = false;
await using (var server = new TestServer(context => Task.CompletedTask,
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
@ -426,7 +429,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
var onAuthenticateCalled = false;
await using (var server = new TestServer(context => Task.CompletedTask,
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
@ -462,7 +465,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public ILogger CreateLogger(string categoryName)
{
if (categoryName == TypeNameHelper.GetTypeDisplayName(typeof(HttpsConnectionAdapter)))
if (categoryName == TypeNameHelper.GetTypeDisplayName(typeof(HttpsConnectionMiddleware)))
{
return FilterLogger;
}

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
context.Response.ContentLength = 12;
return context.Response.WriteAsync("Hello World!");
},
new TestServiceContext(LoggerFactory),
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1},
listenOptions =>
{
listenOptions.UseConnectionLogging();