From a9b95ebf17c55c52263d9b5fff9fa21b6ed62efe Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Mon, 2 Jun 2025 11:08:42 +0200 Subject: [PATCH 1/6] drop callback / implement on-demand tls client hello access --- .../src/IHttpSysRequestPropertyFeature.cs | 34 +++++ .../Http.Features/src/PublicAPI.Unshipped.txt | 2 + .../samples/TlsFeaturesObserve/Program.cs | 74 ++++----- .../samples/TlsFeaturesObserve/Startup.cs | 28 ---- .../TlsFeaturesObserve.csproj | 1 + src/Servers/HttpSys/src/HttpSysListener.cs | 10 -- src/Servers/HttpSys/src/HttpSysOptions.cs | 11 -- .../HttpSys/src/PublicAPI.Unshipped.txt | 2 - .../HttpSys/src/RequestProcessing/Request.cs | 4 - .../RequestContext.FeatureCollection.cs | 6 + .../src/RequestProcessing/RequestContext.cs | 94 ++++-------- .../RequestProcessing/RequestContextOfT.cs | 6 - .../src/RequestProcessing/TlsListener.Log.cs | 15 -- .../src/RequestProcessing/TlsListener.cs | 143 ------------------ .../HttpSys/src/StandardFeatureCollection.cs | 1 + 15 files changed, 106 insertions(+), 325 deletions(-) create mode 100644 src/Http/Http.Features/src/IHttpSysRequestPropertyFeature.cs delete mode 100644 src/Servers/HttpSys/samples/TlsFeaturesObserve/Startup.cs delete mode 100644 src/Servers/HttpSys/src/RequestProcessing/TlsListener.Log.cs delete mode 100644 src/Servers/HttpSys/src/RequestProcessing/TlsListener.cs diff --git a/src/Http/Http.Features/src/IHttpSysRequestPropertyFeature.cs b/src/Http/Http.Features/src/IHttpSysRequestPropertyFeature.cs new file mode 100644 index 000000000000..5ec810b61913 --- /dev/null +++ b/src/Http/Http.Features/src/IHttpSysRequestPropertyFeature.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Features; + +/// +/// Provides API to read HTTP_REQUEST_PROPERTY value from the HTTP.SYS request. +/// +/// +public interface IHttpSysRequestPropertyFeature +{ + /// + /// Reads the TLS client hello from HTTP.SYS + /// + /// where raw bytes of tls client hello message will be written + /// + /// Returns the number of bytes written to . + /// Or can return the size of buffer needed if wasn't large enough. + /// + /// + /// Works only if HTTP_SERVICE_CONFIG_SSL_FLAG_ENABLE_CACHE_CLIENT_HELLO flag is set on http.sys service configuration. + /// See + /// and + ///

+ /// If you don't want to guess required size before first invocation, + /// you should call first with set to empty size, so that you can retrieve through the buffer size you need, + /// then allocate that amount of memory, then retry the query. + ///
+ /// + /// True, if fetching TLS client hello was successful, false if size is not large enough. + /// If non-successful for other reason throws an exception. + /// + bool TryGetTlsClientHello(Span tlsClientHelloBytesDestination, out int bytesReturned); +} diff --git a/src/Http/Http.Features/src/PublicAPI.Unshipped.txt b/src/Http/Http.Features/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..2a8d76ffbce5 100644 --- a/src/Http/Http.Features/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Features/src/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ #nullable enable +Microsoft.AspNetCore.Http.Features.IHttpSysRequestPropertyFeature +Microsoft.AspNetCore.Http.Features.IHttpSysRequestPropertyFeature.TryGetTlsClientHello(System.Span tlsClientHelloBytesDestination, out int bytesReturned) -> bool diff --git a/src/Servers/HttpSys/samples/TlsFeaturesObserve/Program.cs b/src/Servers/HttpSys/samples/TlsFeaturesObserve/Program.cs index 3742211a9a8d..ed61268897ef 100644 --- a/src/Servers/HttpSys/samples/TlsFeaturesObserve/Program.cs +++ b/src/Servers/HttpSys/samples/TlsFeaturesObserve/Program.cs @@ -1,60 +1,48 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; +using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.HttpSys; using Microsoft.Extensions.Hosting; -using TlsFeatureObserve; using TlsFeaturesObserve.HttpSys; HttpSysConfigurator.ConfigureCacheTlsClientHello(); -CreateHostBuilder(args).Build().Run(); - -static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHost(webBuilder => - { - webBuilder.UseStartup() - .UseHttpSys(options => - { - // If you want to use https locally: https://stackoverflow.com/a/51841893 - options.UrlPrefixes.Add("https://*:6000"); // HTTPS - - options.Authentication.Schemes = AuthenticationSchemes.None; - options.Authentication.AllowAnonymous = true; - - options.TlsClientHelloBytesCallback = ProcessTlsClientHello; - }); - }); - -static void ProcessTlsClientHello(IFeatureCollection features, ReadOnlySpan tlsClientHelloBytes) -{ - var httpConnectionFeature = features.Get(); - - var myTlsFeature = new MyTlsFeature( - connectionId: httpConnectionFeature.ConnectionId, - tlsClientHelloLength: tlsClientHelloBytes.Length); - features.Set(myTlsFeature); -} +var builder = WebApplication.CreateBuilder(args); -public interface IMyTlsFeature +builder.WebHost.UseHttpSys(options => { - string ConnectionId { get; } - int TlsClientHelloLength { get; } -} + options.UrlPrefixes.Add("https://*:6000"); + options.Authentication.Schemes = AuthenticationSchemes.None; + options.Authentication.AllowAnonymous = true; +}); -public class MyTlsFeature : IMyTlsFeature +var app = builder.Build(); + +app.Use(async (context, next) => { - public string ConnectionId { get; } - public int TlsClientHelloLength { get; } - - public MyTlsFeature(string connectionId, int tlsClientHelloLength) - { - ConnectionId = connectionId; - TlsClientHelloLength = tlsClientHelloLength; - } -} + var connectionFeature = context.Features.GetRequiredFeature(); + var httpSysPropFeature = context.Features.GetRequiredFeature(); + + // first time invocation to find out required size + var success = httpSysPropFeature.TryGetTlsClientHello(Array.Empty(), out var bytesReturned); + Debug.Assert(!success); + Debug.Assert(bytesReturned > 0); + + // rent with enough memory span and invoke + var bytes = ArrayPool.Shared.Rent(bytesReturned); + success = httpSysPropFeature.TryGetTlsClientHello(bytes, out _); + Debug.Assert(success); + + await context.Response.WriteAsync($"[Response] connectionId={connectionFeature.ConnectionId}; tlsClientHello.length={bytesReturned}; tlsclienthello start={string.Join(' ', bytes.AsSpan(0, 30).ToArray())}"); + await next(context); +}); + +app.Run(); diff --git a/src/Servers/HttpSys/samples/TlsFeaturesObserve/Startup.cs b/src/Servers/HttpSys/samples/TlsFeaturesObserve/Startup.cs deleted file mode 100644 index 8ba6d27aef98..000000000000 --- a/src/Servers/HttpSys/samples/TlsFeaturesObserve/Startup.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Connections.Features; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.HttpSys; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace TlsFeatureObserve; - -public class Startup -{ - public void Configure(IApplicationBuilder app) - { - app.Run(async (HttpContext context) => - { - context.Response.ContentType = "text/plain"; - - var tlsFeature = context.Features.Get(); - await context.Response.WriteAsync("TlsClientHello data: " + $"connectionId={tlsFeature?.ConnectionId}; length={tlsFeature?.TlsClientHelloLength}"); - }); - } -} diff --git a/src/Servers/HttpSys/samples/TlsFeaturesObserve/TlsFeaturesObserve.csproj b/src/Servers/HttpSys/samples/TlsFeaturesObserve/TlsFeaturesObserve.csproj index f65f8a98a72a..3ba4390b4e73 100644 --- a/src/Servers/HttpSys/samples/TlsFeaturesObserve/TlsFeaturesObserve.csproj +++ b/src/Servers/HttpSys/samples/TlsFeaturesObserve/TlsFeaturesObserve.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Servers/HttpSys/src/HttpSysListener.cs b/src/Servers/HttpSys/src/HttpSysListener.cs index 02f595c98fe6..1963aceea284 100644 --- a/src/Servers/HttpSys/src/HttpSysListener.cs +++ b/src/Servers/HttpSys/src/HttpSysListener.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpSys.Internal; -using Microsoft.AspNetCore.Server.HttpSys.RequestProcessing; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Windows.Win32; @@ -42,7 +41,6 @@ internal sealed partial class HttpSysListener : IDisposable private readonly UrlGroup _urlGroup; private readonly RequestQueue _requestQueue; private readonly DisconnectListener _disconnectListener; - private readonly TlsListener? _tlsListener; private readonly object _internalLock; @@ -76,12 +74,7 @@ public HttpSysListener(HttpSysOptions options, ILoggerFactory loggerFactory) _requestQueue = new RequestQueue(options.RequestQueueName, options.RequestQueueMode, options.RequestQueueSecurityDescriptor, Logger); _urlGroup = new UrlGroup(_serverSession, _requestQueue, Logger); - _disconnectListener = new DisconnectListener(_requestQueue, Logger); - if (options.TlsClientHelloBytesCallback is not null) - { - _tlsListener = new TlsListener(Logger, options.TlsClientHelloBytesCallback); - } } catch (Exception exception) { @@ -89,7 +82,6 @@ public HttpSysListener(HttpSysOptions options, ILoggerFactory loggerFactory) _requestQueue?.Dispose(); _urlGroup?.Dispose(); _serverSession?.Dispose(); - _tlsListener?.Dispose(); Log.HttpSysListenerCtorError(Logger, exception); throw; } @@ -106,7 +98,6 @@ internal enum State internal UrlGroup UrlGroup => _urlGroup; internal RequestQueue RequestQueue => _requestQueue; - internal TlsListener? TlsListener => _tlsListener; internal DisconnectListener DisconnectListener => _disconnectListener; public HttpSysOptions Options { get; } @@ -260,7 +251,6 @@ private void DisposeInternal() Debug.Assert(!_serverSession.Id.IsInvalid, "ServerSessionHandle is invalid in CloseV2Config"); _serverSession.Dispose(); - _tlsListener?.Dispose(); } /// diff --git a/src/Servers/HttpSys/src/HttpSysOptions.cs b/src/Servers/HttpSys/src/HttpSysOptions.cs index bb9cdc6954e4..3d47e5cea32d 100644 --- a/src/Servers/HttpSys/src/HttpSysOptions.cs +++ b/src/Servers/HttpSys/src/HttpSysOptions.cs @@ -250,17 +250,6 @@ public Http503VerbosityLevel Http503Verbosity /// public bool UseLatin1RequestHeaders { get; set; } - /// - /// A callback to be invoked to get the TLS client hello bytes. - /// Null by default. - /// - /// - /// Works only if HTTP_SERVICE_CONFIG_SSL_FLAG_ENABLE_CACHE_CLIENT_HELLO flag is set on http.sys service configuration. - /// See - /// and - /// - public Action>? TlsClientHelloBytesCallback { get; set; } - // Not called when attaching to an existing queue. internal void Apply(UrlGroup urlGroup, RequestQueue? requestQueue) { diff --git a/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt b/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt index 393f7b26a8f3..091f7f846729 100644 --- a/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt +++ b/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt @@ -1,5 +1,3 @@ #nullable enable -Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.TlsClientHelloBytesCallback.get -> System.Action>? -Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.TlsClientHelloBytesCallback.set -> void Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.RequestQueueSecurityDescriptor.get -> System.Security.AccessControl.GenericSecurityDescriptor? Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.RequestQueueSecurityDescriptor.set -> void diff --git a/src/Servers/HttpSys/src/RequestProcessing/Request.cs b/src/Servers/HttpSys/src/RequestProcessing/Request.cs index 478a8a587db6..8e4babf7ca21 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/Request.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/Request.cs @@ -9,7 +9,6 @@ using System.Security.Cryptography.X509Certificates; using System.Security.Principal; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.Shared; using Microsoft.Extensions.Logging; @@ -370,9 +369,6 @@ private void GetTlsHandshakeResults() SniHostName = sni.Hostname.ToString(); } - internal bool GetAndInvokeTlsClientHelloCallback(IFeatureCollection features, Action> tlsClientHelloBytesCallback) - => RequestContext.GetAndInvokeTlsClientHelloMessageBytesCallback(features, tlsClientHelloBytesCallback); - public X509Certificate2? ClientCertificate { get diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs index 2751a1025a89..1c80f92febc2 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs @@ -36,6 +36,7 @@ internal partial class RequestContext : IHttpResponseTrailersFeature, IHttpResetFeature, IHttpSysRequestDelegationFeature, + IHttpSysRequestPropertyFeature, IConnectionLifetimeNotificationFeature { private IFeatureCollection? _features; @@ -753,4 +754,9 @@ void IConnectionLifetimeNotificationFeature.RequestClose() Response.Headers[HeaderNames.Connection] = "close"; } } + + public bool TryGetTlsClientHello(Span tlsClientHelloBytesDestination, out int bytesReturned) + { + return TryGetTlsClientHelloMessageBytes(tlsClientHelloBytesDestination, out bytesReturned); + } } diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index 9e34d23f8584..fef8263686ae 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -223,88 +223,56 @@ internal void ForceCancelRequest() /// /// Attempts to get the client hello message bytes from the http.sys. - /// If not successful, will return false. + /// If successful writes the bytes into , and shows how many bytes were written in . + /// If not successful because is not large enough, returns false and shows a size of required in . + /// If not successful for other reason - throws exception with message/errorCode. /// - internal unsafe bool GetAndInvokeTlsClientHelloMessageBytesCallback(IFeatureCollection features, Action> tlsClientHelloBytesCallback) + internal unsafe bool TryGetTlsClientHelloMessageBytes( + Span destination, + out int bytesReturned) { + bytesReturned = default; if (!HttpApi.SupportsClientHello) { // not supported, so we just return and don't invoke the callback - return false; + throw new InvalidOperationException("HttpApi does not support client hello. See HTTP_FEATURE_ID.HttpFeatureCacheTlsClientHello"); } - uint bytesReturnedValue = 0; - uint* bytesReturned = &bytesReturnedValue; uint statusCode; - var requestId = PinsReleased ? Request.RequestId : RequestId; - // we will try with some "random" buffer size - var buffer = ArrayPool.Shared.Rent(512); - try - { - fixed (byte* pBuffer = buffer) - { - statusCode = HttpApi.HttpGetRequestProperty( - requestQueueHandle: Server.RequestQueue.Handle, - requestId, - propertyId: (HTTP_REQUEST_PROPERTY)11 /* HTTP_REQUEST_PROPERTY.HttpRequestPropertyTlsClientHello */, - qualifier: null, - qualifierSize: 0, - output: pBuffer, - outputSize: (uint)buffer.Length, - bytesReturned: (IntPtr)bytesReturned, - overlapped: IntPtr.Zero); - - if (statusCode is ErrorCodes.ERROR_SUCCESS) - { - tlsClientHelloBytesCallback(features, buffer.AsSpan(0, (int)bytesReturnedValue)); - return true; - } - } - } - finally - { - ArrayPool.Shared.Return(buffer, clearArray: true); - } + uint bytesReturnedValue = 0; + uint* bytesReturnedPointer = &bytesReturnedValue; - // if buffer supplied is too small, `bytesReturned` will have proper size - // so retry should succeed with the properly allocated buffer - if (statusCode is ErrorCodes.ERROR_MORE_DATA or ErrorCodes.ERROR_INSUFFICIENT_BUFFER) + fixed (byte* pBuffer = destination) { - try - { - var correctSize = (int)bytesReturnedValue; - buffer = ArrayPool.Shared.Rent(correctSize); + statusCode = HttpApi.HttpGetRequestProperty( + requestQueueHandle: Server.RequestQueue.Handle, + requestId, + propertyId: (HTTP_REQUEST_PROPERTY)11 /* HTTP_REQUEST_PROPERTY.HttpRequestPropertyTlsClientHello */, + qualifier: null, + qualifierSize: 0, + output: pBuffer, + outputSize: (uint)destination.Length, + bytesReturned: (IntPtr)bytesReturnedPointer, + overlapped: IntPtr.Zero); - fixed (byte* pBuffer = buffer) - { - statusCode = HttpApi.HttpGetRequestProperty( - requestQueueHandle: Server.RequestQueue.Handle, - requestId, - propertyId: (HTTP_REQUEST_PROPERTY)11 /* HTTP_REQUEST_PROPERTY.HttpRequestPropertyTlsClientHello */, - qualifier: null, - qualifierSize: 0, - output: pBuffer, - outputSize: (uint)buffer.Length, - bytesReturned: (IntPtr)bytesReturned, - overlapped: IntPtr.Zero); - - if (statusCode is ErrorCodes.ERROR_SUCCESS) - { - tlsClientHelloBytesCallback(features, buffer.AsSpan(0, correctSize)); - return true; - } - } + bytesReturned = (int)bytesReturnedValue; + + if (statusCode is ErrorCodes.ERROR_SUCCESS) + { + return true; } - finally + + // if buffer supplied is too small, `bytesReturned` will have proper size + if (statusCode is ErrorCodes.ERROR_MORE_DATA or ErrorCodes.ERROR_INSUFFICIENT_BUFFER) { - ArrayPool.Shared.Return(buffer, clearArray: true); + return false; } } Log.TlsClientHelloRetrieveError(Logger, requestId, statusCode); - return false; + throw new HttpSysException((int)statusCode); } internal unsafe HTTP_REQUEST_PROPERTY_SNI GetClientSni() diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs index 399f1292d60d..2a1d06a06d26 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs @@ -48,12 +48,6 @@ public override async Task ExecuteAsync() context = application.CreateContext(Features); try { - if (Server.Options.TlsClientHelloBytesCallback is not null && Server.TlsListener is not null - && Request.IsHttps) - { - Server.TlsListener.InvokeTlsClientHelloCallback(Request.RawConnectionId, Features, Request.GetAndInvokeTlsClientHelloCallback); - } - await application.ProcessRequestAsync(context); await CompleteAsync(); } diff --git a/src/Servers/HttpSys/src/RequestProcessing/TlsListener.Log.cs b/src/Servers/HttpSys/src/RequestProcessing/TlsListener.Log.cs deleted file mode 100644 index 20ffe5c74b6f..000000000000 --- a/src/Servers/HttpSys/src/RequestProcessing/TlsListener.Log.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.HttpSys.RequestProcessing; - -internal sealed partial class TlsListener : IDisposable -{ - private static partial class Log - { - [LoggerMessage(LoggerEventIds.TlsListenerError, LogLevel.Error, "Error during closed connection cleanup.", EventName = "TlsListenerCleanupClosedConnectionError")] - public static partial void CleanupClosedConnectionError(ILogger logger, Exception exception); - } -} diff --git a/src/Servers/HttpSys/src/RequestProcessing/TlsListener.cs b/src/Servers/HttpSys/src/RequestProcessing/TlsListener.cs deleted file mode 100644 index 8e7edb9bb47d..000000000000 --- a/src/Servers/HttpSys/src/RequestProcessing/TlsListener.cs +++ /dev/null @@ -1,143 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.HttpSys.RequestProcessing; - -internal sealed partial class TlsListener : IDisposable -{ - private readonly ConcurrentDictionary _connectionTimestamps = new(); - private readonly Action> _tlsClientHelloBytesCallback; - private readonly ILogger _logger; - - private readonly PeriodicTimer _cleanupTimer; - private readonly Task _cleanupTask; - private readonly TimeProvider _timeProvider; - - private readonly TimeSpan ConnectionIdleTime = TimeSpan.FromMinutes(5); - private readonly TimeSpan CleanupDelay = TimeSpan.FromSeconds(10); - internal readonly int CacheSizeLimit = 1_000_000; - - // Internal for testing purposes - internal ReadOnlyDictionary ConnectionTimeStamps => _connectionTimestamps.AsReadOnly(); - - internal TlsListener(ILogger logger, Action> tlsClientHelloBytesCallback, TimeProvider? timeProvider = null) - { - if (AppContext.GetData("Microsoft.AspNetCore.Server.HttpSys.TlsListener.CacheSizeLimit") is int limit) - { - CacheSizeLimit = limit; - } - - if (AppContext.GetData("Microsoft.AspNetCore.Server.HttpSys.TlsListener.ConnectionIdleTime") is int idleTime) - { - ConnectionIdleTime = TimeSpan.FromSeconds(idleTime); - } - - if (AppContext.GetData("Microsoft.AspNetCore.Server.HttpSys.TlsListener.CleanupDelay") is int cleanupDelay) - { - CleanupDelay = TimeSpan.FromSeconds(cleanupDelay); - } - - _logger = logger; - _tlsClientHelloBytesCallback = tlsClientHelloBytesCallback; - - _timeProvider = timeProvider ?? TimeProvider.System; - _cleanupTimer = new PeriodicTimer(CleanupDelay, _timeProvider); - _cleanupTask = CleanupLoopAsync(); - } - - // Method looks weird because we want it to be testable by not directly requiring a Request object - internal void InvokeTlsClientHelloCallback(ulong connectionId, IFeatureCollection features, - Func>, bool> invokeTlsClientHelloCallback) - { - if (!_connectionTimestamps.TryAdd(connectionId, _timeProvider.GetUtcNow())) - { - // update TTL - _connectionTimestamps[connectionId] = _timeProvider.GetUtcNow(); - return; - } - - _ = invokeTlsClientHelloCallback(features, _tlsClientHelloBytesCallback); - } - - internal async Task CleanupLoopAsync() - { - while (await _cleanupTimer.WaitForNextTickAsync()) - { - try - { - var now = _timeProvider.GetUtcNow(); - - // Remove idle connections - foreach (var kvp in _connectionTimestamps) - { - if (now - kvp.Value >= ConnectionIdleTime) - { - _connectionTimestamps.TryRemove(kvp.Key, out _); - } - } - - // Evict oldest items if above CacheSizeLimit - var currentCount = _connectionTimestamps.Count; - if (currentCount > CacheSizeLimit) - { - var excessCount = currentCount - CacheSizeLimit; - - // Find the oldest items in a single pass - var oldestTimestamps = new SortedSet>(TimeComparer.Instance); - - foreach (var kvp in _connectionTimestamps) - { - if (oldestTimestamps.Count < excessCount) - { - oldestTimestamps.Add(new KeyValuePair(kvp.Key, kvp.Value)); - } - else if (kvp.Value < oldestTimestamps.Max.Value) - { - oldestTimestamps.Remove(oldestTimestamps.Max); - oldestTimestamps.Add(new KeyValuePair(kvp.Key, kvp.Value)); - } - } - - // Remove the oldest keys - foreach (var item in oldestTimestamps) - { - _connectionTimestamps.TryRemove(item.Key, out _); - } - } - } - catch (Exception ex) - { - Log.CleanupClosedConnectionError(_logger, ex); - } - } - } - - public void Dispose() - { - _cleanupTimer.Dispose(); - _cleanupTask.Wait(); - } - - private sealed class TimeComparer : IComparer> - { - public static TimeComparer Instance { get; } = new TimeComparer(); - - public int Compare(KeyValuePair x, KeyValuePair y) - { - // Compare timestamps first - int timestampComparison = x.Value.CompareTo(y.Value); - if (timestampComparison != 0) - { - return timestampComparison; - } - - // Use the key as a tiebreaker to ensure uniqueness - return x.Key.CompareTo(y.Key); - } - } -} diff --git a/src/Servers/HttpSys/src/StandardFeatureCollection.cs b/src/Servers/HttpSys/src/StandardFeatureCollection.cs index dda57166921e..1c7d078d8253 100644 --- a/src/Servers/HttpSys/src/StandardFeatureCollection.cs +++ b/src/Servers/HttpSys/src/StandardFeatureCollection.cs @@ -27,6 +27,7 @@ internal sealed class StandardFeatureCollection : IFeatureCollection { typeof(IHttpBodyControlFeature), _identityFunc }, { typeof(IHttpSysRequestInfoFeature), _identityFunc }, { typeof(IHttpSysRequestTimingFeature), _identityFunc }, + { typeof(IHttpSysRequestPropertyFeature), _identityFunc }, { typeof(IHttpResponseTrailersFeature), ctx => ctx.GetResponseTrailersFeature() }, { typeof(IHttpResetFeature), ctx => ctx.GetResetFeature() }, { typeof(IConnectionLifetimeNotificationFeature), ctx => ctx.GetConnectionLifetimeNotificationFeature() }, From 7b11c44e7050d43a03e0cff35c808b638f678309 Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Mon, 2 Jun 2025 15:07:56 +0200 Subject: [PATCH 2/6] fix build, remove tests for callback (no way to test current behavior?) --- .../src/RequestProcessing/RequestContext.cs | 4 +- .../test/FunctionalTests/TlsListenerTests.cs | 140 ------------------ 2 files changed, 1 insertion(+), 143 deletions(-) delete mode 100644 src/Servers/HttpSys/test/FunctionalTests/TlsListenerTests.cs diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index fef8263686ae..488b3d3b31b0 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -1,11 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.Runtime.InteropServices; using System.Security.Principal; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; @@ -35,7 +33,7 @@ public RequestContext(HttpSysListener server, uint? bufferSize, ulong requestId) public Request Request { get; private set; } = default!; - public Response Response { get; private set; } = default!; + public Response Response { get; private set; } = default!; public WindowsPrincipal User => Request.User; diff --git a/src/Servers/HttpSys/test/FunctionalTests/TlsListenerTests.cs b/src/Servers/HttpSys/test/FunctionalTests/TlsListenerTests.cs deleted file mode 100644 index d0ff2731a017..000000000000 --- a/src/Servers/HttpSys/test/FunctionalTests/TlsListenerTests.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.HttpSys.RequestProcessing; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Time.Testing; -using Moq; - -namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests; - -public class TlsListenerTests -{ - [Fact] - public void AddsAndUpdatesConnectionTimestamps() - { - // Arrange - var logger = Mock.Of(); - var timeProvider = new FakeTimeProvider(); - var callbackInvoked = false; - var tlsListener = new TlsListener(logger, (_, __) => { callbackInvoked = true; }, timeProvider); - - var features = Mock.Of(); - - // Act - tlsListener.InvokeTlsClientHelloCallback(connectionId: 1UL, features, - invokeTlsClientHelloCallback: (f, cb) => { cb(f, ReadOnlySpan.Empty); return true; }); - - var originalTime = timeProvider.GetUtcNow(); - - // Assert - Assert.True(callbackInvoked); - Assert.Equal(originalTime, Assert.Single(tlsListener.ConnectionTimeStamps).Value); - - timeProvider.Advance(TimeSpan.FromSeconds(1)); - callbackInvoked = false; - // Update the timestamp - tlsListener.InvokeTlsClientHelloCallback(connectionId: 1UL, features, - invokeTlsClientHelloCallback: (f, cb) => { cb(f, ReadOnlySpan.Empty); return true; }); - - // Callback should not be invoked again and the timestamp should be updated - Assert.False(callbackInvoked); - Assert.Equal(timeProvider.GetUtcNow(), Assert.Single(tlsListener.ConnectionTimeStamps).Value); - Assert.NotEqual(originalTime, timeProvider.GetUtcNow()); - } - - [Fact] - public async Task RemovesIdleConnections() - { - // Arrange - var logger = Mock.Of(); - var timeProvider = new FakeTimeProvider(); - using var tlsListener = new TlsListener(logger, (_, __) => { }, timeProvider); - - var features = Mock.Of(); - - bool InvokeCallback(IFeatureCollection f, Action> cb) - { - cb(f, ReadOnlySpan.Empty); - return true; - } - - // Act - tlsListener.InvokeTlsClientHelloCallback(connectionId: 1UL, features, InvokeCallback); - - // 1 less minute than the idle time cleanup - timeProvider.Advance(TimeSpan.FromMinutes(4)); - Assert.Single(tlsListener.ConnectionTimeStamps); - - tlsListener.InvokeTlsClientHelloCallback(connectionId: 2UL, features, InvokeCallback); - Assert.Equal(2, tlsListener.ConnectionTimeStamps.Count); - - // With the previous 4 minutes, this should be 5 minutes and remove the first connection - timeProvider.Advance(TimeSpan.FromMinutes(1)); - - var timeout = TimeSpan.FromSeconds(5); - while (timeout > TimeSpan.Zero) - { - // Wait for the cleanup loop to run - if (tlsListener.ConnectionTimeStamps.Count == 1) - { - break; - } - timeout -= TimeSpan.FromMilliseconds(100); - await Task.Delay(100); - } - - // Assert - Assert.Single(tlsListener.ConnectionTimeStamps); - Assert.Contains(2UL, tlsListener.ConnectionTimeStamps.Keys); - } - - [Fact] - public async Task EvictsOldestConnectionsWhenExceedingCacheSizeLimit() - { - // Arrange - var logger = Mock.Of(); - var timeProvider = new FakeTimeProvider(); - var tlsListener = new TlsListener(logger, (_, __) => { }, timeProvider); - var features = Mock.Of(); - - ulong i = 0; - for (; i < (ulong)tlsListener.CacheSizeLimit; i++) - { - tlsListener.InvokeTlsClientHelloCallback(i, features, (f, cb) => { cb(f, ReadOnlySpan.Empty); return true; }); - } - - timeProvider.Advance(TimeSpan.FromSeconds(5)); - - for (; i < (ulong)tlsListener.CacheSizeLimit + 3; i++) - { - tlsListener.InvokeTlsClientHelloCallback(i, features, (f, cb) => { cb(f, ReadOnlySpan.Empty); return true; }); - } - - // 'touch' first connection to update its timestamp - tlsListener.InvokeTlsClientHelloCallback(0, features, (f, cb) => { cb(f, ReadOnlySpan.Empty); return true; }); - - // Make sure the cleanup loop has run to evict items since we're above the cache size limit - timeProvider.Advance(TimeSpan.FromMinutes(1)); - - var timeout = TimeSpan.FromSeconds(5); - while (timeout > TimeSpan.Zero) - { - // Wait for the cleanup loop to run - if (tlsListener.ConnectionTimeStamps.Count == tlsListener.CacheSizeLimit) - { - break; - } - timeout -= TimeSpan.FromMilliseconds(100); - await Task.Delay(100); - } - - Assert.Equal(tlsListener.CacheSizeLimit, tlsListener.ConnectionTimeStamps.Count); - Assert.Contains(0UL, tlsListener.ConnectionTimeStamps.Keys); - // 3 newest connections should be present - Assert.Contains(i - 1, tlsListener.ConnectionTimeStamps.Keys); - Assert.Contains(i - 2, tlsListener.ConnectionTimeStamps.Keys); - Assert.Contains(i - 3, tlsListener.ConnectionTimeStamps.Keys); - } -} From b2787d5fbaeda5bb2c8056776dba6f71c70c3dd7 Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Tue, 3 Jun 2025 13:57:16 +0200 Subject: [PATCH 3/6] change message --- src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index 488b3d3b31b0..aa02ef007048 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -233,7 +233,7 @@ internal unsafe bool TryGetTlsClientHelloMessageBytes( if (!HttpApi.SupportsClientHello) { // not supported, so we just return and don't invoke the callback - throw new InvalidOperationException("HttpApi does not support client hello. See HTTP_FEATURE_ID.HttpFeatureCacheTlsClientHello"); + throw new InvalidOperationException("Windows HTTP Server API does not support HTTP_FEATURE_ID.HttpFeatureCacheTlsClientHello or HttpQueryRequestProperty. See HTTP_FEATURE_ID for details."); } uint statusCode; From 60f23f0b90a2eaf62b24be98b5362c6ca0f5aa57 Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Wed, 4 Jun 2025 14:42:35 +0200 Subject: [PATCH 4/6] PR comments & smoke tests --- .../Http.Features/src/PublicAPI.Unshipped.txt | 2 -- .../src/IHttpSysRequestPropertyFeature.cs | 14 +++++++------ .../HttpSys/src/PublicAPI.Unshipped.txt | 2 ++ .../src/RequestProcessing/RequestContext.cs | 2 +- .../test/FunctionalTests/HttpsTests.cs | 21 +++++++++++++++++++ .../Core/src/ListenOptionsHttpsExtensions.cs | 2 +- 6 files changed, 33 insertions(+), 10 deletions(-) rename src/{Http/Http.Features => Servers/HttpSys}/src/IHttpSysRequestPropertyFeature.cs (62%) diff --git a/src/Http/Http.Features/src/PublicAPI.Unshipped.txt b/src/Http/Http.Features/src/PublicAPI.Unshipped.txt index 2a8d76ffbce5..7dc5c58110bf 100644 --- a/src/Http/Http.Features/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Features/src/PublicAPI.Unshipped.txt @@ -1,3 +1 @@ #nullable enable -Microsoft.AspNetCore.Http.Features.IHttpSysRequestPropertyFeature -Microsoft.AspNetCore.Http.Features.IHttpSysRequestPropertyFeature.TryGetTlsClientHello(System.Span tlsClientHelloBytesDestination, out int bytesReturned) -> bool diff --git a/src/Http/Http.Features/src/IHttpSysRequestPropertyFeature.cs b/src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs similarity index 62% rename from src/Http/Http.Features/src/IHttpSysRequestPropertyFeature.cs rename to src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs index 5ec810b61913..8f173d789664 100644 --- a/src/Http/Http.Features/src/IHttpSysRequestPropertyFeature.cs +++ b/src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.AspNetCore.Http.Features; +namespace Microsoft.AspNetCore.Server.HttpSys; /// /// Provides API to read HTTP_REQUEST_PROPERTY value from the HTTP.SYS request. @@ -12,23 +12,25 @@ public interface IHttpSysRequestPropertyFeature /// /// Reads the TLS client hello from HTTP.SYS /// - /// where raw bytes of tls client hello message will be written + /// Where the raw bytes of the TLS Client Hello message will be written. /// /// Returns the number of bytes written to . - /// Or can return the size of buffer needed if wasn't large enough. + /// Or can return the size of the buffer needed if wasn't large enough. /// /// /// Works only if HTTP_SERVICE_CONFIG_SSL_FLAG_ENABLE_CACHE_CLIENT_HELLO flag is set on http.sys service configuration. /// See /// and ///

- /// If you don't want to guess required size before first invocation, - /// you should call first with set to empty size, so that you can retrieve through the buffer size you need, - /// then allocate that amount of memory, then retry the query. + /// If you don't want to guess the required size before first invocation, + /// you should first call with set to empty size, so that you can retrieve the required buffer size from , + /// then allocate that amount of memory and retry the query. ///
/// /// True, if fetching TLS client hello was successful, false if size is not large enough. /// If non-successful for other reason throws an exception. /// + /// Any HttpSys error except for ERROR_INSUFFICIENT_BUFFER or ERROR_MORE_DATA. + /// If HttpSys does not support querying the TLS Client Hello. bool TryGetTlsClientHello(Span tlsClientHelloBytesDestination, out int bytesReturned); } diff --git a/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt b/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt index 091f7f846729..20bc39698c15 100644 --- a/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt +++ b/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt @@ -1,3 +1,5 @@ #nullable enable Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.RequestQueueSecurityDescriptor.get -> System.Security.AccessControl.GenericSecurityDescriptor? Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.RequestQueueSecurityDescriptor.set -> void +Microsoft.AspNetCore.Server.HttpSys.IHttpSysRequestPropertyFeature +Microsoft.AspNetCore.Server.HttpSys.IHttpSysRequestPropertyFeature.TryGetTlsClientHello(System.Span tlsClientHelloBytesDestination, out int bytesReturned) -> bool diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index aa02ef007048..e0957c6a2132 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -33,7 +33,7 @@ public RequestContext(HttpSysListener server, uint? bufferSize, ulong requestId) public Request Request { get; private set; } = default!; - public Response Response { get; private set; } = default!; + public Response Response { get; private set; } = default!; public WindowsPrincipal User => Request.User; diff --git a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs index 0d3610cc4cfb..ae629dc23131 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs @@ -239,6 +239,27 @@ public async Task Https_ITlsHandshakeFeature_MatchesIHttpSysExtensionInfoFeature } } + [ConditionalFact] + [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H2)] + public void Https_SetsIHttpSysRequestPropertyFeature() + { + using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext => + { + try + { + var requestPropertyFeature = httpContext.Features.Get(); + Assert.NotNull(requestPropertyFeature); + } + catch (Exception ex) + { + await httpContext.Response.WriteAsync(ex.ToString()); + } + }, LoggerFactory)) + { + // nothing + } + } + [ConditionalFact] [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H2)] public async Task Https_SetsIHttpSysRequestTimingFeature() diff --git a/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs b/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs index 42d7ac8f0476..da294f05dbef 100644 --- a/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs +++ b/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs @@ -188,7 +188,7 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, Action. ///
/// The to configure. - /// Options to configure HTTPS. + /// Options to configure HTTPS. /// The . public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions) { From 65fbefe3e0899fd5ecf4a8fa0b8768edcd07889a Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Thu, 5 Jun 2025 08:20:21 +0200 Subject: [PATCH 5/6] pr review --- src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs | 2 +- src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs | 5 +++-- src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index e0957c6a2132..f3721433a292 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -255,7 +255,7 @@ internal unsafe bool TryGetTlsClientHelloMessageBytes( bytesReturned: (IntPtr)bytesReturnedPointer, overlapped: IntPtr.Zero); - bytesReturned = (int)bytesReturnedValue; + bytesReturned = checked((int)bytesReturnedValue); if (statusCode is ErrorCodes.ERROR_SUCCESS) { diff --git a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs index ae629dc23131..9b2b4134c681 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs @@ -241,7 +241,7 @@ public async Task Https_ITlsHandshakeFeature_MatchesIHttpSysExtensionInfoFeature [ConditionalFact] [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H2)] - public void Https_SetsIHttpSysRequestPropertyFeature() + public async Task Https_SetsIHttpSysRequestPropertyFeature() { using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext => { @@ -256,7 +256,8 @@ public void Https_SetsIHttpSysRequestPropertyFeature() } }, LoggerFactory)) { - // nothing + string response = await SendRequestAsync(address); + Assert.Equal(string.Empty, response); } } diff --git a/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs b/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs index da294f05dbef..42d7ac8f0476 100644 --- a/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs +++ b/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs @@ -188,7 +188,7 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, Action. ///
/// The to configure. - /// Options to configure HTTPS. + /// Options to configure HTTPS. /// The . public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions) { From 546cb71c4498b1ba4468c806401d3cd987562e3b Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Fri, 6 Jun 2025 12:52:41 +0200 Subject: [PATCH 6/6] doc comments --- src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs | 4 ++-- src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs b/src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs index 8f173d789664..392e11cb73e6 100644 --- a/src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs +++ b/src/Servers/HttpSys/src/IHttpSysRequestPropertyFeature.cs @@ -12,7 +12,7 @@ public interface IHttpSysRequestPropertyFeature /// /// Reads the TLS client hello from HTTP.SYS /// - /// Where the raw bytes of the TLS Client Hello message will be written. + /// Where the raw bytes of the TLS Client Hello message are written. /// /// Returns the number of bytes written to . /// Or can return the size of the buffer needed if wasn't large enough. @@ -28,7 +28,7 @@ public interface IHttpSysRequestPropertyFeature /// /// /// True, if fetching TLS client hello was successful, false if size is not large enough. - /// If non-successful for other reason throws an exception. + /// If unsuccessful for other reason throws an exception. /// /// Any HttpSys error except for ERROR_INSUFFICIENT_BUFFER or ERROR_MORE_DATA. /// If HttpSys does not support querying the TLS Client Hello. diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index f3721433a292..eba7d33ff3b8 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -262,7 +262,7 @@ internal unsafe bool TryGetTlsClientHelloMessageBytes( return true; } - // if buffer supplied is too small, `bytesReturned` will have proper size + // if buffer supplied is too small, `bytesReturned` has proper size if (statusCode is ErrorCodes.ERROR_MORE_DATA or ErrorCodes.ERROR_INSUFFICIENT_BUFFER) { return false;