| File: UserCallbacks\ResolveTlsContextCallback.cs | Web Access |
| Project: src\aspnetcore\src\Servers\Kestrel\Transport.DirectTls\src\Microsoft.AspNetCore.Server.Kestrel.Transport.DirectTls.csproj (Microsoft.AspNetCore.Server.Kestrel.Transport.DirectTls) |
// 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.Net.Security; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Transport.DirectTls.Connection; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.DirectTls.UserCallbacks; /// <summary> /// The <c>NeedsTlsContext</c> suspension: the optional ClientHello listener followed by the server-certificate /// selector. /// </summary> internal sealed class ResolveTlsContextCallback : HandshakeUserCallback { private readonly Func<ConnectionContext?, string?, (TlsContext Context, RemoteCertificateValidationCallback? ClientCertificateValidation)> _contextResolver; private readonly Action<ConnectionContext, ReadOnlySequence<byte>>? _clientHelloCallback; private readonly string? _targetHostName; // ClientHello record copied out of the session on the pump thread. Rented from the shared pool and // returned as soon as the (synchronous) user callback returns, matching the transient-buffer contract of // the socket-transport TlsListener. private byte[]? _clientHelloBuffer; private readonly int _clientHelloLength; /// <summary> /// Creates the work item for the <c>NeedsTlsContext</c> suspension. <paramref name="clientHelloBuffer"/> /// holds the ClientHello record already copied out of the session on the pump thread (null when there is /// no listener, or nothing was captured); this work item returns it to <see cref="ArrayPool{T}.Shared"/> /// once the listener has run. /// </summary> public ResolveTlsContextCallback( TlsEventPump pump, int fd, DirectTlsConnection? connection, string? targetHostName, Func<ConnectionContext?, string?, (TlsContext Context, RemoteCertificateValidationCallback? ClientCertificateValidation)> contextResolver, Action<ConnectionContext, ReadOnlySequence<byte>>? clientHelloCallback, byte[]? clientHelloBuffer, int clientHelloLength) : base(pump, fd, connection) { _targetHostName = targetHostName; _contextResolver = contextResolver; _clientHelloCallback = clientHelloCallback; _clientHelloBuffer = clientHelloBuffer; _clientHelloLength = clientHelloLength; } /// <summary>The TLS context the certificate selector resolved.</summary> public TlsContext? ResolvedContext { get; private set; } /// <summary>The client-certificate validation callback that came back with the resolved context.</summary> public RemoteCertificateValidationCallback? ResolvedClientCertificateValidation { get; private set; } /// <inheritdoc /> protected override void RunUserCode() { // Fire the observe-only ClientHello listener first: the listener sees the ClientHello before the real context is installed. if (_clientHelloCallback is not null && Connection is not null && _clientHelloBuffer is not null && _clientHelloLength > 0) { _clientHelloCallback(Connection, new ReadOnlySequence<byte>(_clientHelloBuffer, 0, _clientHelloLength)); } var (context, clientCertificateValidation) = _contextResolver(Connection, _targetHostName); ResolvedContext = context; ResolvedClientCertificateValidation = clientCertificateValidation; } /// <inheritdoc /> protected override void ReleaseTransientState() { if (_clientHelloBuffer is { } buffer) { _clientHelloBuffer = null; ArrayPool<byte>.Shared.Return(buffer); } } }