- 
                Notifications
    You must be signed in to change notification settings 
- Fork 10.5k
Make TLS & QUIC Pay-for-Play (Redux) #47454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Merged
      
      
    
  
     Merged
                    Changes from 8 commits
      Commits
    
    
            Show all changes
          
          
            9 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      7843321
              
                Make TLS & QUIC pay-for-play
              
              
                amcasey 7aea16b
              
                Improve assert message
              
              
                amcasey 7b94e52
              
                Adopt MemberNotNullAttribute
              
              
                amcasey a269b08
              
                Assert that members are non-null to suppress CS8774
              
              
                amcasey 367d052
              
                UseHttpsConfiguration -> UseKestrelHttpsConfiguration
              
              
                amcasey 7696f09
              
                UseKestrelSlim -> UseKestrelCore
              
              
                amcasey d798e05
              
                Drop convenience overloads of UseKestrelCore
              
              
                amcasey 6236efd
              
                Use more explicit error strings in HttpsConfigurationService.EnsureIn…
              
              
                amcasey 7d44b2b
              
                Update src/Servers/Kestrel/Core/test/KestrelServerTests.cs
              
              
                JamesNK File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
        
          
  
    
      
          
            260 changes: 260 additions & 0 deletions
          
          260 
        
  src/Servers/Kestrel/Core/src/HttpsConfigurationService.cs
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|  | ||
| using System.Diagnostics; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.IO.Pipelines; | ||
| using System.Net; | ||
| using System.Net.Security; | ||
| using Microsoft.AspNetCore.Connections; | ||
| using Microsoft.AspNetCore.Hosting; | ||
| using Microsoft.AspNetCore.Http.Features; | ||
| using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; | ||
| using Microsoft.AspNetCore.Server.Kestrel.Https; | ||
| using Microsoft.AspNetCore.Server.Kestrel.Https.Internal; | ||
| using Microsoft.Extensions.Hosting; | ||
| using Microsoft.Extensions.Logging; | ||
|  | ||
| namespace Microsoft.AspNetCore.Server.Kestrel.Core; | ||
|  | ||
| /// <inheritdoc /> | ||
| internal sealed class HttpsConfigurationService : IHttpsConfigurationService | ||
| { | ||
| private readonly IInitializer? _initializer; | ||
| private bool _isInitialized; | ||
|  | ||
| private TlsConfigurationLoader? _tlsConfigurationLoader; | ||
| private Action<FeatureCollection, ListenOptions>? _populateMultiplexedTransportFeatures; | ||
| private Func<ListenOptions, ListenOptions>? _useHttpsWithDefaults; | ||
|  | ||
| /// <summary> | ||
| /// Create an uninitialized <see cref="HttpsConfigurationService"/>. | ||
| /// To initialize it later, call <see cref="Initialize"/>. | ||
| /// </summary> | ||
| public HttpsConfigurationService() | ||
| { | ||
| } | ||
|  | ||
| /// <summary> | ||
| /// Create an initialized <see cref="HttpsConfigurationService"/>. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// In practice, <see cref="Initialize"/> won't be called until it's needed. | ||
| /// </remarks> | ||
| public HttpsConfigurationService(IInitializer initializer) | ||
| { | ||
| _initializer = initializer; | ||
| } | ||
|  | ||
| /// <inheritdoc /> | ||
| // If there's an initializer, it *can* be initialized, even though it might not be yet. | ||
| // Use explicit interface implentation so we don't accidentally call it within this class. | ||
| bool IHttpsConfigurationService.IsInitialized => _isInitialized || _initializer is not null; | ||
|  | ||
| /// <inheritdoc/> | ||
| public void Initialize( | ||
| IHostEnvironment hostEnvironment, | ||
| ILogger<KestrelServer> serverLogger, | ||
| ILogger<HttpsConnectionMiddleware> httpsLogger) | ||
| { | ||
| if (_isInitialized) | ||
| { | ||
| return; | ||
| } | ||
|  | ||
| _isInitialized = true; | ||
|  | ||
| _tlsConfigurationLoader = new TlsConfigurationLoader(hostEnvironment, serverLogger, httpsLogger); | ||
| _populateMultiplexedTransportFeatures = PopulateMultiplexedTransportFeaturesWorker; | ||
| _useHttpsWithDefaults = UseHttpsWithDefaultsWorker; | ||
| } | ||
|  | ||
| /// <inheritdoc/> | ||
| public void ApplyHttpsConfiguration( | ||
| HttpsConnectionAdapterOptions httpsOptions, | ||
| EndpointConfig endpoint, | ||
| KestrelServerOptions serverOptions, | ||
| CertificateConfig? defaultCertificateConfig, | ||
| ConfigurationReader configurationReader) | ||
| { | ||
| EnsureInitialized(CoreStrings.NeedHttpsConfigurationToApplyHttpsConfiguration); | ||
| _tlsConfigurationLoader.ApplyHttpsConfiguration(httpsOptions, endpoint, serverOptions, defaultCertificateConfig, configurationReader); | ||
| } | ||
|  | ||
| /// <inheritdoc/> | ||
| public ListenOptions UseHttpsWithSni(ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions, EndpointConfig endpoint) | ||
| { | ||
| // This doesn't get a distinct string since it won't actually throw - it's always called after ApplyHttpsConfiguration | ||
| EnsureInitialized(CoreStrings.NeedHttpsConfigurationToApplyHttpsConfiguration); | ||
| return _tlsConfigurationLoader.UseHttpsWithSni(listenOptions, httpsOptions, endpoint); | ||
| } | ||
|  | ||
| /// <inheritdoc/> | ||
| public CertificateAndConfig? LoadDefaultCertificate(ConfigurationReader configurationReader) | ||
| { | ||
| EnsureInitialized(CoreStrings.NeedHttpsConfigurationToLoadDefaultCertificate); | ||
| return _tlsConfigurationLoader.LoadDefaultCertificate(configurationReader); | ||
| } | ||
|  | ||
| /// <inheritdoc/> | ||
| public void PopulateMultiplexedTransportFeatures(FeatureCollection features, ListenOptions listenOptions) | ||
| { | ||
| EnsureInitialized(CoreStrings.NeedHttpsConfigurationToUseHttp3); | ||
| _populateMultiplexedTransportFeatures.Invoke(features, listenOptions); | ||
| } | ||
|  | ||
| /// <inheritdoc/> | ||
| public ListenOptions UseHttpsWithDefaults(ListenOptions listenOptions) | ||
| { | ||
| EnsureInitialized(CoreStrings.NeedHttpsConfigurationToBindHttpsAddresses); | ||
| return _useHttpsWithDefaults.Invoke(listenOptions); | ||
| } | ||
|  | ||
| /// <summary> | ||
| /// If this instance has not been initialized, initialize it if possible and throw otherwise. | ||
| /// </summary> | ||
| /// <exception cref="InvalidOperationException">If initialization is not possible.</exception> | ||
| [MemberNotNull(nameof(_useHttpsWithDefaults), nameof(_tlsConfigurationLoader), nameof(_populateMultiplexedTransportFeatures))] | ||
| private void EnsureInitialized(string uninitializedError) | ||
| { | ||
| if (!_isInitialized) | ||
| { | ||
| if (_initializer is not null) | ||
| { | ||
| _initializer.Initialize(this); | ||
| } | ||
| else | ||
| { | ||
| throw new InvalidOperationException(uninitializedError); | ||
| } | ||
| } | ||
|  | ||
| Debug.Assert(_useHttpsWithDefaults is not null); | ||
| Debug.Assert(_tlsConfigurationLoader is not null); | ||
| Debug.Assert(_populateMultiplexedTransportFeatures is not null); | ||
| } | ||
|  | ||
| /// <summary> | ||
| /// The initialized implementation of <see cref="PopulateMultiplexedTransportFeatures"/>. | ||
| /// </summary> | ||
| internal static void PopulateMultiplexedTransportFeaturesWorker(FeatureCollection features, ListenOptions listenOptions) | ||
| { | ||
| // HttpsOptions or HttpsCallbackOptions should always be set in production, but it's not set for InMemory tests. | ||
| // The QUIC transport will check if TlsConnectionCallbackOptions is missing. | ||
| if (listenOptions.HttpsOptions != null) | ||
| { | ||
| var sslServerAuthenticationOptions = HttpsConnectionMiddleware.CreateHttp3Options(listenOptions.HttpsOptions); | ||
| features.Set(new TlsConnectionCallbackOptions | ||
| { | ||
| ApplicationProtocols = sslServerAuthenticationOptions.ApplicationProtocols ?? new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 }, | ||
| OnConnection = (context, cancellationToken) => ValueTask.FromResult(sslServerAuthenticationOptions), | ||
| OnConnectionState = null, | ||
| }); | ||
| } | ||
| else if (listenOptions.HttpsCallbackOptions != null) | ||
| { | ||
| features.Set(new TlsConnectionCallbackOptions | ||
| { | ||
| ApplicationProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http3 }, | ||
| OnConnection = (context, cancellationToken) => | ||
| { | ||
| return listenOptions.HttpsCallbackOptions.OnConnection(new TlsHandshakeCallbackContext | ||
| { | ||
| ClientHelloInfo = context.ClientHelloInfo, | ||
| CancellationToken = cancellationToken, | ||
| State = context.State, | ||
| Connection = new ConnectionContextAdapter(context.Connection), | ||
| }); | ||
| }, | ||
| OnConnectionState = listenOptions.HttpsCallbackOptions.OnConnectionState, | ||
| }); | ||
| } | ||
| } | ||
|  | ||
| /// <summary> | ||
| /// The initialized implementation of <see cref="UseHttpsWithDefaults"/>. | ||
| /// </summary> | ||
| internal static ListenOptions UseHttpsWithDefaultsWorker(ListenOptions listenOptions) | ||
| { | ||
| return listenOptions.UseHttps(); | ||
| } | ||
|  | ||
| /// <summary> | ||
| /// TlsHandshakeCallbackContext.Connection is ConnectionContext but QUIC connection only implements BaseConnectionContext. | ||
| /// </summary> | ||
| private sealed class ConnectionContextAdapter : ConnectionContext | ||
| { | ||
| private readonly BaseConnectionContext _inner; | ||
|  | ||
| public ConnectionContextAdapter(BaseConnectionContext inner) => _inner = inner; | ||
|  | ||
| public override IDuplexPipe Transport | ||
| { | ||
| get => throw new NotSupportedException("Not supported by HTTP/3 connections."); | ||
| set => throw new NotSupportedException("Not supported by HTTP/3 connections."); | ||
| } | ||
| public override string ConnectionId | ||
| { | ||
| get => _inner.ConnectionId; | ||
| set => _inner.ConnectionId = value; | ||
| } | ||
| public override IFeatureCollection Features => _inner.Features; | ||
| public override IDictionary<object, object?> Items | ||
| { | ||
| get => _inner.Items; | ||
| set => _inner.Items = value; | ||
| } | ||
| public override EndPoint? LocalEndPoint | ||
| { | ||
| get => _inner.LocalEndPoint; | ||
| set => _inner.LocalEndPoint = value; | ||
| } | ||
| public override EndPoint? RemoteEndPoint | ||
| { | ||
| get => _inner.RemoteEndPoint; | ||
| set => _inner.RemoteEndPoint = value; | ||
| } | ||
| public override CancellationToken ConnectionClosed | ||
| { | ||
| get => _inner.ConnectionClosed; | ||
| set => _inner.ConnectionClosed = value; | ||
| } | ||
| public override ValueTask DisposeAsync() => _inner.DisposeAsync(); | ||
| } | ||
|  | ||
| /// <summary> | ||
| /// Register an instance of this type to initialize registered instances of <see cref="HttpsConfigurationService"/>. | ||
| /// </summary> | ||
| internal interface IInitializer | ||
| { | ||
| /// <summary> | ||
| /// Invokes <see cref="IHttpsConfigurationService.Initialize"/>, passing appropriate arguments. | ||
| /// </summary> | ||
| void Initialize(IHttpsConfigurationService httpsConfigurationService); | ||
| } | ||
|  | ||
| /// <inheritdoc/> | ||
| internal sealed class Initializer : IInitializer | ||
| { | ||
| private readonly IHostEnvironment _hostEnvironment; | ||
| private readonly ILogger<KestrelServer> _serverLogger; | ||
| private readonly ILogger<HttpsConnectionMiddleware> _httpsLogger; | ||
|  | ||
| public Initializer( | ||
| IHostEnvironment hostEnvironment, | ||
| ILogger<KestrelServer> serverLogger, | ||
| ILogger<HttpsConnectionMiddleware> httpsLogger) | ||
| { | ||
| _hostEnvironment = hostEnvironment; | ||
| _serverLogger = serverLogger; | ||
| _httpsLogger = httpsLogger; | ||
| } | ||
|  | ||
| /// <inheritdoc/> | ||
| public void Initialize(IHttpsConfigurationService httpsConfigurationService) | ||
| { | ||
| httpsConfigurationService.Initialize(_hostEnvironment, _serverLogger, _httpsLogger); | ||
| } | ||
| } | ||
| } | ||
|  | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.