Convert TLS connection adapter to connection middleware (#11109)
This commit is contained in:
parent
0b5d4baa07
commit
689b052509
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
||||||
public AdaptedPipeline(IDuplexPipe transport,
|
public AdaptedPipeline(IDuplexPipe transport,
|
||||||
Pipe inputPipe,
|
Pipe inputPipe,
|
||||||
Pipe outputPipe,
|
Pipe outputPipe,
|
||||||
IKestrelTrace log,
|
ILogger log,
|
||||||
int minAllocBufferSize)
|
int minAllocBufferSize)
|
||||||
{
|
{
|
||||||
TransportStream = new RawStream(transport.Input, transport.Output, throwOnCancelled: true);
|
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 Pipe Output { get; }
|
||||||
|
|
||||||
public IKestrelTrace Log { get; }
|
public ILogger Log { get; }
|
||||||
|
|
||||||
PipeReader IDuplexPipe.Input => Input.Reader;
|
PipeReader IDuplexPipe.Input => Input.Reader;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO.Pipelines;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
@ -96,5 +97,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
||||||
_handshakeTimeout = value != Timeout.InfiniteTimeSpan ? value : TimeSpan.MaxValue;
|
_handshakeTimeout = value != Timeout.InfiniteTimeSpan ? value : TimeSpan.MaxValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal PipeScheduler Scheduler { get; set; } = PipeScheduler.ThreadPool;
|
||||||
|
|
||||||
|
internal long? MaxInputBufferSize { get; set; }
|
||||||
|
|
||||||
|
internal long? MaxOutputBufferSize { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -215,64 +215,90 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
|
|
||||||
private void StopProcessingNextRequest()
|
private void StopProcessingNextRequest()
|
||||||
{
|
{
|
||||||
|
ProtocolSelectionState previousState;
|
||||||
lock (_protocolSelectionLock)
|
lock (_protocolSelectionLock)
|
||||||
{
|
{
|
||||||
|
previousState = _protocolSelectionState;
|
||||||
|
|
||||||
switch (_protocolSelectionState)
|
switch (_protocolSelectionState)
|
||||||
{
|
{
|
||||||
case ProtocolSelectionState.Initializing:
|
case ProtocolSelectionState.Initializing:
|
||||||
CloseUninitializedConnection(new ConnectionAbortedException(CoreStrings.ServerShutdownDuringConnectionInitialization));
|
|
||||||
_protocolSelectionState = ProtocolSelectionState.Aborted;
|
_protocolSelectionState = ProtocolSelectionState.Aborted;
|
||||||
break;
|
break;
|
||||||
case ProtocolSelectionState.Selected:
|
case ProtocolSelectionState.Selected:
|
||||||
_requestProcessor.StopProcessingNextRequest();
|
|
||||||
break;
|
|
||||||
case ProtocolSelectionState.Aborted:
|
case ProtocolSelectionState.Aborted:
|
||||||
break;
|
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()
|
private void OnInputOrOutputCompleted()
|
||||||
{
|
{
|
||||||
|
ProtocolSelectionState previousState;
|
||||||
lock (_protocolSelectionLock)
|
lock (_protocolSelectionLock)
|
||||||
{
|
{
|
||||||
|
previousState = _protocolSelectionState;
|
||||||
|
|
||||||
switch (_protocolSelectionState)
|
switch (_protocolSelectionState)
|
||||||
{
|
{
|
||||||
case ProtocolSelectionState.Initializing:
|
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;
|
_protocolSelectionState = ProtocolSelectionState.Aborted;
|
||||||
break;
|
break;
|
||||||
case ProtocolSelectionState.Selected:
|
case ProtocolSelectionState.Selected:
|
||||||
_requestProcessor.OnInputOrOutputCompleted();
|
|
||||||
break;
|
|
||||||
case ProtocolSelectionState.Aborted:
|
case ProtocolSelectionState.Aborted:
|
||||||
break;
|
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)
|
private void Abort(ConnectionAbortedException ex)
|
||||||
{
|
{
|
||||||
|
ProtocolSelectionState previousState;
|
||||||
|
|
||||||
lock (_protocolSelectionLock)
|
lock (_protocolSelectionLock)
|
||||||
{
|
{
|
||||||
switch (_protocolSelectionState)
|
previousState = _protocolSelectionState;
|
||||||
{
|
|
||||||
case ProtocolSelectionState.Initializing:
|
|
||||||
CloseUninitializedConnection(ex);
|
|
||||||
break;
|
|
||||||
case ProtocolSelectionState.Selected:
|
|
||||||
_requestProcessor.Abort(ex);
|
|
||||||
break;
|
|
||||||
case ProtocolSelectionState.Aborted:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_protocolSelectionState = ProtocolSelectionState.Aborted;
|
_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)
|
private async Task<Stream> ApplyConnectionAdaptersAsync(RawStream stream)
|
||||||
|
|
@ -365,6 +391,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
private void CloseUninitializedConnection(ConnectionAbortedException abortReason)
|
private void CloseUninitializedConnection(ConnectionAbortedException abortReason)
|
||||||
{
|
{
|
||||||
_context.ConnectionContext.Abort(abortReason);
|
_context.ConnectionContext.Abort(abortReason);
|
||||||
|
|
||||||
|
if (_context.ConnectionAdapters.Count > 0)
|
||||||
|
{
|
||||||
|
_adaptedTransport.Input.Complete();
|
||||||
|
_adaptedTransport.Output.Complete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTimeout(TimeoutReason reason)
|
public void OnTimeout(TimeoutReason reason)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Pipelines;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Authentication;
|
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;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
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 HttpsConnectionAdapterOptions _options;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private readonly X509Certificate2 _serverCertificate;
|
private readonly X509Certificate2 _serverCertificate;
|
||||||
private readonly Func<ConnectionContext, string, X509Certificate2> _serverCertificateSelector;
|
private readonly Func<ConnectionContext, string, X509Certificate2> _serverCertificateSelector;
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options)
|
||||||
|
: this(next, options, loggerFactory: NullLoggerFactory.Instance)
|
||||||
public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options)
|
|
||||||
: this(options, loggerFactory: null)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options, ILoggerFactory loggerFactory)
|
public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null)
|
||||||
{
|
{
|
||||||
|
|
@ -49,6 +50,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
throw new NotSupportedException(CoreStrings.HTTP2NoTlsOsx);
|
throw new NotSupportedException(CoreStrings.HTTP2NoTlsOsx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_next = next;
|
||||||
// capture the certificate now so it can't be switched after validation
|
// capture the certificate now so it can't be switched after validation
|
||||||
_serverCertificate = options.ServerCertificate;
|
_serverCertificate = options.ServerCertificate;
|
||||||
_serverCertificateSelector = options.ServerCertificateSelector;
|
_serverCertificateSelector = options.ServerCertificateSelector;
|
||||||
|
|
@ -69,18 +71,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
_options = options;
|
_options = options;
|
||||||
_logger = loggerFactory?.CreateLogger<HttpsConnectionAdapter>() ?? (ILogger)NullLogger.Instance;
|
_logger = loggerFactory?.CreateLogger<HttpsConnectionMiddleware>();
|
||||||
}
|
}
|
||||||
|
public Task OnConnectionAsync(ConnectionContext context)
|
||||||
public bool IsHttps => true;
|
|
||||||
|
|
||||||
public Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
|
|
||||||
{
|
{
|
||||||
// Don't trust SslStream not to block.
|
|
||||||
return Task.Run(() => InnerOnConnectionAsync(context));
|
return Task.Run(() => InnerOnConnectionAsync(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterContext context)
|
private async Task InnerOnConnectionAsync(ConnectionContext context)
|
||||||
{
|
{
|
||||||
SslStream sslStream;
|
SslStream sslStream;
|
||||||
bool certificateRequired;
|
bool certificateRequired;
|
||||||
|
|
@ -88,14 +86,43 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
context.Features.Set<ITlsConnectionFeature>(feature);
|
context.Features.Set<ITlsConnectionFeature>(feature);
|
||||||
context.Features.Set<ITlsHandshakeFeature>(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)
|
if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
|
||||||
{
|
{
|
||||||
sslStream = new SslStream(context.ConnectionStream);
|
sslStream = new SslStream(transportStream);
|
||||||
certificateRequired = false;
|
certificateRequired = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sslStream = new SslStream(context.ConnectionStream,
|
sslStream = new SslStream(transportStream,
|
||||||
leaveInnerStreamOpen: false,
|
leaveInnerStreamOpen: false,
|
||||||
userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
|
userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
|
||||||
{
|
{
|
||||||
|
|
@ -132,69 +159,66 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
certificateRequired = true;
|
certificateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var timeoutFeature = context.Features.Get<IConnectionTimeoutFeature>();
|
using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout))
|
||||||
timeoutFeature.SetTimeout(_options.HandshakeTimeout);
|
using (cancellationTokeSource.Token.UnsafeRegister(state => ((ConnectionContext)state).Abort(), context))
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// Adapt to the SslStream signature
|
try
|
||||||
ServerCertificateSelectionCallback selector = null;
|
|
||||||
if (_serverCertificateSelector != null)
|
|
||||||
{
|
{
|
||||||
selector = (sender, name) =>
|
// Adapt to the SslStream signature
|
||||||
|
ServerCertificateSelectionCallback selector = null;
|
||||||
|
if (_serverCertificateSelector != null)
|
||||||
{
|
{
|
||||||
context.Features.Set(sslStream);
|
selector = (sender, name) =>
|
||||||
var cert = _serverCertificateSelector(context.ConnectionContext, name);
|
|
||||||
if (cert != null)
|
|
||||||
{
|
{
|
||||||
EnsureCertificateIsAllowedForServerAuth(cert);
|
context.Features.Set(sslStream);
|
||||||
}
|
var cert = _serverCertificateSelector(context, name);
|
||||||
return cert;
|
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);
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
var sslOptions = new SslServerAuthenticationOptions
|
|
||||||
{
|
{
|
||||||
ServerCertificate = _serverCertificate,
|
_logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
|
||||||
ServerCertificateSelectionCallback = selector,
|
sslStream.Dispose();
|
||||||
ClientCertificateRequired = certificateRequired,
|
return;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) when (ex is IOException || ex is AuthenticationException)
|
||||||
if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
|
|
||||||
{
|
{
|
||||||
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;
|
feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
|
||||||
|
|
@ -208,7 +232,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
feature.KeyExchangeStrength = sslStream.KeyExchangeStrength;
|
feature.KeyExchangeStrength = sslStream.KeyExchangeStrength;
|
||||||
feature.Protocol = sslStream.SslProtocol;
|
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)
|
private static void EnsureCertificateIsAllowedForServerAuth(X509Certificate2 certificate)
|
||||||
|
|
@ -233,31 +281,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
|
|
||||||
return new X509Certificate2(certificate);
|
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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +256,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointDefaults or configureEndpoint may have added an https adapter.
|
// 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)
|
if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
|
|
||||||
|
|
@ -89,23 +89,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
|
|
||||||
public IServiceProvider ApplicationServices => KestrelServerOptions?.ApplicationServices;
|
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>
|
/// <summary>
|
||||||
/// Gets the name of this endpoint to display on command-line when the web server starts.
|
/// Gets the name of this endpoint to display on command-line when the web server starts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal virtual string GetDisplayName()
|
internal virtual string GetDisplayName()
|
||||||
{
|
{
|
||||||
var scheme = ConnectionAdapters.Any(f => f.IsHttps)
|
|
||||||
? "https"
|
|
||||||
: "http";
|
|
||||||
|
|
||||||
switch (EndPoint)
|
switch (EndPoint)
|
||||||
{
|
{
|
||||||
case IPEndPoint _:
|
case IPEndPoint _:
|
||||||
return $"{scheme}://{IPEndPoint}";
|
return $"{Scheme}://{IPEndPoint}";
|
||||||
case UnixDomainSocketEndPoint _:
|
case UnixDomainSocketEndPoint _:
|
||||||
return $"{scheme}://unix:{EndPoint}";
|
return $"{Scheme}://unix:{EndPoint}";
|
||||||
case FileHandleEndPoint _:
|
case FileHandleEndPoint _:
|
||||||
return $"{scheme}://<file handle>";
|
return $"{Scheme}://<file handle>";
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Hosting
|
namespace Microsoft.AspNetCore.Hosting
|
||||||
{
|
{
|
||||||
|
|
@ -185,6 +186,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
return listenOptions.UseHttps(options);
|
return listenOptions.UseHttps(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,6 +201,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
listenOptions.UseHttps(options);
|
listenOptions.UseHttps(options);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -211,10 +214,20 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions)
|
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
|
// Set the list of protocols from listen options
|
||||||
httpsOptions.HttpProtocols = listenOptions.Protocols;
|
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;
|
return listenOptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal override string GetDisplayName()
|
internal override string GetDisplayName()
|
||||||
{
|
{
|
||||||
var scheme = ConnectionAdapters.Any(f => f.IsHttps)
|
return $"{Scheme}://localhost:{IPEndPoint.Port}";
|
||||||
? "https"
|
|
||||||
: "http";
|
|
||||||
|
|
||||||
return $"{scheme}://localhost:{IPEndPoint.Port}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override async Task BindAsync(AddressBindContext context)
|
internal override async Task BindAsync(AddressBindContext context)
|
||||||
|
|
@ -78,6 +74,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
{
|
{
|
||||||
KestrelServerOptions = KestrelServerOptions,
|
KestrelServerOptions = KestrelServerOptions,
|
||||||
Protocols = Protocols,
|
Protocols = Protocols,
|
||||||
|
IsTls = IsTls
|
||||||
};
|
};
|
||||||
|
|
||||||
options._middleware.AddRange(_middleware);
|
options._middleware.AddRange(_middleware);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
StartDummyApplication(server);
|
StartDummyApplication(server);
|
||||||
|
|
||||||
Assert.True(server.Options.ListenOptions.Any());
|
Assert.True(server.Options.ListenOptions.Any());
|
||||||
Assert.Contains(server.Options.ListenOptions[0].ConnectionAdapters, adapter => adapter.IsHttps);
|
Assert.True(server.Options.ListenOptions[0].IsTls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,8 +168,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
||||||
Assert.True(ran1);
|
Assert.True(ran1);
|
||||||
Assert.True(ran2);
|
Assert.True(ran2);
|
||||||
|
|
||||||
Assert.NotNull(serverOptions.ListenOptions[0].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
Assert.True(serverOptions.ListenOptions[0].IsTls);
|
||||||
Assert.Null(serverOptions.ListenOptions[1].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
Assert.False(serverOptions.ListenOptions[1].IsTls);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -210,8 +210,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
||||||
Assert.True(ran2);
|
Assert.True(ran2);
|
||||||
|
|
||||||
// You only get Https once per endpoint.
|
// You only get Https once per endpoint.
|
||||||
Assert.NotNull(serverOptions.ListenOptions[0].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
Assert.True(serverOptions.ListenOptions[0].IsTls);
|
||||||
Assert.NotNull(serverOptions.ListenOptions[1].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
Assert.True(serverOptions.ListenOptions[1].IsTls);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestServer(RequestDelegate app, TestServiceContext context, ListenOptions listenOptions)
|
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)
|
public TestServer(RequestDelegate app, TestServiceContext context, Action<ListenOptions> configureListenOptions)
|
||||||
: this(app, context, options => options.ListenOptions.Add(listenOptions), configureServices)
|
: this(app, context, options =>
|
||||||
|
{
|
||||||
|
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
||||||
|
{
|
||||||
|
KestrelServerOptions = options
|
||||||
|
};
|
||||||
|
configureListenOptions(listenOptions);
|
||||||
|
options.ListenOptions.Add(listenOptions);
|
||||||
|
}, _ => { })
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
var ex = Assert.Throws<NotSupportedException>(() => new TestServer(context =>
|
var ex = Assert.Throws<NotSupportedException>(() => new TestServer(context =>
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}, new TestServiceContext(LoggerFactory),
|
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
kestrelOptions =>
|
kestrelOptions =>
|
||||||
{
|
{
|
||||||
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
|
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
|
||||||
|
|
||||||
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
||||||
}, new TestServiceContext(LoggerFactory),
|
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
kestrelOptions =>
|
kestrelOptions =>
|
||||||
{
|
{
|
||||||
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||||
|
|
@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
|
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
|
||||||
|
|
||||||
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
||||||
}, new TestServiceContext(LoggerFactory),
|
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
kestrelOptions =>
|
kestrelOptions =>
|
||||||
{
|
{
|
||||||
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
.Setup(m => m.Http2ConnectionClosing(It.IsAny<string>()))
|
.Setup(m => m.Http2ConnectionClosing(It.IsAny<string>()))
|
||||||
.Callback(() => requestStopping.SetResult(null));
|
.Callback(() => requestStopping.SetResult(null));
|
||||||
|
|
||||||
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object);
|
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object) { ExpectedConnectionMiddlewareCount = 1 };
|
||||||
|
|
||||||
testContext.InitializeHeartbeat();
|
testContext.InitializeHeartbeat();
|
||||||
|
|
||||||
|
|
@ -111,7 +111,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
|
|
||||||
var testContext = new TestServiceContext(LoggerFactory)
|
var testContext = new TestServiceContext(LoggerFactory)
|
||||||
{
|
{
|
||||||
MemoryPoolFactory = memoryPoolFactory.Create
|
MemoryPoolFactory = memoryPoolFactory.Create,
|
||||||
|
ExpectedConnectionMiddlewareCount = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
TestApplicationErrorLogger.ThrowOnUngracefulShutdown = false;
|
TestApplicationErrorLogger.ThrowOnUngracefulShutdown = false;
|
||||||
|
|
|
||||||
|
|
@ -583,18 +583,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
{
|
{
|
||||||
MinResponseDataRate = new MinDataRate(bytesPerSecond: 1024 * 1024, gracePeriod: TimeSpan.FromSeconds(2))
|
MinResponseDataRate = new MinDataRate(bytesPerSecond: 1024 * 1024, gracePeriod: TimeSpan.FromSeconds(2))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
ExpectedConnectionMiddlewareCount = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
testContext.InitializeHeartbeat();
|
testContext.InitializeHeartbeat();
|
||||||
|
|
||||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
void ConfigureListenOptions(ListenOptions listenOptions)
|
||||||
{
|
{
|
||||||
ConnectionAdapters =
|
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = certificate });
|
||||||
{
|
}
|
||||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = certificate })
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using (var server = new TestServer(async context =>
|
using (var server = new TestServer(async context =>
|
||||||
{
|
{
|
||||||
|
|
@ -621,7 +619,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
{
|
{
|
||||||
await aborted.Task.DefaultTimeout();
|
await aborted.Task.DefaultTimeout();
|
||||||
}
|
}
|
||||||
}, testContext, listenOptions))
|
}, testContext, ConfigureListenOptions))
|
||||||
{
|
{
|
||||||
using (var connection = server.CreateConnection())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.Http2
|
||||||
private static X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();
|
private static X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/7000 ")]
|
[SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/7000")]
|
||||||
public async Task TlsHandshakeRejectsTlsLessThan12()
|
public async Task TlsHandshakeRejectsTlsLessThan12()
|
||||||
{
|
{
|
||||||
using (var server = new TestServer(context =>
|
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);
|
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
||||||
},
|
},
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1},
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.Protocols = HttpProtocols.Http2;
|
listenOptions.Protocols = HttpProtocols.Http2;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Connections.Features;
|
using Microsoft.AspNetCore.Connections.Features;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
|
@ -34,15 +35,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CanReadAndWriteWithHttpsConnectionAdapter()
|
public async Task CanReadAndWriteWithHttpsConnectionAdapter()
|
||||||
{
|
{
|
||||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
void ConfigureListenOptions(ListenOptions listenOptions)
|
||||||
{
|
{
|
||||||
ConnectionAdapters =
|
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
|
||||||
{
|
|
||||||
new HttpsConnectionAdapter(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}/",
|
var result = await server.HttpClientSlim.PostAsync($"https://localhost:{server.Port}/",
|
||||||
new FormUrlEncodedContent(new[] {
|
new FormUrlEncodedContent(new[] {
|
||||||
|
|
@ -57,12 +55,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task HandshakeDetailsAreAvailable()
|
public async Task HandshakeDetailsAreAvailable()
|
||||||
{
|
{
|
||||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
void ConfigureListenOptions(ListenOptions listenOptions)
|
||||||
{
|
{
|
||||||
ConnectionAdapters =
|
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
|
||||||
{
|
|
||||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 })
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await using (var server = new TestServer(context =>
|
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
|
Assert.True(tlsFeature.KeyExchangeStrength >= 0, "KeyExchangeStrength"); // May be 0 on mac
|
||||||
|
|
||||||
return context.Response.WriteAsync("hello world");
|
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);
|
var result = await server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/", validateCertificate: false);
|
||||||
Assert.Equal("hello world", result);
|
Assert.Equal("hello world", result);
|
||||||
|
|
@ -88,20 +83,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RequireCertificateFailsWhenNoCertificate()
|
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 =
|
ServerCertificate = _x509Certificate2,
|
||||||
{
|
ClientCertificateMode = ClientCertificateMode.RequireCertificate
|
||||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
});
|
||||||
{
|
|
||||||
ServerCertificate = _x509Certificate2,
|
|
||||||
ClientCertificateMode = ClientCertificateMode.RequireCertificate
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
await using (var server = new TestServer(App, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, listenOptions))
|
||||||
await using (var server = new TestServer(App, new TestServiceContext(LoggerFactory), listenOptions))
|
|
||||||
{
|
{
|
||||||
await Assert.ThrowsAnyAsync<Exception>(
|
await Assert.ThrowsAnyAsync<Exception>(
|
||||||
() => server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/"));
|
() => server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/"));
|
||||||
|
|
@ -111,17 +100,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AllowCertificateContinuesWhenNoCertificate()
|
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 =>
|
await using (var server = new TestServer(context =>
|
||||||
{
|
{
|
||||||
|
|
@ -129,7 +115,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
Assert.NotNull(tlsFeature);
|
Assert.NotNull(tlsFeature);
|
||||||
Assert.Null(tlsFeature.ClientCertificate);
|
Assert.Null(tlsFeature.ClientCertificate);
|
||||||
return context.Response.WriteAsync("hello world");
|
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);
|
var result = await server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/", validateCertificate: false);
|
||||||
Assert.Equal("hello world", result);
|
Assert.Equal("hello world", result);
|
||||||
|
|
@ -139,7 +125,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ThrowsWhenNoServerCertificateIsProvided()
|
public void ThrowsWhenNoServerCertificateIsProvided()
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentException>(() => new HttpsConnectionAdapter(
|
Assert.Throws<ArgumentException>(() => new HttpsConnectionMiddleware(context => Task.CompletedTask,
|
||||||
new HttpsConnectionAdapterOptions())
|
new HttpsConnectionAdapterOptions())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -147,15 +133,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task UsesProvidedServerCertificate()
|
public async Task UsesProvidedServerCertificate()
|
||||||
{
|
{
|
||||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
void ConfigureListenOptions(ListenOptions listenOptions)
|
||||||
{
|
{
|
||||||
ConnectionAdapters =
|
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
|
||||||
{
|
|
||||||
new HttpsConnectionAdapter(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())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -173,24 +156,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
public async Task UsesProvidedServerCertificateSelector()
|
public async Task UsesProvidedServerCertificateSelector()
|
||||||
{
|
{
|
||||||
var selectorCalled = 0;
|
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.NotNull(connection);
|
Assert.Equal("localhost", name);
|
||||||
Assert.NotNull(connection.Features.Get<SslStream>());
|
selectorCalled++;
|
||||||
Assert.Equal("localhost", name);
|
return _x509Certificate2;
|
||||||
selectorCalled++;
|
}
|
||||||
return _x509Certificate2;
|
});
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
|
||||||
};
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
|
|
||||||
{
|
{
|
||||||
using (var connection = server.CreateConnection())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -209,28 +190,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
public async Task UsesProvidedServerCertificateSelectorEachTime()
|
public async Task UsesProvidedServerCertificateSelectorEachTime()
|
||||||
{
|
{
|
||||||
var selectorCalled = 0;
|
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);
|
return _x509Certificate2;
|
||||||
Assert.NotNull(connection.Features.Get<SslStream>());
|
|
||||||
Assert.Equal("localhost", name);
|
|
||||||
selectorCalled++;
|
|
||||||
if (selectorCalled == 1)
|
|
||||||
{
|
|
||||||
return _x509Certificate2;
|
|
||||||
}
|
|
||||||
return _x509Certificate2NoExt;
|
|
||||||
}
|
}
|
||||||
})
|
return _x509Certificate2NoExt;
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
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())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -259,21 +238,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
public async Task UsesProvidedServerCertificateSelectorValidatesEkus()
|
public async Task UsesProvidedServerCertificateSelectorValidatesEkus()
|
||||||
{
|
{
|
||||||
var selectorCalled = 0;
|
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");
|
||||||
selectorCalled++;
|
}
|
||||||
return TestResources.GetTestCertificate("eku.code_signing.pfx");
|
});
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
|
||||||
};
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
|
|
||||||
{
|
{
|
||||||
using (var connection = server.CreateConnection())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -292,25 +269,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
public async Task UsesProvidedServerCertificateSelectorOverridesServerCertificate()
|
public async Task UsesProvidedServerCertificateSelectorOverridesServerCertificate()
|
||||||
{
|
{
|
||||||
var selectorCalled = 0;
|
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,
|
Assert.NotNull(connection);
|
||||||
ServerCertificateSelector = (connection, name) =>
|
Assert.NotNull(connection.Features.Get<SslStream>());
|
||||||
{
|
Assert.Equal("localhost", name);
|
||||||
Assert.NotNull(connection);
|
selectorCalled++;
|
||||||
Assert.NotNull(connection.Features.Get<SslStream>());
|
return _x509Certificate2;
|
||||||
Assert.Equal("localhost", name);
|
}
|
||||||
selectorCalled++;
|
});
|
||||||
return _x509Certificate2;
|
}
|
||||||
}
|
|
||||||
})
|
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
|
||||||
}
|
|
||||||
};
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
|
|
||||||
{
|
{
|
||||||
using (var connection = server.CreateConnection())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -329,21 +304,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
public async Task UsesProvidedServerCertificateSelectorFailsIfYouReturnNull()
|
public async Task UsesProvidedServerCertificateSelectorFailsIfYouReturnNull()
|
||||||
{
|
{
|
||||||
var selectorCalled = 0;
|
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;
|
||||||
selectorCalled++;
|
}
|
||||||
return null;
|
});
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
|
||||||
};
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory), listenOptions))
|
|
||||||
{
|
{
|
||||||
using (var connection = server.CreateConnection())
|
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.
|
[InlineData(HttpProtocols.Http1AndHttp2)] // Make sure Http/1.1 doesn't regress with Http/2 enabled.
|
||||||
public async Task CertificatePassedToHttpContext(HttpProtocols httpProtocols)
|
public async Task CertificatePassedToHttpContext(HttpProtocols httpProtocols)
|
||||||
{
|
{
|
||||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
void ConfigureListenOptions(ListenOptions listenOptions)
|
||||||
{
|
{
|
||||||
Protocols = httpProtocols,
|
listenOptions.Protocols = httpProtocols;
|
||||||
ConnectionAdapters =
|
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
|
||||||
{
|
{
|
||||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
ServerCertificate = _x509Certificate2,
|
||||||
{
|
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
|
||||||
ServerCertificate = _x509Certificate2,
|
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
|
||||||
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
|
});
|
||||||
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
await using (var server = new TestServer(context =>
|
await using (var server = new TestServer(context =>
|
||||||
{
|
{
|
||||||
|
|
@ -384,7 +354,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
Assert.NotNull(tlsFeature.ClientCertificate);
|
Assert.NotNull(tlsFeature.ClientCertificate);
|
||||||
Assert.NotNull(context.Connection.ClientCertificate);
|
Assert.NotNull(context.Connection.ClientCertificate);
|
||||||
return context.Response.WriteAsync("hello world");
|
return context.Response.WriteAsync("hello world");
|
||||||
}, new TestServiceContext(LoggerFactory), listenOptions))
|
}, new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 }, ConfigureListenOptions))
|
||||||
{
|
{
|
||||||
using (var connection = server.CreateConnection())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -401,15 +371,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task HttpsSchemePassedToRequestFeature()
|
public async Task HttpsSchemePassedToRequestFeature()
|
||||||
{
|
{
|
||||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
void ConfigureListenOptions(ListenOptions listenOptions)
|
||||||
{
|
{
|
||||||
ConnectionAdapters =
|
listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 });
|
||||||
{
|
}
|
||||||
new HttpsConnectionAdapter(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);
|
var result = await server.HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/", validateCertificate: false);
|
||||||
Assert.Equal("https", result);
|
Assert.Equal("https", result);
|
||||||
|
|
@ -419,20 +386,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task DoesNotSupportTls10()
|
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,
|
||||||
ServerCertificate = _x509Certificate2,
|
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
|
||||||
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
|
// 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
|
// 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)
|
public async Task ClientCertificateValidationGetsCalledWithNotNullParameters(ClientCertificateMode mode)
|
||||||
{
|
{
|
||||||
var clientCertificateValidationCalled = false;
|
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,
|
clientCertificateValidationCalled = true;
|
||||||
ClientCertificateMode = mode,
|
Assert.NotNull(certificate);
|
||||||
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) =>
|
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())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -490,20 +452,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[InlineData(ClientCertificateMode.RequireCertificate)]
|
[InlineData(ClientCertificateMode.RequireCertificate)]
|
||||||
public async Task ValidationFailureRejectsConnection(ClientCertificateMode mode)
|
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,
|
||||||
ServerCertificate = _x509Certificate2,
|
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => false
|
||||||
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())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -519,19 +478,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[InlineData(ClientCertificateMode.RequireCertificate)]
|
[InlineData(ClientCertificateMode.RequireCertificate)]
|
||||||
public async Task RejectsConnectionOnSslPolicyErrorsWhenNoValidation(ClientCertificateMode mode)
|
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())
|
using (var connection = server.CreateConnection())
|
||||||
{
|
{
|
||||||
|
|
@ -545,18 +501,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CertificatePassedToHttpContextIsNotDisposed()
|
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,
|
||||||
ServerCertificate = _x509Certificate2,
|
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
|
||||||
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
|
});
|
||||||
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
RequestDelegate app = context =>
|
RequestDelegate app = context =>
|
||||||
{
|
{
|
||||||
|
|
@ -568,7 +521,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
return context.Response.WriteAsync("hello world");
|
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
|
// 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
|
// 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");
|
var cert = new X509Certificate2(certPath, "testPassword");
|
||||||
Assert.Empty(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
|
Assert.Empty(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
|
||||||
|
|
||||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
|
||||||
{
|
{
|
||||||
ServerCertificate = cert,
|
ServerCertificate = cert,
|
||||||
});
|
});
|
||||||
|
|
@ -609,7 +562,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
var eku = Assert.Single(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
|
var eku = Assert.Single(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
|
||||||
Assert.NotEmpty(eku.EnhancedKeyUsages);
|
Assert.NotEmpty(eku.EnhancedKeyUsages);
|
||||||
|
|
||||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
|
||||||
{
|
{
|
||||||
ServerCertificate = cert,
|
ServerCertificate = cert,
|
||||||
});
|
});
|
||||||
|
|
@ -628,7 +581,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
Assert.NotEmpty(eku.EnhancedKeyUsages);
|
Assert.NotEmpty(eku.EnhancedKeyUsages);
|
||||||
|
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
|
||||||
{
|
{
|
||||||
ServerCertificate = cert,
|
ServerCertificate = cert,
|
||||||
}));
|
}));
|
||||||
|
|
@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
LoggerFactory.AddProvider(loggerProvider);
|
LoggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask,
|
await using (var server = new TestServer(context => Task.CompletedTask,
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
||||||
|
|
@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
LoggerFactory.AddProvider(loggerProvider);
|
LoggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask,
|
await using (var server = new TestServer(context => Task.CompletedTask,
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
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 =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
||||||
|
|
@ -237,7 +237,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
tcs.SetException(ex);
|
tcs.SetException(ex);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
||||||
|
|
@ -268,7 +268,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
LoggerFactory.AddProvider(loggerProvider);
|
LoggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask,
|
await using (var server = new TestServer(context => Task.CompletedTask,
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
||||||
|
|
@ -294,7 +294,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
LoggerFactory.AddProvider(loggerProvider);
|
LoggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask,
|
await using (var server = new TestServer(context => Task.CompletedTask,
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
||||||
|
|
@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
var loggerProvider = new HandshakeErrorLoggerProvider();
|
var loggerProvider = new HandshakeErrorLoggerProvider();
|
||||||
LoggerFactory.AddProvider(loggerProvider);
|
LoggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
var testContext = new TestServiceContext(LoggerFactory);
|
var testContext = new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 };
|
||||||
var heartbeatManager = new HeartbeatManager(testContext.ConnectionManager);
|
var heartbeatManager = new HeartbeatManager(testContext.ConnectionManager);
|
||||||
|
|
||||||
var handshakeStartedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var handshakeStartedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -326,7 +326,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
listenOptions.UseHttps(o =>
|
listenOptions.UseHttps(o =>
|
||||||
{
|
{
|
||||||
o.ServerCertificate = new X509Certificate2(TestResources.GetTestCertificate());
|
o.ServerCertificate = new X509Certificate2(TestResources.GetTestCertificate());
|
||||||
o.OnAuthenticate = (_, __) => handshakeStartedTcs.SetResult(null);
|
o.OnAuthenticate = (_, __) =>
|
||||||
|
{
|
||||||
|
handshakeStartedTcs.SetResult(null);
|
||||||
|
};
|
||||||
|
|
||||||
handshakeTimeout = o.HandshakeTimeout;
|
handshakeTimeout = o.HandshakeTimeout;
|
||||||
});
|
});
|
||||||
|
|
@ -358,7 +361,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
LoggerFactory.AddProvider(loggerProvider);
|
LoggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask,
|
await using (var server = new TestServer(context => Task.CompletedTask,
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
||||||
|
|
@ -390,7 +393,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
var onAuthenticateCalled = false;
|
var onAuthenticateCalled = false;
|
||||||
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask,
|
await using (var server = new TestServer(context => Task.CompletedTask,
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(httpsOptions =>
|
listenOptions.UseHttps(httpsOptions =>
|
||||||
|
|
@ -426,7 +429,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
var onAuthenticateCalled = false;
|
var onAuthenticateCalled = false;
|
||||||
|
|
||||||
await using (var server = new TestServer(context => Task.CompletedTask,
|
await using (var server = new TestServer(context => Task.CompletedTask,
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1 },
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseHttps(httpsOptions =>
|
listenOptions.UseHttps(httpsOptions =>
|
||||||
|
|
@ -462,7 +465,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
|
|
||||||
public ILogger CreateLogger(string categoryName)
|
public ILogger CreateLogger(string categoryName)
|
||||||
{
|
{
|
||||||
if (categoryName == TypeNameHelper.GetTypeDisplayName(typeof(HttpsConnectionAdapter)))
|
if (categoryName == TypeNameHelper.GetTypeDisplayName(typeof(HttpsConnectionMiddleware)))
|
||||||
{
|
{
|
||||||
return FilterLogger;
|
return FilterLogger;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||||
context.Response.ContentLength = 12;
|
context.Response.ContentLength = 12;
|
||||||
return context.Response.WriteAsync("Hello World!");
|
return context.Response.WriteAsync("Hello World!");
|
||||||
},
|
},
|
||||||
new TestServiceContext(LoggerFactory),
|
new TestServiceContext(LoggerFactory) { ExpectedConnectionMiddlewareCount = 1},
|
||||||
listenOptions =>
|
listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.UseConnectionLogging();
|
listenOptions.UseConnectionLogging();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue