Skip to content

Commit 5e125f3

Browse files
committed
Support default handshake timeout
1 parent f954244 commit 5e125f3

File tree

5 files changed

+20
-17
lines changed

5 files changed

+20
-17
lines changed

src/Servers/Kestrel/Core/src/HttpsConnectionAdapterOptions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ public class HttpsConnectionAdapterOptions
2525
public HttpsConnectionAdapterOptions()
2626
{
2727
ClientCertificateMode = ClientCertificateMode.NoCertificate;
28-
// Defaults to 10 seconds
29-
HandshakeTimeout = HttpsConnectionMiddleware.DefaultHandshakeTimeout;
28+
HandshakeTimeout = TimeSpan.FromSeconds(10);
3029
}
3130

3231
/// <summary>

src/Servers/Kestrel/Core/src/Internal/SniOptionsSelector.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ public SniOptions GetOptions(ConnectionContext connection, string serverName)
144144
Debug.Assert(_fallbackServerCertificateSelector != null,
145145
"The cached SniOptions ServerCertificate can only be null if there's a fallback certificate selector.");
146146

147-
// If a ServerCertificateSelector passed into HttpsConnectionMiddleware via HttpsConnectionAdapterOptions doesn't return a cert,
148-
// HttpsConnectionMiddleware doesn't fallback to the ServerCertificate, so we don't do that here either.
147+
// If a ServerCertificateSelector doesn't return a cert, HttpsConnectionMiddleware doesn't fallback to the ServerCertificate.
149148
options = options.Clone();
150149
options.SslOptions.ServerCertificate = _fallbackServerCertificateSelector(connection, serverName);
151150
}

src/Servers/Kestrel/Core/src/KestrelConfigurationLoader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ public void Load()
359359
var logger = Options.ApplicationServices.GetRequiredService<ILogger<KestrelConfigurationLoader>>();
360360
var sniOptionsSelector = new SniOptionsSelector(this, endpoint, httpsOptions, listenOptions.Protocols, logger);
361361

362-
listenOptions.UseHttps(ServerOptionsSelectionCallback, sniOptionsSelector);
362+
listenOptions.UseHttps(ServerOptionsSelectionCallback, sniOptionsSelector, httpsOptions.HandshakeTimeout);
363363
}
364364
}
365365

src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,16 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConn
235235
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
236236
/// <param name="httpsOptionsCallback">Callback to configure HTTPS options.</param>
237237
/// <param name="state">State for the <see cref="ServerOptionsSelectionCallback" />.</param>
238+
/// <param name="handshakeTimeout">Specifies the maximum amount of time allowed for the TLS/SSL handshake. This must be positive and finite.</param>
238239
/// <returns>The <see cref="ListenOptions"/>.</returns>
239-
internal static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsOptionsCallback httpsOptionsCallback, object state = null)
240+
internal static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsOptionsCallback httpsOptionsCallback, object state, TimeSpan handshakeTimeout)
240241
{
241242
var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService<ILoggerFactory>() ?? NullLoggerFactory.Instance;
242243

243244
listenOptions.IsTls = true;
244245
listenOptions.Use(next =>
245246
{
246-
var middleware = new HttpsConnectionMiddleware(next, httpsOptionsCallback, state, loggerFactory);
247+
var middleware = new HttpsConnectionMiddleware(next, httpsOptionsCallback, state, handshakeTimeout, loggerFactory);
247248
return middleware.OnConnectionAsync;
248249
});
249250

src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ internal class HttpsConnectionMiddleware
3131
{
3232
private const string EnableWindows81Http2 = "Microsoft.AspNetCore.Server.Kestrel.EnableWindows81Http2";
3333

34-
internal static readonly TimeSpan DefaultHandshakeTimeout = TimeSpan.FromSeconds(10);
34+
private static readonly bool _isWindowsVersionIncompatible = IsWindowsVersionIncompatible();
3535

3636
private readonly ConnectionDelegate _next;
37+
private readonly TimeSpan _handshakeTimeout;
3738
private readonly ILogger _logger;
3839
private readonly Func<Stream, SslStream> _sslStreamFactory;
3940

@@ -43,7 +44,6 @@ internal class HttpsConnectionMiddleware
4344
private readonly Func<ConnectionContext, string, X509Certificate2> _serverCertificateSelector;
4445

4546
// The following fields are only set by ServerOptionsSelectionCallback ctor.
46-
// If we ever expose this via a public API, we should really create a delegate type.
4747
private readonly HttpsOptionsCallback _httpsOptionsCallback;
4848
private readonly object _httpsOptionsCallbackState;
4949

@@ -59,12 +59,13 @@ public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapter
5959
throw new ArgumentNullException(nameof(options));
6060
}
6161

62-
_options = options;
62+
_next = next;
63+
_handshakeTimeout = options.HandshakeTimeout;
6364
_logger = loggerFactory.CreateLogger<HttpsConnectionMiddleware>();
6465

66+
_options = options;
6567
_options.HttpProtocols = ValidateAndNormalizeHttpProtocols(_options.HttpProtocols, _logger);
6668

67-
_next = next;
6869
// capture the certificate now so it can't be switched after validation
6970
_serverCertificate = options.ServerCertificate;
7071
_serverCertificateSelector = options.ServerCertificateSelector;
@@ -94,10 +95,13 @@ internal HttpsConnectionMiddleware(
9495
ConnectionDelegate next,
9596
HttpsOptionsCallback httpsOptionsCallback,
9697
object httpsOptionsCallbackState,
98+
TimeSpan handshakeTimeout,
9799
ILoggerFactory loggerFactory)
98100
{
99101
_next = next;
102+
_handshakeTimeout = handshakeTimeout;
100103
_logger = loggerFactory.CreateLogger<HttpsConnectionMiddleware>();
104+
101105
_httpsOptionsCallback = httpsOptionsCallback;
102106
_httpsOptionsCallbackState = httpsOptionsCallbackState;
103107
_sslStreamFactory = s => new SslStream(s);
@@ -122,7 +126,7 @@ public async Task OnConnectionAsync(ConnectionContext context)
122126

123127
try
124128
{
125-
using var cancellationTokenSource = new CancellationTokenSource(_options?.HandshakeTimeout ?? DefaultHandshakeTimeout);
129+
using var cancellationTokenSource = new CancellationTokenSource(_handshakeTimeout);
126130
if (_httpsOptionsCallback is null)
127131
{
128132
await DoOptionsBasedHandshakeAsync(context, sslStream, feature, cancellationTokenSource.Token);
@@ -255,9 +259,6 @@ internal static void ConfigureAlpn(SslServerAuthenticationOptions serverOptions,
255259
}
256260
}
257261

258-
private bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
259-
RemoteCertificateValidationCallback(_options.ClientCertificateMode, _options.ClientCertificateValidation, certificate, chain, sslPolicyErrors);
260-
261262
internal static bool RemoteCertificateValidationCallback(
262263
ClientCertificateMode clientCertificateMode,
263264
Func<X509Certificate2, X509Chain, SslPolicyErrors, bool> clientCertificateValidation,
@@ -295,6 +296,9 @@ internal static bool RemoteCertificateValidationCallback(
295296
return true;
296297
}
297298

299+
private bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
300+
RemoteCertificateValidationCallback(_options.ClientCertificateMode, _options.ClientCertificateValidation, certificate, chain, sslPolicyErrors);
301+
298302
private SslDuplexPipe CreateSslDuplexPipe(IDuplexPipe transport, MemoryPool<byte> memoryPool)
299303
{
300304
var inputPipeOptions = new StreamPipeReaderOptions
@@ -366,12 +370,12 @@ internal static HttpProtocols ValidateAndNormalizeHttpProtocols(HttpProtocols ht
366370
{
367371
throw new NotSupportedException(CoreStrings.Http2NoTlsOsx);
368372
}
369-
else if (IsWindowsVersionIncompatible())
373+
else if (_isWindowsVersionIncompatible)
370374
{
371375
throw new NotSupportedException(CoreStrings.Http2NoTlsWin81);
372376
}
373377
}
374-
else if (httpProtocols == HttpProtocols.Http1AndHttp2 && IsWindowsVersionIncompatible())
378+
else if (httpProtocols == HttpProtocols.Http1AndHttp2 && _isWindowsVersionIncompatible)
375379
{
376380
logger.Http2DefaultCiphersInsufficient();
377381
return HttpProtocols.Http1;

0 commit comments

Comments
 (0)