Browse Source

Enable nullable in Kestrel.Core (#28050)

James Newton-King 5 years ago
parent
commit
6ea0926914
100 changed files with 792 additions and 609 deletions
  1. 2 2
      src/Hosting/Hosting/src/Internal/HostingApplication.cs
  2. 1 1
      src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs
  3. 1 1
      src/Hosting/Server.Abstractions/src/IHostContextContainer.cs
  4. 1 1
      src/Hosting/Server.Abstractions/src/IHttpApplication.cs
  5. 2 2
      src/Hosting/Server.Abstractions/src/PublicAPI.Shipped.txt
  6. 1 1
      src/Http/Http.Features/src/IHttpResponseTrailersFeature.cs
  7. 2 1
      src/Http/Http.Features/src/PublicAPI.Shipped.txt
  8. 0 2
      src/Http/Http.Features/src/PublicAPI.Unshipped.txt
  9. 46 25
      src/Http/Http/src/BindingAddress.cs
  10. 3 0
      src/Servers/Kestrel/Core/src/AnyIPListenOptions.cs
  11. 4 4
      src/Servers/Kestrel/Core/src/CertificateLoader.cs
  12. 1 1
      src/Servers/Kestrel/Core/src/Features/IHttpMinRequestBodyDataRateFeature.cs
  13. 2 2
      src/Servers/Kestrel/Core/src/Features/IHttpMinResponseDataRateFeature.cs
  14. 4 4
      src/Servers/Kestrel/Core/src/HttpsConnectionAdapterOptions.cs
  15. 16 4
      src/Servers/Kestrel/Core/src/Internal/AddressBindContext.cs
  16. 6 5
      src/Servers/Kestrel/Core/src/Internal/AddressBinder.cs
  17. 9 9
      src/Servers/Kestrel/Core/src/Internal/Certificates/CertificateConfigLoader.cs
  18. 1 1
      src/Servers/Kestrel/Core/src/Internal/Certificates/ICertificateConfigLoader.cs
  19. 50 45
      src/Servers/Kestrel/Core/src/Internal/ConfigurationReader.cs
  20. 2 2
      src/Servers/Kestrel/Core/src/Internal/ConnectionLogScope.cs
  21. 11 9
      src/Servers/Kestrel/Core/src/Internal/Http/DateHeaderValueManager.cs
  22. 7 5
      src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs
  23. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.FeatureCollection.cs
  24. 9 7
      src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs
  25. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http/Http1ConnectionOfT.cs
  26. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/Http1MessageBody.cs
  27. 9 9
      src/Servers/Kestrel/Core/src/Internal/Http/Http1OutputProducer.cs
  28. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs
  29. 10 9
      src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs
  30. 53 50
      src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs
  31. 3 3
      src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs
  32. 12 12
      src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestPipeReader.cs
  33. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs
  34. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseHeaders.cs
  35. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http/HttpResponsePipeWriter.cs
  36. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs
  37. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseTrailers.cs
  38. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http/HttpUpgradeStream.cs
  39. 3 3
      src/Servers/Kestrel/Core/src/Internal/Http/IHttpOutputProducer.cs
  40. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/IHttpResponseControl.cs
  41. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs
  42. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http/ReasonPhrases.cs
  43. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http/ZeroContentLengthMessageBody.cs
  44. 15 11
      src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/AwaitableProvider.cs
  45. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/OutputFlowControl.cs
  46. 3 3
      src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamOutputFlowControl.cs
  47. 43 30
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs
  48. 4 4
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2FrameWriter.cs
  49. 3 3
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2HeaderEnumerator.cs
  50. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs
  51. 6 6
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs
  52. 7 2
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.FeatureCollection.cs
  53. 8 8
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs
  54. 37 6
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamContext.cs
  55. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamOfT.cs
  56. 3 2
      src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamStack.cs
  57. 35 37
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs
  58. 3 3
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3ControlStream.cs
  59. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3ControlStreamOfT.cs
  60. 3 3
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3FrameWriter.cs
  61. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3MessageBody.cs
  62. 4 4
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3OutputProducer.cs
  63. 10 8
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs
  64. 2 2
      src/Servers/Kestrel/Core/src/Internal/Http3/Http3StreamOfT.cs
  65. 1 1
      src/Servers/Kestrel/Core/src/Internal/Http3/QPack/EncoderStreamReader.cs
  66. 27 8
      src/Servers/Kestrel/Core/src/Internal/Http3ConnectionContext.cs
  67. 20 1
      src/Servers/Kestrel/Core/src/Internal/Http3StreamContext.cs
  68. 14 14
      src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs
  69. 34 11
      src/Servers/Kestrel/Core/src/Internal/HttpConnectionContext.cs
  70. 2 2
      src/Servers/Kestrel/Core/src/Internal/IRequestProcessor.cs
  71. 2 1
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionReference.cs
  72. 3 3
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/Disposable.cs
  73. 1 1
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/Heartbeat.cs
  74. 3 3
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.cs
  75. 2 2
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/IKestrelTrace.cs
  76. 3 3
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelConnection.cs
  77. 15 15
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelEventSource.cs
  78. 30 30
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelTrace.cs
  79. 7 5
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/BufferSegment.cs
  80. 2 1
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/BufferSegmentStack.cs
  81. 15 8
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/ConcurrentPipeWriter.cs
  82. 5 5
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/TimingPipeFlusher.cs
  83. 1 1
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/ThreadPoolAwaitable.cs
  84. 4 2
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/TimeoutControl.cs
  85. 2 2
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/TimeoutControlExtensions.cs
  86. 4 4
      src/Servers/Kestrel/Core/src/Internal/Infrastructure/WrappingStream.cs
  87. 21 24
      src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs
  88. 8 8
      src/Servers/Kestrel/Core/src/Internal/LoggerExtensions.cs
  89. 11 8
      src/Servers/Kestrel/Core/src/Internal/ServiceContext.cs
  90. 15 13
      src/Servers/Kestrel/Core/src/Internal/SniOptionsSelector.cs
  91. 3 3
      src/Servers/Kestrel/Core/src/Internal/TlsConnectionFeature.cs
  92. 16 15
      src/Servers/Kestrel/Core/src/KestrelConfigurationLoader.cs
  93. 1 1
      src/Servers/Kestrel/Core/src/KestrelServer.cs
  94. 2 2
      src/Servers/Kestrel/Core/src/KestrelServerLimits.cs
  95. 11 9
      src/Servers/Kestrel/Core/src/KestrelServerOptions.cs
  96. 7 7
      src/Servers/Kestrel/Core/src/ListenOptions.cs
  97. 2 2
      src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs
  98. 2 2
      src/Servers/Kestrel/Core/src/LocalhostListenOptions.cs
  99. 1 0
      src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj
  100. 9 11
      src/Servers/Kestrel/Core/src/Middleware/Http3ConnectionMiddleware.cs

+ 2 - 2
src/Hosting/Hosting/src/Internal/HostingApplication.cs

@@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Hosting
         // Set up the request
         public Context CreateContext(IFeatureCollection contextFeatures)
         {
-            Context hostContext;
+            Context? hostContext;
             if (contextFeatures is IHostContextContainer<Context> container)
             {
                 hostContext = container.HostContext;
@@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Hosting
         }
 
         // Clean up the request
-        public void DisposeContext(Context context, Exception exception)
+        public void DisposeContext(Context context, Exception? exception)
         {
             var httpContext = context.HttpContext!;
             _diagnostics.RequestEnd(httpContext, exception, context);

+ 1 - 1
src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs

@@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Hosting
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public void RequestEnd(HttpContext httpContext, Exception exception, HostingApplication.Context context)
+        public void RequestEnd(HttpContext httpContext, Exception? exception, HostingApplication.Context context)
         {
             // Local cache items resolved multiple items, in order of use so they are primed in cpu pipeline when used
             var startTimestamp = context.StartTimestamp;

+ 1 - 1
src/Hosting/Server.Abstractions/src/IHostContextContainer.cs

@@ -13,6 +13,6 @@ namespace Microsoft.AspNetCore.Hosting.Server.Abstractions
         /// <summary>
         /// Represents the <typeparamref name="TContext"/>  of the host.
         /// </summary>
-        TContext HostContext { get; set; }
+        TContext? HostContext { get; set; }
     }
 }

+ 1 - 1
src/Hosting/Server.Abstractions/src/IHttpApplication.cs

@@ -31,6 +31,6 @@ namespace Microsoft.AspNetCore.Hosting.Server
         /// </summary>
         /// <param name="context">The TContext to be disposed.</param>
         /// <param name="exception">The Exception thrown when processing did not complete successfully, otherwise null.</param>
-        void DisposeContext(TContext context, Exception exception);
+        void DisposeContext(TContext context, Exception? exception);
     }
 }

+ 2 - 2
src/Hosting/Server.Abstractions/src/PublicAPI.Shipped.txt

@@ -1,6 +1,6 @@
 #nullable enable
 Microsoft.AspNetCore.Hosting.Server.Abstractions.IHostContextContainer<TContext>
-Microsoft.AspNetCore.Hosting.Server.Abstractions.IHostContextContainer<TContext>.HostContext.get -> TContext
+Microsoft.AspNetCore.Hosting.Server.Abstractions.IHostContextContainer<TContext>.HostContext.get -> TContext?
 Microsoft.AspNetCore.Hosting.Server.Abstractions.IHostContextContainer<TContext>.HostContext.set -> void
 Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature
 Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature.Addresses.get -> System.Collections.Generic.ICollection<string!>!
@@ -8,7 +8,7 @@ Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature.PreferHosti
 Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature.PreferHostingUrls.set -> void
 Microsoft.AspNetCore.Hosting.Server.IHttpApplication<TContext>
 Microsoft.AspNetCore.Hosting.Server.IHttpApplication<TContext>.CreateContext(Microsoft.AspNetCore.Http.Features.IFeatureCollection! contextFeatures) -> TContext
-Microsoft.AspNetCore.Hosting.Server.IHttpApplication<TContext>.DisposeContext(TContext context, System.Exception! exception) -> void
+Microsoft.AspNetCore.Hosting.Server.IHttpApplication<TContext>.DisposeContext(TContext context, System.Exception? exception) -> void
 Microsoft.AspNetCore.Hosting.Server.IHttpApplication<TContext>.ProcessRequestAsync(TContext context) -> System.Threading.Tasks.Task!
 Microsoft.AspNetCore.Hosting.Server.IServer
 Microsoft.AspNetCore.Hosting.Server.IServer.Features.get -> Microsoft.AspNetCore.Http.Features.IFeatureCollection!

+ 1 - 1
src/Http/Http.Features/src/IHttpResponseTrailersFeature.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 namespace Microsoft.AspNetCore.Http.Features

+ 2 - 1
src/Http/Http.Features/src/PublicAPI.Shipped.txt

@@ -132,8 +132,8 @@ Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.ReasonPhrase.set -> void
 Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.StatusCode.get -> int
 Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.StatusCode.set -> void
 Microsoft.AspNetCore.Http.Features.IHttpResponseTrailersFeature
-Microsoft.AspNetCore.Http.Features.IHttpResponseTrailersFeature.Trailers.get -> Microsoft.AspNetCore.Http.IHeaderDictionary!
 Microsoft.AspNetCore.Http.Features.IHttpResponseTrailersFeature.Trailers.set -> void
+Microsoft.AspNetCore.Http.Features.IHttpResponseTrailersFeature.Trailers.get -> Microsoft.AspNetCore.Http.IHeaderDictionary!
 Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature
 Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature.SendFileAsync(string! path, long offset, long? count, System.Threading.CancellationToken cancellation) -> System.Threading.Tasks.Task!
 Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature
@@ -233,6 +233,7 @@ Microsoft.AspNetCore.Http.ISession.Keys.get -> System.Collections.Generic.IEnume
 Microsoft.AspNetCore.Http.ISession.LoadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
 Microsoft.AspNetCore.Http.ISession.Remove(string! key) -> void
 Microsoft.AspNetCore.Http.ISession.Set(string! key, byte[]! value) -> void
+Microsoft.AspNetCore.Http.ISession.TryGetValue(string! key, out byte[]? value) -> bool
 Microsoft.AspNetCore.Http.SameSiteMode
 Microsoft.AspNetCore.Http.SameSiteMode.Lax = 1 -> Microsoft.AspNetCore.Http.SameSiteMode
 Microsoft.AspNetCore.Http.SameSiteMode.None = 0 -> Microsoft.AspNetCore.Http.SameSiteMode

+ 0 - 2
src/Http/Http.Features/src/PublicAPI.Unshipped.txt

@@ -1,3 +1 @@
 #nullable enable
-Microsoft.AspNetCore.Http.ISession.TryGetValue(string! key, out byte[]? value) -> bool
-

+ 46 - 25
src/Http/Http/src/BindingAddress.cs

@@ -12,10 +12,24 @@ namespace Microsoft.AspNetCore.Http
     {
         private const string UnixPipeHostPrefix = "unix:/";
 
-        public string Host { get; private set; } = default!;
-        public string PathBase { get; private set; } = default!;
-        public int Port { get; internal set; }
-        public string Scheme { get; private set; } = default!;
+        private BindingAddress(string host, string pathBase, int port, string scheme)
+        {
+            Host = host;
+            PathBase = pathBase;
+            Port = port;
+            Scheme = scheme;
+        }
+
+        [Obsolete("This constructor is obsolete and will be removed in a future version. Use BindingAddress.Parse(address) to create a BindingAddress instance.")]
+        public BindingAddress()
+        {
+            throw new InvalidOperationException("This constructor is obsolete and will be removed in a future version. Use BindingAddress.Parse(address) to create a BindingAddress instance.");
+        }
+
+        public string Host { get; }
+        public string PathBase { get; }
+        public int Port { get; }
+        public string Scheme { get; }
 
         public bool IsUnixPipe
         {
@@ -34,16 +48,20 @@ namespace Microsoft.AspNetCore.Http
                     throw new InvalidOperationException("Binding address is not a unix pipe.");
                 }
 
-                var unixPipeHostPrefixLength = UnixPipeHostPrefix.Length;
-                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                {
-                    // "/" character in unix refers to root. Windows has drive letters and volume separator (c:)
-                    unixPipeHostPrefixLength--;
-                }
-                return Host.Substring(unixPipeHostPrefixLength);
+                return GetUnixPipePath(Host);
             }
         }
 
+        private static string GetUnixPipePath(string host)
+        {
+            var unixPipeHostPrefixLength = UnixPipeHostPrefix.Length;
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // "/" character in unix refers to root. Windows has drive letters and volume separator (c:)
+                unixPipeHostPrefixLength--;
+            }
+            return host.Substring(unixPipeHostPrefixLength);
+        }
 
         public override string ToString()
         {
@@ -77,6 +95,7 @@ namespace Microsoft.AspNetCore.Http
 
         public static BindingAddress Parse(string address)
         {
+            // A null/empty address will throw FormatException
             address = address ?? string.Empty;
 
             int schemeDelimiterStart = address.IndexOf(Uri.SchemeDelimiter, StringComparison.Ordinal);
@@ -117,8 +136,9 @@ namespace Microsoft.AspNetCore.Http
                 pathDelimiterStart = pathDelimiterEnd = address.Length;
             }
 
-            var serverAddress = new BindingAddress();
-            serverAddress.Scheme = address.Substring(0, schemeDelimiterStart);
+            var scheme = address.Substring(0, schemeDelimiterStart);
+            string? host = null;
+            int port = 0;
 
             var hasSpecifiedPort = false;
             if (!isUnixPipe)
@@ -133,49 +153,50 @@ namespace Microsoft.AspNetCore.Http
                     if (int.TryParse(portString, NumberStyles.Integer, CultureInfo.InvariantCulture, out portNumber))
                     {
                         hasSpecifiedPort = true;
-                        serverAddress.Host = address.Substring(schemeDelimiterEnd, portDelimiterStart - schemeDelimiterEnd);
-                        serverAddress.Port = portNumber;
+                        host = address.Substring(schemeDelimiterEnd, portDelimiterStart - schemeDelimiterEnd);
+                        port = portNumber;
                     }
                 }
 
                 if (!hasSpecifiedPort)
                 {
-                    if (string.Equals(serverAddress.Scheme, "http", StringComparison.OrdinalIgnoreCase))
+                    if (string.Equals(scheme, "http", StringComparison.OrdinalIgnoreCase))
                     {
-                        serverAddress.Port = 80;
+                        port = 80;
                     }
-                    else if (string.Equals(serverAddress.Scheme, "https", StringComparison.OrdinalIgnoreCase))
+                    else if (string.Equals(scheme, "https", StringComparison.OrdinalIgnoreCase))
                     {
-                        serverAddress.Port = 443;
+                        port = 443;
                     }
                 }
             }
 
             if (!hasSpecifiedPort)
             {
-                serverAddress.Host = address.Substring(schemeDelimiterEnd, pathDelimiterStart - schemeDelimiterEnd);
+                host = address.Substring(schemeDelimiterEnd, pathDelimiterStart - schemeDelimiterEnd);
             }
 
-            if (string.IsNullOrEmpty(serverAddress.Host))
+            if (string.IsNullOrEmpty(host))
             {
                 throw new FormatException($"Invalid url: '{address}'");
             }
 
-            if (isUnixPipe && !Path.IsPathRooted(serverAddress.UnixPipePath))
+            if (isUnixPipe && !Path.IsPathRooted(GetUnixPipePath(host)))
             {
                 throw new FormatException($"Invalid url, unix socket path must be absolute: '{address}'");
             }
 
+            string pathBase;
             if (address[address.Length - 1] == '/')
             {
-                serverAddress.PathBase = address.Substring(pathDelimiterEnd, address.Length - pathDelimiterEnd - 1);
+                pathBase = address.Substring(pathDelimiterEnd, address.Length - pathDelimiterEnd - 1);
             }
             else
             {
-                serverAddress.PathBase = address.Substring(pathDelimiterEnd);
+                pathBase = address.Substring(pathDelimiterEnd);
             }
 
-            return serverAddress;
+            return new BindingAddress(host: host, pathBase: pathBase, port: port, scheme: scheme);
         }
     }
 }

+ 3 - 0
src/Servers/Kestrel/Core/src/AnyIPListenOptions.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics;
 using System.IO;
 using System.Net;
 using System.Threading.Tasks;
@@ -19,6 +20,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
 
         internal override async Task BindAsync(AddressBindContext context)
         {
+            Debug.Assert(IPEndPoint != null);
+
             // when address is 'http://hostname:port', 'http://*:port', or 'http://+:port'
             try
             {

+ 4 - 4
src/Servers/Kestrel/Core/src/CertificateLoader.cs

@@ -29,8 +29,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
         {
             using (var store = new X509Store(storeName, storeLocation))
             {
-                X509Certificate2Collection storeCertificates = null;
-                X509Certificate2 foundCertificate = null;
+                X509Certificate2Collection? storeCertificates = null;
+                X509Certificate2? foundCertificate = null;
 
                 try
                 {
@@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
                 hasEkuExtension = true;
                 foreach (var oid in extension.EnhancedKeyUsages)
                 {
-                    if (oid.Value.Equals(ServerAuthenticationOid, StringComparison.Ordinal))
+                    if (string.Equals(oid.Value, ServerAuthenticationOid, StringComparison.Ordinal))
                     {
                         return true;
                     }
@@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
         internal static bool DoesCertificateHaveAnAccessiblePrivateKey(X509Certificate2 certificate)
             => certificate.HasPrivateKey;
 
-        private static void DisposeCertificates(X509Certificate2Collection certificates, X509Certificate2 except)
+        private static void DisposeCertificates(X509Certificate2Collection? certificates, X509Certificate2? except)
         {
             if (certificates != null)
             {

+ 1 - 1
src/Servers/Kestrel/Core/src/Features/IHttpMinRequestBodyDataRateFeature.cs

@@ -17,6 +17,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Features
         /// This feature is not supported for HTTP/2 requests except to disable it entirely by setting <see cref="MinDataRate"/> to <see langword="null"/> 
         /// Instead, use <see cref="KestrelServerLimits.MinRequestBodyDataRate"/> for server-wide configuration which applies to both HTTP/2 and HTTP/1.x.
         /// </summary>
-        MinDataRate MinDataRate { get; set; }
+        MinDataRate? MinDataRate { get; set; }
     }
 }

+ 2 - 2
src/Servers/Kestrel/Core/src/Features/IHttpMinResponseDataRateFeature.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Features
@@ -17,6 +17,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Features
         /// This feature is not available for HTTP/2 requests. Instead, use <see cref="KestrelServerLimits.MinResponseDataRate"/>
         /// for server-wide configuration which applies to both HTTP/2 and HTTP/1.x.
         /// </summary>
-        MinDataRate MinDataRate { get; set; }
+        MinDataRate? MinDataRate { get; set; }
     }
 }

+ 4 - 4
src/Servers/Kestrel/Core/src/HttpsConnectionAdapterOptions.cs

@@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
         /// If the server certificate has an Extended Key Usage extension, the usages must include Server Authentication (OID 1.3.6.1.5.5.7.3.1).
         /// </para>
         /// </summary>
-        public X509Certificate2 ServerCertificate { get; set; }
+        public X509Certificate2? ServerCertificate { get; set; }
 
         /// <summary>
         /// <para>
@@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
         /// If the server certificate has an Extended Key Usage extension, the usages must include Server Authentication (OID 1.3.6.1.5.5.7.3.1).
         /// </para>
         /// </summary>
-        public Func<ConnectionContext, string, X509Certificate2> ServerCertificateSelector { get; set; }
+        public Func<ConnectionContext, string?, X509Certificate2?>? ServerCertificateSelector { get; set; }
 
         /// <summary>
         /// Specifies the client certificate requirements for a HTTPS connection. Defaults to <see cref="ClientCertificateMode.NoCertificate"/>.
@@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
         /// Specifies a callback for additional client certificate validation that will be invoked during authentication. This will be ignored
         /// if <see cref="AllowAnyClientCertificate"/> is called after this callback is set.
         /// </summary>
-        public Func<X509Certificate2, X509Chain, SslPolicyErrors, bool> ClientCertificateValidation { get; set; }
+        public Func<X509Certificate2, X509Chain?, SslPolicyErrors, bool>? ClientCertificateValidation { get; set; }
 
         /// <summary>
         /// Specifies allowable SSL protocols. Defaults to <see cref="SslProtocols.None" /> which allows the operating system to choose the best protocol to use,
@@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
         /// Provides direct configuration of the <see cref="SslServerAuthenticationOptions"/> on a per-connection basis.
         /// This is called after all of the other settings have already been applied.
         /// </summary>
-        public Action<ConnectionContext, SslServerAuthenticationOptions> OnAuthenticate { get; set; }
+        public Action<ConnectionContext, SslServerAuthenticationOptions>? OnAuthenticate { get; set; }
 
         /// <summary>
         /// Specifies the maximum amount of time allowed for the TLS/SSL handshake. This must be positive and finite. Defaults to 10 seconds.

+ 16 - 4
src/Servers/Kestrel/Core/src/Internal/AddressBindContext.cs

@@ -10,12 +10,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
     internal class AddressBindContext
     {
-        public ServerAddressesFeature ServerAddressesFeature { get; set; }
+        public AddressBindContext(
+            ServerAddressesFeature serverAddressesFeature,
+            KestrelServerOptions serverOptions,
+            ILogger logger,
+            Func<ListenOptions, Task> createBinding)
+        {
+            ServerAddressesFeature = serverAddressesFeature;
+            ServerOptions = serverOptions;
+            Logger = logger;
+            CreateBinding = createBinding;
+        }
+
+        public ServerAddressesFeature ServerAddressesFeature { get; }
         public ICollection<string> Addresses => ServerAddressesFeature.InternalCollection;
 
-        public KestrelServerOptions ServerOptions { get; set; }
-        public ILogger Logger { get; set; }
+        public KestrelServerOptions ServerOptions { get; }
+        public ILogger Logger { get; }
 
-        public Func<ListenOptions, Task> CreateBinding { get; set; }
+        public Func<ListenOptions, Task> CreateBinding { get; }
     }
 }

+ 6 - 5
src/Servers/Kestrel/Core/src/Internal/AddressBinder.cs

@@ -3,15 +3,16 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
 using System.Net;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Connections;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Hosting.Server.Features;
 using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Connections;
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
 using Microsoft.Extensions.Logging;
 
@@ -73,7 +74,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
         /// Returns an <see cref="IPEndPoint"/> for the given host an port.
         /// If the host parameter isn't "localhost" or an IP address, use IPAddress.Any.
         /// </summary>
-        protected internal static bool TryCreateIPEndPoint(BindingAddress address, out IPEndPoint endpoint)
+        protected internal static bool TryCreateIPEndPoint(BindingAddress address, [NotNullWhen(true)] out IPEndPoint? endpoint)
         {
             if (!IPAddress.TryParse(address.Host, out var ip))
             {
@@ -118,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
                 throw new InvalidOperationException(CoreStrings.FormatConfigurePathBaseFromMethodCall($"{nameof(IApplicationBuilder)}.UsePathBase()"));
             }
 
-            ListenOptions options = null;
+            ListenOptions? options = null;
             if (parsedAddress.IsUnixPipe)
             {
                 options = new ListenOptions(parsedAddress.UnixPipePath);
@@ -150,12 +151,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
         {
             public async Task BindAsync(AddressBindContext context)
             {
-                var httpDefault = ParseAddress(Constants.DefaultServerAddress, out var https);
+                var httpDefault = ParseAddress(Constants.DefaultServerAddress, out _);
                 context.ServerOptions.ApplyEndpointDefaults(httpDefault);
                 await httpDefault.BindAsync(context).ConfigureAwait(false);
 
                 // Conditional https default, only if a cert is available
-                var httpsDefault = ParseAddress(Constants.DefaultServerHttpsAddress, out https);
+                var httpsDefault = ParseAddress(Constants.DefaultServerHttpsAddress, out _);
                 context.ServerOptions.ApplyEndpointDefaults(httpsDefault);
 
                 if (httpsDefault.IsTls || httpsDefault.TryUseHttps())

+ 9 - 9
src/Servers/Kestrel/Core/src/Internal/Certificates/CertificateConfigLoader.cs

@@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
 
         public bool IsTestMock => false;
 
-        public X509Certificate2 LoadCertificate(CertificateConfig certInfo, string endpointName)
+        public X509Certificate2? LoadCertificate(CertificateConfig? certInfo, string endpointName)
         {
             if (certInfo is null)
             {
@@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
             }
             else if (certInfo.IsFileCert)
             {
-                var certificatePath = Path.Combine(HostEnvironment.ContentRootPath, certInfo.Path);
+                var certificatePath = Path.Combine(HostEnvironment.ContentRootPath, certInfo.Path!);
                 if (certInfo.KeyPath != null)
                 {
                     var certificateKeyPath = Path.Combine(HostEnvironment.ContentRootPath, certInfo.KeyPath);
@@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
                     throw new InvalidOperationException(CoreStrings.InvalidPemKey);
                 }
 
-                return new X509Certificate2(Path.Combine(HostEnvironment.ContentRootPath, certInfo.Path), certInfo.Password);
+                return new X509Certificate2(Path.Combine(HostEnvironment.ContentRootPath, certInfo.Path!), certInfo.Password);
             }
             else if (certInfo.IsStoreCert)
             {
@@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
             return new X509Certificate2(certificateBytes, "", X509KeyStorageFlags.DefaultKeySet);
         }
 
-        private static X509Certificate2 LoadCertificateKey(X509Certificate2 certificate, string keyPath, string password)
+        private static X509Certificate2 LoadCertificateKey(X509Certificate2 certificate, string keyPath, string? password)
         {
             // OIDs for the certificate key types.
             const string RSAOid = "1.2.840.113549.1.1.1";
@@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
             };
         }
 
-        private static X509Certificate2 GetCertificate(string certificatePath)
+        private static X509Certificate2? GetCertificate(string certificatePath)
         {
             if (X509Certificate2.GetCertContentType(certificatePath) == X509ContentType.Cert)
             {
@@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
             return null;
         }
 
-        private static X509Certificate2 AttachPemRSAKey(X509Certificate2 certificate, string keyText, string password)
+        private static X509Certificate2 AttachPemRSAKey(X509Certificate2 certificate, string keyText, string? password)
         {
             using var rsa = RSA.Create();
             if (password == null)
@@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
             return certificate.CopyWithPrivateKey(rsa);
         }
 
-        private static X509Certificate2 AttachPemDSAKey(X509Certificate2 certificate, string keyText, string password)
+        private static X509Certificate2 AttachPemDSAKey(X509Certificate2 certificate, string keyText, string? password)
         {
             using var dsa = DSA.Create();
             if (password == null)
@@ -146,7 +146,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
             return certificate.CopyWithPrivateKey(dsa);
         }
 
-        private static X509Certificate2 AttachPemECDSAKey(X509Certificate2 certificate, string keyText, string password)
+        private static X509Certificate2 AttachPemECDSAKey(X509Certificate2 certificate, string keyText, string? password)
         {
             using var ecdsa = ECDsa.Create();
             if (password == null)
@@ -163,7 +163,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
 
         private static X509Certificate2 LoadFromStoreCert(CertificateConfig certInfo)
         {
-            var subject = certInfo.Subject;
+            var subject = certInfo.Subject!;
             var storeName = string.IsNullOrEmpty(certInfo.Store) ? StoreName.My.ToString() : certInfo.Store;
             var location = certInfo.Location;
             var storeLocation = StoreLocation.CurrentUser;

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Certificates/ICertificateConfigLoader.cs

@@ -9,6 +9,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Certificates
     {
         bool IsTestMock { get; }
 
-        X509Certificate2 LoadCertificate(CertificateConfig certInfo, string endpointName);
+        X509Certificate2? LoadCertificate(CertificateConfig? certInfo, string endpointName);
     }
 }

+ 50 - 45
src/Servers/Kestrel/Core/src/Internal/ConfigurationReader.cs

@@ -24,9 +24,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         private readonly IConfiguration _configuration;
 
-        private IDictionary<string, CertificateConfig> _certificates;
-        private EndpointDefaults _endpointDefaults;
-        private IEnumerable<EndpointConfig> _endpoints;
+        private IDictionary<string, CertificateConfig>? _certificates;
+        private EndpointDefaults? _endpointDefaults;
+        private IEnumerable<EndpointConfig>? _endpoints;
 
         public ConfigurationReader(IConfiguration configuration)
         {
@@ -101,16 +101,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
                     throw new InvalidOperationException(CoreStrings.FormatEndpointMissingUrl(endpointConfig.Key));
                 }
 
-                var endpoint = new EndpointConfig
+                var endpoint = new EndpointConfig(
+                    endpointConfig.Key,
+                    url,
+                    ReadSni(endpointConfig.GetSection(SniKey), endpointConfig.Key),
+                    endpointConfig)
                 {
-                    Name = endpointConfig.Key,
-                    Url = url,
                     Protocols = ParseProtocols(endpointConfig[ProtocolsKey]),
-                    ConfigSection = endpointConfig,
-                    Certificate = new CertificateConfig(endpointConfig.GetSection(CertificateKey)),
                     SslProtocols = ParseSslProcotols(endpointConfig.GetSection(SslProtocolsKey)),
                     ClientCertificateMode = ParseClientCertificateMode(endpointConfig[ClientCertificateModeKey]),
-                    Sni = ReadSni(endpointConfig.GetSection(SniKey), endpointConfig.Key),
+                    Certificate = new CertificateConfig(endpointConfig.GetSection(CertificateKey))
                 };
 
                 endpoints.Add(endpoint);
@@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         internal static void ThrowIfContainsHttpsOnlyConfiguration(EndpointConfig endpoint)
         {
-            if (endpoint.Certificate.IsFileCert || endpoint.Certificate.IsStoreCert)
+            if (endpoint.Certificate != null && (endpoint.Certificate.IsFileCert || endpoint.Certificate.IsStoreCert))
             {
                 throw new InvalidOperationException(CoreStrings.FormatEndpointHasUnusedHttpsConfig(endpoint.Name, CertificateKey));
             }
@@ -259,33 +259,38 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
     // }
     internal class EndpointConfig
     {
-        private IConfigurationSection _configSection;
         private ConfigSectionClone _configSectionClone;
 
-        public string Name { get; set; }
-        public string Url { get; set; }
+        public EndpointConfig(
+            string name,
+            string url,
+            Dictionary<string, SniConfig> sni,
+            IConfigurationSection configSection)
+        {
+            Name = name;
+            Url = url;
+            Sni = sni;
+
+            // Compare config sections because it's accessible to app developers via an Action<EndpointConfiguration> callback.
+            // We cannot rely entirely on comparing config sections for equality, because KestrelConfigurationLoader.Reload() sets
+            // EndpointConfig properties to their default values. If a default value changes, the properties would no longer be equal,
+            // but the config sections could still be equal.
+            ConfigSection = configSection;
+            // The IConfigrationSection will mutate, so we need to take a snapshot to compare against later and check for changes.
+            _configSectionClone = new ConfigSectionClone(configSection);
+        }
+
+        public string Name { get; }
+        public string Url { get; }
+        public Dictionary<string, SniConfig> Sni { get; }
+        public IConfigurationSection ConfigSection { get; }
+
         public HttpProtocols? Protocols { get; set; }
         public SslProtocols? SslProtocols { get; set; }
-        public CertificateConfig Certificate { get; set; }
+        public CertificateConfig? Certificate { get; set; }
         public ClientCertificateMode? ClientCertificateMode { get; set; }
-        public Dictionary<string, SniConfig> Sni { get; set; }
 
-        // Compare config sections because it's accessible to app developers via an Action<EndpointConfiguration> callback.
-        // We cannot rely entirely on comparing config sections for equality, because KestrelConfigurationLoader.Reload() sets
-        // EndpointConfig properties to their default values. If a default value changes, the properties would no longer be equal,
-        // but the config sections could still be equal.
-        public IConfigurationSection ConfigSection
-        {
-            get => _configSection;
-            set
-            {
-                _configSection = value;
-                // The IConfigrationSection will mutate, so we need to take a snapshot to compare against later and check for changes.
-                _configSectionClone = new ConfigSectionClone(value);
-            }
-        }
-
-        public override bool Equals(object obj) =>
+        public override bool Equals(object? obj) =>
             obj is EndpointConfig other &&
             Name == other.Name &&
             Url == other.Url &&
@@ -300,8 +305,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             Protocols ?? ListenOptions.DefaultHttpProtocols, SslProtocols ?? System.Security.Authentication.SslProtocols.None,
             Certificate, ClientCertificateMode ?? Https.ClientCertificateMode.NoCertificate, Sni.Count, _configSectionClone);
 
-        public static bool operator ==(EndpointConfig lhs, EndpointConfig rhs) => lhs is null ? rhs is null : lhs.Equals(rhs);
-        public static bool operator !=(EndpointConfig lhs, EndpointConfig rhs) => !(lhs == rhs);
+        public static bool operator ==(EndpointConfig? lhs, EndpointConfig? rhs) => lhs is null ? rhs is null : lhs.Equals(rhs);
+        public static bool operator !=(EndpointConfig? lhs, EndpointConfig? rhs) => !(lhs == rhs);
 
         private static bool CompareSniDictionaries(Dictionary<string, SniConfig> lhs, Dictionary<string, SniConfig> rhs)
         {
@@ -326,10 +331,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
     {
         public HttpProtocols? Protocols { get; set; }
         public SslProtocols? SslProtocols { get; set; }
-        public CertificateConfig Certificate { get; set; }
+        public CertificateConfig? Certificate { get; set; }
         public ClientCertificateMode? ClientCertificateMode { get; set; }
 
-        public override bool Equals(object obj) =>
+        public override bool Equals(object? obj) =>
             obj is SniConfig other &&
             (Protocols ?? ListenOptions.DefaultHttpProtocols) == (other.Protocols ?? ListenOptions.DefaultHttpProtocols) &&
             (SslProtocols ?? System.Security.Authentication.SslProtocols.None) == (other.SslProtocols ?? System.Security.Authentication.SslProtocols.None) &&
@@ -373,30 +378,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
         {
         }
 
-        public IConfigurationSection ConfigSection { get; }
+        public IConfigurationSection? ConfigSection { get; }
 
         // File
         public bool IsFileCert => !string.IsNullOrEmpty(Path);
 
-        public string Path { get; set; }
+        public string? Path { get; set; }
 
-        public string KeyPath { get; set; }
+        public string? KeyPath { get; set; }
 
-        public string Password { get; set; }
+        public string? Password { get; set; }
 
         // Cert store
 
         public bool IsStoreCert => !string.IsNullOrEmpty(Subject);
 
-        public string Subject { get; set; }
+        public string? Subject { get; set; }
 
-        public string Store { get; set; }
+        public string? Store { get; set; }
 
-        public string Location { get; set; }
+        public string? Location { get; set; }
 
         public bool? AllowInvalid { get; set; }
 
-        public override bool Equals(object obj) =>
+        public override bool Equals(object? obj) =>
             obj is CertificateConfig other &&
             Path == other.Path &&
             KeyPath == other.KeyPath &&
@@ -408,7 +413,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         public override int GetHashCode() => HashCode.Combine(Path, KeyPath, Password, Subject, Store, Location, AllowInvalid ?? false);
 
-        public static bool operator ==(CertificateConfig lhs, CertificateConfig rhs) => lhs is null ? rhs is null : lhs.Equals(rhs);
-        public static bool operator !=(CertificateConfig lhs, CertificateConfig rhs) => !(lhs == rhs);
+        public static bool operator ==(CertificateConfig? lhs, CertificateConfig? rhs) => lhs is null ? rhs is null : lhs.Equals(rhs);
+        public static bool operator !=(CertificateConfig? lhs, CertificateConfig? rhs) => !(lhs == rhs);
     }
 }

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/ConnectionLogScope.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
@@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
     {
         private readonly string _connectionId;
 
-        private string _cachedToString;
+        private string? _cachedToString;
 
         public ConnectionLogScope(string connectionId)
         {

+ 11 - 9
src/Servers/Kestrel/Core/src/Internal/Http/DateHeaderValueManager.cs

@@ -17,14 +17,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         // This uses C# compiler's ability to refer to static data directly. For more information see https://vcsjones.dev/2019/02/01/csharp-readonly-span-bytes-static
         private static ReadOnlySpan<byte> DatePreambleBytes => new byte[8] { (byte)'\r', (byte)'\n', (byte)'D', (byte)'a', (byte)'t', (byte)'e', (byte)':', (byte)' ' };
 
-        private DateHeaderValues _dateValues;
+        private DateHeaderValues? _dateValues;
 
         /// <summary>
         /// Returns a value representing the current server date/time for use in the HTTP "Date" response header
         /// in accordance with http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18
         /// </summary>
         /// <returns>The value in string and byte[] format.</returns>
-        public DateHeaderValues GetDateHeaderValues() => _dateValues;
+        public DateHeaderValues GetDateHeaderValues() => _dateValues!;
 
         // Called by the Timer (background) thread
         public void OnHeartbeat(DateTimeOffset now)
@@ -43,18 +43,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             DatePreambleBytes.CopyTo(dateBytes);
             Encoding.ASCII.GetBytes(dateValue, dateBytes.AsSpan(DatePreambleBytes.Length));
 
-            var dateValues = new DateHeaderValues
-            {
-                Bytes = dateBytes,
-                String = dateValue
-            };
+            var dateValues = new DateHeaderValues(dateBytes, dateValue);
             Volatile.Write(ref _dateValues, dateValues);
         }
 
         public class DateHeaderValues
         {
-            public byte[] Bytes;
-            public string String;
+            public readonly byte[] Bytes;
+            public readonly string String;
+
+            public DateHeaderValues(byte[] bytes, string s)
+            {
+                Bytes = bytes;
+                String = s;
+            }
         }
     }
 }

+ 7 - 5
src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs

@@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         private Mode _mode = Mode.Prefix;
         private volatile bool _canceled;
-        private Task _pumpTask;
+        private Task? _pumpTask;
         private readonly Pipe _requestBodyPipe;
         private ReadResult _readResult;
 
@@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         {
             Debug.Assert(!RequestUpgrade, "Upgraded connections should never use this code path!");
 
-            Exception error = null;
+            Exception? error = null;
 
             try
             {
@@ -171,6 +171,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             // call complete here on the reader
             _requestBodyPipe.Reader.Complete();
 
+            Debug.Assert(_pumpTask != null, "OnReadStarted must have been called.");
+
             // PumpTask catches all Exceptions internally.
             if (_pumpTask.IsCompleted)
             {
@@ -180,14 +182,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
 
             // Should I call complete here?
-            return StopAsyncAwaited();
+            return StopAsyncAwaited(_pumpTask);
         }
 
-        private async ValueTask StopAsyncAwaited()
+        private async ValueTask StopAsyncAwaited(Task pumpTask)
         {
             _canceled = true;
             _context.Input.CancelPendingRead();
-            await _pumpTask;
+            await pumpTask;
 
             // At this point both the request body pipe reader and writer should be completed.
             _requestBodyPipe.Reset();

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.FeatureCollection.cs

@@ -8,13 +8,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
     internal partial class Http1Connection : IHttpMinRequestBodyDataRateFeature,
                                              IHttpMinResponseDataRateFeature
     {
-        MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate
+        MinDataRate? IHttpMinRequestBodyDataRateFeature.MinDataRate
         {
             get => MinRequestBodyDataRate;
             set => MinRequestBodyDataRate = value;
         }
 
-        MinDataRate IHttpMinResponseDataRateFeature.MinDataRate
+        MinDataRate? IHttpMinResponseDataRateFeature.MinDataRate
         {
             get => MinResponseDataRate;
             set => MinResponseDataRate = value;

+ 9 - 7
src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs

@@ -30,14 +30,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         private uint _requestCount;
 
         private HttpRequestTarget _requestTargetForm = HttpRequestTarget.Unknown;
-        private Uri _absoluteRequestTarget;
+        private Uri? _absoluteRequestTarget;
 
         // The _parsed fields cache the Path, QueryString, RawTarget, and/or _absoluteRequestTarget
         // from the previous request when DisableStringReuse is false.
-        private string _parsedPath = null;
-        private string _parsedQueryString = null;
-        private string _parsedRawTarget = null;
-        private Uri _parsedAbsoluteRequestTarget;
+        private string? _parsedPath = null;
+        private string? _parsedQueryString = null;
+        private string? _parsedRawTarget = null;
+        private Uri? _parsedAbsoluteRequestTarget;
 
         private long _remainingRequestHeadersBytesAllowed;
 
@@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         public bool RequestTimedOut => _requestTimedOut;
 
-        public MinDataRate MinResponseDataRate { get; set; }
+        public MinDataRate? MinResponseDataRate { get; set; }
 
         public MemoryPool<byte> MemoryPool { get; }
 
@@ -134,6 +134,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         public void HandleReadDataRateTimeout()
         {
+            Debug.Assert(MinRequestBodyDataRate != null);
+
             Log.RequestBodyMinimumDataRateNotSatisfied(ConnectionId, TraceIdentifier, MinRequestBodyDataRate.BytesPerSecond);
             SendTimeoutResponse();
         }
@@ -596,7 +598,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
                 // System.Uri doesn't not tell us if the port was in the original string or not.
                 // When IsDefaultPort = true, we will allow Host: with or without the default port
-                if (hostText != _absoluteRequestTarget.Authority)
+                if (hostText != _absoluteRequestTarget!.Authority)
                 {
                     if (!_absoluteRequestTarget.IsDefaultPort
                         || hostText != _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture))

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http/Http1ConnectionOfT.cs

@@ -5,10 +5,10 @@ using Microsoft.AspNetCore.Hosting.Server.Abstractions;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 {
-    internal sealed class Http1Connection<TContext> : Http1Connection, IHostContextContainer<TContext>
+    internal sealed class Http1Connection<TContext> : Http1Connection, IHostContextContainer<TContext> where TContext : notnull
     {
         public Http1Connection(HttpConnectionContext context) : base(context) { }
 
-        TContext IHostContextContainer<TContext>.HostContext { get; set; }
+        TContext? IHostContextContainer<TContext>.HostContext { get; set; }
     }
 }

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/Http1MessageBody.cs

@@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         public abstract bool TryReadInternal(out ReadResult readResult);
 
-        public override void Complete(Exception exception)
+        public override void Complete(Exception? exception)
         {
             _readerCompleted = true;
             _context.ReportApplicationError(exception);

+ 9 - 9
src/Servers/Kestrel/Core/src/Internal/Http/Http1OutputProducer.cs

@@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         private int _currentMemoryPrefixBytes;
 
         private readonly ConcurrentPipeWriter _pipeWriter;
-        private IMemoryOwner<byte> _fakeMemoryOwner;
+        private IMemoryOwner<byte>? _fakeMemoryOwner;
 
         // Chunked responses need to be treated uniquely when using GetMemory + Advance.
         // We need to know the size of the data written to the chunk before calling Advance on the
@@ -68,9 +68,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         // Fields needed to store writes before calling either startAsync or Write/FlushAsync
         // These should be cleared by the end of the request
-        private List<CompletedBuffer> _completedSegments;
+        private List<CompletedBuffer>? _completedSegments;
         private Memory<byte> _currentSegment;
-        private IMemoryOwner<byte> _currentSegmentOwner;
+        private IMemoryOwner<byte>? _currentSegmentOwner;
         private int _position;
         private bool _startCalled;
 
@@ -332,7 +332,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             writer.Commit();
         }
 
-        public void WriteResponseHeaders(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appComplete)
+        public void WriteResponseHeaders(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appComplete)
         {
             lock (_contextLock)
             {
@@ -349,7 +349,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        private void WriteResponseHeadersInternal(ref BufferWriter<PipeWriter> writer, int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk)
+        private void WriteResponseHeadersInternal(ref BufferWriter<PipeWriter> writer, int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk)
         {
             writer.Write(HttpVersion11Bytes);
             var statusBytes = ReasonPhrases.ToStatusBytes(statusCode, reasonPhrase);
@@ -481,7 +481,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             return WriteAsync(ContinueBytes);
         }
 
-        public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
         {
             lock (_contextLock)
             {
@@ -501,7 +501,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> buffer, CancellationToken cancellationToken)
         {
             lock (_contextLock)
             {
@@ -741,14 +741,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         /// </summary>
         private readonly struct CompletedBuffer
         {
-            private readonly IMemoryOwner<byte> _memoryOwner;
+            private readonly IMemoryOwner<byte>? _memoryOwner;
 
             public Memory<byte> Buffer { get; }
             public int Length { get; }
 
             public ReadOnlySpan<byte> Span => Buffer.Span.Slice(0, Length);
 
-            public CompletedBuffer(IMemoryOwner<byte> owner, Memory<byte> buffer, int length)
+            public CompletedBuffer(IMemoryOwner<byte>? owner, Memory<byte> buffer, int length)
             {
                 _memoryOwner = owner;
 

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs

@@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         protected long _bits = 0;
         protected long? _contentLength;
         protected bool _isReadOnly;
-        protected Dictionary<string, StringValues> MaybeUnknown;
+        protected Dictionary<string, StringValues>? MaybeUnknown;
         protected Dictionary<string, StringValues> Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase));
 
         public long? ContentLength

+ 10 - 9
src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.IO.Pipelines;
 using System.Net;
@@ -74,19 +75,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         string IHttpRequestFeature.Path
         {
-            get => Path;
+            get => Path!;
             set => Path = value;
         }
 
         string IHttpRequestFeature.QueryString
         {
-            get => QueryString;
+            get => QueryString!;
             set => QueryString = value;
         }
 
         string IHttpRequestFeature.RawTarget
         {
-            get => RawTarget;
+            get => RawTarget!;
             set => RawTarget = value;
         }
 
@@ -122,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        bool IHttpRequestBodyDetectionFeature.CanHaveBody => _bodyControl.CanHaveBody;
+        bool IHttpRequestBodyDetectionFeature.CanHaveBody => _bodyControl!.CanHaveBody;
 
         bool IHttpRequestTrailersFeature.Available => RequestTrailersAvailable;
 
@@ -144,7 +145,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             set => StatusCode = value;
         }
 
-        string IHttpResponseFeature.ReasonPhrase
+        string? IHttpResponseFeature.ReasonPhrase
         {
             get => ReasonPhrase;
             set => ReasonPhrase = value;
@@ -166,13 +167,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         bool IHttpUpgradeFeature.IsUpgradableRequest => IsUpgradableRequest;
 
-        IPAddress IHttpConnectionFeature.RemoteIpAddress
+        IPAddress? IHttpConnectionFeature.RemoteIpAddress
         {
             get => RemoteIpAddress;
             set => RemoteIpAddress = value;
         }
 
-        IPAddress IHttpConnectionFeature.LocalIpAddress
+        IPAddress? IHttpConnectionFeature.LocalIpAddress
         {
             get => LocalIpAddress;
             set => LocalIpAddress = value;
@@ -240,7 +241,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         PipeWriter IHttpResponseBodyFeature.Writer => ResponseBodyPipeWriter;
 
-        Endpoint IEndpointFeature.Endpoint
+        Endpoint? IEndpointFeature.Endpoint
         {
             get => _endpoint;
             set => _endpoint = value;
@@ -306,7 +307,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
             await FlushAsync();
 
-            return _bodyControl.Upgrade();
+            return _bodyControl!.Upgrade();
         }
 
         void IHttpRequestLifetimeFeature.Abort()

+ 53 - 50
src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.IO.Pipelines;
 using System.Linq;
@@ -36,14 +37,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         internal const string SchemeHttp = "http";
         internal const string SchemeHttps = "https";
 
-        protected BodyControl _bodyControl;
-        private Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
-        private Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
+        protected BodyControl? _bodyControl;
+        private Stack<KeyValuePair<Func<object, Task>, object>>? _onStarting;
+        private Stack<KeyValuePair<Func<object, Task>, object>>? _onCompleted;
 
         private readonly object _abortLock = new object();
         protected volatile bool _connectionAborted;
         private bool _preventRequestAbortedCancellation;
-        private CancellationTokenSource _abortedCts;
+        private CancellationTokenSource? _abortedCts;
         private CancellationToken? _manuallySetRequestAbortToken;
 
         protected RequestProcessingStatus _requestProcessingStatus;
@@ -57,27 +58,27 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         private bool _hasAdvanced;
         private bool _isLeasedMemoryInvalid = true;
         private bool _autoChunk;
-        protected Exception _applicationException;
-        private BadHttpRequestException _requestRejectedException;
+        protected Exception? _applicationException;
+        private BadHttpRequestException? _requestRejectedException;
 
         protected HttpVersion _httpVersion;
         // This should only be used by the application, not the server. This is settable on HttpRequest but we don't want that to affect
         // how Kestrel processes requests/responses.
-        private string _httpProtocol;
+        private string? _httpProtocol;
 
-        private string _requestId;
+        private string? _requestId;
         private int _requestHeadersParsed;
 
         private long _responseBytesWritten;
 
-        private HttpConnectionContext _context;
-        private RouteValueDictionary _routeValues;
-        private Endpoint _endpoint;
+        private HttpConnectionContext _context = default!;
+        private RouteValueDictionary? _routeValues;
+        private Endpoint? _endpoint;
 
-        protected string _methodText = null;
-        private string _scheme = null;
-        private Stream _requestStreamInternal;
-        private Stream _responseStreamInternal;
+        protected string? _methodText = null;
+        private string? _scheme = null;
+        private Stream? _requestStreamInternal;
+        private Stream? _responseStreamInternal;
 
         public void Initialize(HttpConnectionContext context)
         {
@@ -90,26 +91,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             HttpResponseControl = this;
         }
 
-        public IHttpResponseControl HttpResponseControl { get; set; }
+        public IHttpResponseControl HttpResponseControl { get; set; } = default!;
 
         public ServiceContext ServiceContext => _context.ServiceContext;
-        private IPEndPoint LocalEndPoint => _context.LocalEndPoint;
-        private IPEndPoint RemoteEndPoint => _context.RemoteEndPoint;
+        private IPEndPoint? LocalEndPoint => _context.LocalEndPoint;
+        private IPEndPoint? RemoteEndPoint => _context.RemoteEndPoint;
         public ITimeoutControl TimeoutControl => _context.TimeoutControl;
 
         public IFeatureCollection ConnectionFeatures => _context.ConnectionFeatures;
-        public IHttpOutputProducer Output { get; protected set; }
+        public IHttpOutputProducer Output { get; protected set; } = default!;
 
         protected IKestrelTrace Log => ServiceContext.Log;
         private DateHeaderValueManager DateHeaderValueManager => ServiceContext.DateHeaderValueManager;
         // Hold direct reference to ServerOptions since this is used very often in the request processing path
-        protected KestrelServerOptions ServerOptions { get; set; }
+        protected KestrelServerOptions ServerOptions { get; set; } = default!;
         protected string ConnectionId => _context.ConnectionId;
 
-        public string ConnectionIdFeature { get; set; }
+        public string ConnectionIdFeature { get; set; } = default!;
         public bool HasStartedConsumingRequestBody { get; set; }
         public long? MaxRequestBodySize { get; set; }
-        public MinDataRate MinRequestBodyDataRate { get; set; }
+        public MinDataRate? MinRequestBodyDataRate { get; set; }
         public bool AllowSynchronousIO { get; set; }
 
         /// <summary>
@@ -131,18 +132,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         public bool IsUpgradableRequest { get; private set; }
         public bool IsUpgraded { get; set; }
-        public IPAddress RemoteIpAddress { get; set; }
+        public IPAddress? RemoteIpAddress { get; set; }
         public int RemotePort { get; set; }
-        public IPAddress LocalIpAddress { get; set; }
+        public IPAddress? LocalIpAddress { get; set; }
         public int LocalPort { get; set; }
-        public string Scheme { get; set; }
+        public string? Scheme { get; set; }
         public HttpMethod Method { get; set; }
         public string MethodText => ((IHttpRequestFeature)this).Method;
-        public string PathBase { get; set; }
+        public string? PathBase { get; set; }
 
-        public string Path { get; set; }
-        public string QueryString { get; set; }
-        public string RawTarget { get; set; }
+        public string? Path { get; set; }
+        public string? QueryString { get; set; }
+        public string? RawTarget { get; set; }
 
         public string HttpVersion
         {
@@ -221,12 +222,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        public IHeaderDictionary RequestHeaders { get; set; }
+        public IHeaderDictionary RequestHeaders { get; set; } = default!;
         public IHeaderDictionary RequestTrailers { get; } = new HeaderDictionary();
         public bool RequestTrailersAvailable { get; set; }
-        public Stream RequestBody { get; set; }
-        public PipeReader RequestBodyPipeReader { get; set; }
-        public HttpResponseTrailers ResponseTrailers { get; set; }
+        public Stream RequestBody { get; set; } = default!;
+        public PipeReader RequestBodyPipeReader { get; set; } = default!;
+        public HttpResponseTrailers? ResponseTrailers { get; set; }
 
         private int _statusCode;
         public int StatusCode
@@ -243,9 +244,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        private string _reasonPhrase;
+        private string? _reasonPhrase;
 
-        public string ReasonPhrase
+        public string? ReasonPhrase
         {
             get => _reasonPhrase;
 
@@ -260,9 +261,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        public IHeaderDictionary ResponseHeaders { get; set; }
-        public Stream ResponseBody { get; set; }
-        public PipeWriter ResponseBodyPipeWriter { get; set; }
+        public IHeaderDictionary ResponseHeaders { get; set; } = default!;
+        public Stream ResponseBody { get; set; } = default!;
+        public PipeWriter ResponseBodyPipeWriter { get; set; } = default!;
 
         public CancellationToken RequestAborted
         {
@@ -347,7 +348,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             MaxRequestBodySize = ServerOptions.Limits.MaxRequestBodySize;
             MinRequestBodyDataRate = ServerOptions.Limits.MinRequestBodyDataRate;
             AllowSynchronousIO = ServerOptions.AllowSynchronousIO;
-            TraceIdentifier = null;
+            TraceIdentifier = null!;
             Method = HttpMethod.None;
             _methodText = null;
             _endpoint = null;
@@ -394,7 +395,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             _manuallySetRequestAbortToken = null;
 
             // Lock to prevent CancelRequestAbortedToken from attempting to cancel a disposed CTS.
-            CancellationTokenSource localAbortCts = null;
+            CancellationTokenSource? localAbortCts = null;
 
             lock (_abortLock)
             {
@@ -450,7 +451,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         {
             try
             {
-                CancellationTokenSource localAbortCts = null;
+                CancellationTokenSource? localAbortCts = null;
 
                 lock (_abortLock)
                 {
@@ -489,7 +490,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             if (shouldScheduleCancellation)
             {
                 // Potentially calling user code. CancelRequestAbortedToken logs any exceptions.
-                ServiceContext.Scheduler.Schedule(state => ((HttpProtocol)state).CancelRequestAbortedTokenCallback(), this);
+                ServiceContext.Scheduler.Schedule(state => ((HttpProtocol)state!).CancelRequestAbortedTokenCallback(), this);
             }
         }
 
@@ -555,7 +556,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             RequestTrailersAvailable = true;
         }
 
-        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> application)
+        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             try
             {
@@ -606,7 +607,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application)
+        private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             while (_keepAlive)
             {
@@ -694,6 +695,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
                 // Using these streams in the OnCompleted callback is not allowed.
                 try
                 {
+                    Debug.Assert(_bodyControl != null);
                     await _bodyControl.StopAsync();
                 }
                 catch (Exception ex)
@@ -857,7 +859,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         {
             var responseHeaders = HttpResponseHeaders;
             return new InvalidOperationException(
-                CoreStrings.FormatTooManyBytesWritten(_responseBytesWritten + count, responseHeaders.ContentLength.Value));
+                CoreStrings.FormatTooManyBytesWritten(_responseBytesWritten + count, responseHeaders.ContentLength!.Value));
         }
 
         private void CheckLastWrite()
@@ -878,7 +880,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
         }
 
-        protected bool VerifyResponseContentLength(out Exception ex)
+        protected bool VerifyResponseContentLength([NotNullWhen(false)] out Exception? ex)
         {
             var responseHeaders = HttpResponseHeaders;
 
@@ -912,7 +914,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             }
 
             if (_httpVersion != Http.HttpVersion.Http10 &&
-                RequestHeaders.TryGetValue(HeaderNames.Expect, out var expect) &&
+                ((IHeaderDictionary)HttpRequestHeaders).TryGetValue(HeaderNames.Expect, out var expect) &&
                 (expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase))
             {
                 Output.Write100ContinueAsync().GetAwaiter().GetResult();
@@ -1191,7 +1193,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
                 {
                     if ((option.Protocols & HttpProtocols.Http3) == HttpProtocols.Http3)
                     {
-                        responseHeaders.HeaderAltSvc = $"h3-25=\":{option.IPEndPoint.Port}\"; ma=84600";
+                        responseHeaders.HeaderAltSvc = $"h3-25=\":{option.IPEndPoint!.Port}\"; ma=84600";
                         break;
                     }
                 }
@@ -1307,6 +1309,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         }
 
         [StackTraceHidden]
+        [DoesNotReturn]
         public void ThrowRequestTargetRejected(Span<byte> target)
             => throw GetInvalidRequestTargetException(target);
 
@@ -1331,7 +1334,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             _requestRejectedException = ex;
         }
 
-        public void ReportApplicationError(Exception ex)
+        public void ReportApplicationError(Exception? ex)
         {
             // ReportApplicationError can be called with a null exception from MessageBody
             if (ex == null)
@@ -1416,7 +1419,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             Output.CancelPendingFlush();
         }
 
-        public Task CompleteAsync(Exception exception = null)
+        public Task CompleteAsync(Exception? exception = null)
         {
             if (exception != null)
             {

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs

@@ -19,9 +19,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         private long _previousBits = 0;
 
         public bool ReuseHeaderValues { get; set; }
-        public Func<string, Encoding> EncodingSelector { get; set; }
+        public Func<string, Encoding?> EncodingSelector { get; set; }
 
-        public HttpRequestHeaders(bool reuseHeaderValues = true, Func<string, Encoding> encodingSelector = null)
+        public HttpRequestHeaders(bool reuseHeaderValues = true, Func<string, Encoding?>? encodingSelector = null)
         {
             ReuseHeaderValues = reuseHeaderValues;
             EncodingSelector = encodingSelector ?? KestrelServerOptions.DefaultRequestHeaderEncodingSelector;
@@ -168,7 +168,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
                 _currentKnownType = default;
                 _hasUnknown = collection.MaybeUnknown != null;
                 _unknownEnumerator = _hasUnknown
-                    ? collection.MaybeUnknown.GetEnumerator()
+                    ? collection.MaybeUnknown!.GetEnumerator()
                     : default;
             }
 

+ 12 - 12
src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestPipeReader.cs

@@ -15,9 +15,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
     /// </summary>
     internal sealed class HttpRequestPipeReader : PipeReader
     {
-        private MessageBody _body;
+        private MessageBody? _body;
         private HttpStreamState _state;
-        private ExceptionDispatchInfo _error;
+        private ExceptionDispatchInfo? _error;
 
         public HttpRequestPipeReader()
         {
@@ -28,49 +28,49 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         {
             ValidateState();
 
-            _body.AdvanceTo(consumed);
+            _body!.AdvanceTo(consumed);
         }
 
         public override void AdvanceTo(SequencePosition consumed, SequencePosition examined)
         {
             ValidateState();
 
-            _body.AdvanceTo(consumed, examined);
+            _body!.AdvanceTo(consumed, examined);
         }
 
         public override void CancelPendingRead()
         {
             ValidateState();
 
-            _body.CancelPendingRead();
+            _body!.CancelPendingRead();
         }
 
-        public override void Complete(Exception exception = null)
+        public override void Complete(Exception? exception = null)
         {
             ValidateState();
 
-            _body.Complete(exception);
+            _body!.Complete(exception);
         }
 
-        public override ValueTask CompleteAsync(Exception exception = null)
+        public override ValueTask CompleteAsync(Exception? exception = null)
         {
             ValidateState();
 
-            return _body.CompleteAsync(exception);
+            return _body!.CompleteAsync(exception);
         }
 
         public override ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default)
         {
             ValidateState(cancellationToken);
 
-            return _body.ReadAsync(cancellationToken);
+            return _body!.ReadAsync(cancellationToken);
         }
 
         public override bool TryRead(out ReadResult result)
         {
             ValidateState();
 
-            return _body.TryRead(out result);
+            return _body!.TryRead(out result);
         }
 
         public void StartAcceptingReads(MessageBody body)
@@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             _body = null;
         }
 
-        public void Abort(Exception error = null)
+        public void Abort(Exception? error = null)
         {
             // We don't want to throw an ODE until the app func actually completes.
             // If the request is aborted, we throw a TaskCanceledException instead,

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs

@@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             return Task.CompletedTask;
         }
 
-        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
         {
             return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state);   
         }

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseHeaders.cs

@@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
                 _currentKnownType = default;
                 _hasUnknown = collection.MaybeUnknown != null;
                 _unknownEnumerator = _hasUnknown
-                    ? collection.MaybeUnknown.GetEnumerator()
+                    ? collection.MaybeUnknown!.GetEnumerator()
                     : default;
             }
 

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http/HttpResponsePipeWriter.cs

@@ -34,13 +34,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             _pipeControl.CancelPendingFlush();
         }
 
-        public override void Complete(Exception exception = null)
+        public override void Complete(Exception? exception = null)
         {
             ValidateState();
             _completeTask = _pipeControl.CompleteAsync(exception);
         }
 
-        public override ValueTask CompleteAsync(Exception exception = null)
+        public override ValueTask CompleteAsync(Exception? exception = null)
         {
             Complete();
             return new ValueTask(_completeTask);

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs

@@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             WriteAsync(buffer, offset, count, default).GetAwaiter().GetResult();
         }
 
-        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
         {
             return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state);
         }

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseTrailers.cs

@@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
                 _currentKnownType = default;
                 _hasUnknown = collection.MaybeUnknown != null;
                 _unknownEnumerator = _hasUnknown
-                    ? collection.MaybeUnknown.GetEnumerator()
+                    ? collection.MaybeUnknown!.GetEnumerator()
                     : default;
             }
 

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http/HttpUpgradeStream.cs

@@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             _responseStream.Close();
         }
 
-        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
         {
             return _requestStream.BeginRead(buffer, offset, count, callback, state);
         }
@@ -130,7 +130,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             return _requestStream.EndRead(asyncResult);
         }
 
-        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
         {
             return _responseStream.BeginWrite(buffer, offset, count, callback, state);
         }

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Http/IHttpOutputProducer.cs

@@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         ValueTask<FlushResult> WriteChunkAsync(ReadOnlySpan<byte> data, CancellationToken cancellationToken);
         ValueTask<FlushResult> FlushAsync(CancellationToken cancellationToken);
         ValueTask<FlushResult> Write100ContinueAsync();
-        void WriteResponseHeaders(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appCompleted);
+        void WriteResponseHeaders(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appCompleted);
         // This takes ReadOnlySpan instead of ReadOnlyMemory because it always synchronously copies data before flushing.
         ValueTask<FlushResult> WriteDataToPipeAsync(ReadOnlySpan<byte> data, CancellationToken cancellationToken);
         Task WriteDataAsync(ReadOnlySpan<byte> data, CancellationToken cancellationToken);
@@ -23,8 +23,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         Memory<byte> GetMemory(int sizeHint = 0);
         void CancelPendingFlush();
         void Stop();
-        ValueTask<FlushResult> FirstWriteAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken);
-        ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken);
+        ValueTask<FlushResult> FirstWriteAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken);
+        ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken);
         void Reset();
     }
 }

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/IHttpResponseControl.cs

@@ -17,6 +17,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
         ValueTask<FlushResult> FlushPipeAsync(CancellationToken cancellationToken);
         ValueTask<FlushResult> WritePipeAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken);
         void CancelPendingFlush();
-        Task CompleteAsync(Exception exception = null);
+        Task CompleteAsync(Exception? exception = null);
     }
 }

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs

@@ -56,9 +56,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
 
         public abstract void CancelPendingRead();
 
-        public abstract void Complete(Exception exception);
+        public abstract void Complete(Exception? exception);
 
-        public virtual ValueTask CompleteAsync(Exception exception)
+        public virtual ValueTask CompleteAsync(Exception? exception)
         {
             Complete(exception);
             return default;

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http/ReasonPhrases.cs

@@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             return Encoding.ASCII.GetBytes(statusCode.ToString(CultureInfo.InvariantCulture) + " " + reasonPhrase);
         }
 
-        public static byte[] ToStatusBytes(int statusCode, string reasonPhrase = null)
+        public static byte[] ToStatusBytes(int statusCode, string? reasonPhrase = null)
         {
             if (string.IsNullOrEmpty(reasonPhrase))
             {

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http/ZeroContentLengthMessageBody.cs

@@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
     internal sealed class ZeroContentLengthMessageBody : MessageBody
     {
         public ZeroContentLengthMessageBody(bool keepAlive)
-            : base(null)
+            : base(null!) // Ok to pass null here because this type overrides all the base methods
         {
             RequestKeepAlive = keepAlive;
         }
@@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
             return true;
         }
 
-        public override void Complete(Exception ex) { }
+        public override void Complete(Exception? ex) { }
 
         public override void CancelPendingRead() { }
     }

+ 15 - 11
src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/AwaitableProvider.cs

@@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
 {
     internal abstract class AwaitableProvider
     {
-        public abstract ManualResetValueTaskSource<object> GetAwaitable();
+        public abstract ManualResetValueTaskSource<object?> GetAwaitable();
         public abstract void CompleteCurrent();
         public abstract int ActiveCount { get; }
     }
@@ -19,11 +19,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
     /// </summary>
     internal class MultipleAwaitableProvider : AwaitableProvider
     {
-        private Queue<ManualResetValueTaskSource<object>> _awaitableQueue;
-        private Queue<ManualResetValueTaskSource<object>> _awaitableCache;
+        private Queue<ManualResetValueTaskSource<object?>>? _awaitableQueue;
+        private Queue<ManualResetValueTaskSource<object?>>? _awaitableCache;
 
         public override void CompleteCurrent()
         {
+            Debug.Assert(_awaitableQueue != null);
+            Debug.Assert(_awaitableCache != null);
+
             var awaitable = _awaitableQueue.Dequeue();
             awaitable.TrySetResult(null);
 
@@ -31,17 +34,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
             _awaitableCache.Enqueue(awaitable);
         }
 
-        public override ManualResetValueTaskSource<object> GetAwaitable()
+        public override ManualResetValueTaskSource<object?> GetAwaitable()
         {
             if (_awaitableQueue == null)
             {
-                _awaitableQueue = new Queue<ManualResetValueTaskSource<object>>();
-                _awaitableCache = new Queue<ManualResetValueTaskSource<object>>();
+                _awaitableQueue = new Queue<ManualResetValueTaskSource<object?>>();
+                _awaitableCache = new Queue<ManualResetValueTaskSource<object?>>();
             }
 
             // First attempt to reuse an existing awaitable in the queue
             // to save allocating a new instance.
-            if (_awaitableCache.TryDequeue(out var awaitable))
+            if (_awaitableCache!.TryDequeue(out var awaitable))
             {
                 // Reset previously used awaitable
                 Debug.Assert(awaitable.GetStatus() == ValueTaskSourceStatus.Succeeded, "Previous awaitable should have been completed.");
@@ -49,7 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
             }
             else
             {
-                awaitable = new ManualResetValueTaskSource<object>();
+                awaitable = new ManualResetValueTaskSource<object?>();
             }
 
             _awaitableQueue.Enqueue(awaitable);
@@ -65,18 +68,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
     /// </summary>
     internal class SingleAwaitableProvider : AwaitableProvider
     {
-        private ManualResetValueTaskSource<object> _awaitable;
+        private ManualResetValueTaskSource<object?>? _awaitable;
 
         public override void CompleteCurrent()
         {
+            Debug.Assert(_awaitable != null);
             _awaitable.TrySetResult(null);
         }
 
-        public override ManualResetValueTaskSource<object> GetAwaitable()
+        public override ManualResetValueTaskSource<object?> GetAwaitable()
         {
             if (_awaitable == null)
             {
-                _awaitable = new ManualResetValueTaskSource<object>();
+                _awaitable = new ManualResetValueTaskSource<object?>();
             }
             else
             {

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/OutputFlowControl.cs

@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
         public int Available => _flow.Available;
         public bool IsAborted => _flow.IsAborted;
 
-        public ManualResetValueTaskSource<object> AvailabilityAwaitable
+        public ManualResetValueTaskSource<object?> AvailabilityAwaitable
         {
             get
             {

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamOutputFlowControl.cs

@@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
         private readonly OutputFlowControl _connectionLevelFlowControl;
         private readonly OutputFlowControl _streamLevelFlowControl;
 
-        private ManualResetValueTaskSource<object> _currentConnectionLevelAwaitable;
+        private ManualResetValueTaskSource<object?>? _currentConnectionLevelAwaitable;
         private int _currentConnectionLevelAwaitableVersion;
 
         public StreamOutputFlowControl(OutputFlowControl connectionLevelFlowControl, uint initialWindowSize)
@@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
             _streamLevelFlowControl.Advance(bytes);
         }
 
-        public int AdvanceUpToAndWait(long bytes, out ValueTask<object> availabilityTask)
+        public int AdvanceUpToAndWait(long bytes, out ValueTask<object?> availabilityTask)
         {
             var leastAvailableFlow = _connectionLevelFlowControl.Available < _streamLevelFlowControl.Available
                 ? _connectionLevelFlowControl : _streamLevelFlowControl;
@@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl
                     _currentConnectionLevelAwaitableVersion = awaitable.Version;
                 }
 
-                availabilityTask = new ValueTask<object>(awaitable, awaitable.Version);
+                availabilityTask = new ValueTask<object?>(awaitable, awaitable.Version);
             }
 
             return actual;

+ 43 - 30
src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs

@@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         private readonly Http2Frame _incomingFrame = new Http2Frame();
 
-        private Http2Stream _currentHeadersStream;
+        private Http2Stream? _currentHeadersStream;
         private RequestHeaderParsingState _requestHeaderParsingState;
         private PseudoHeaderFields _parsedPseudoHeaderFields;
         private Http2HeadersFrameFlags _headerFlags;
@@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         private int _isClosed;
 
         // Internal for testing
-        internal readonly Http2KeepAlive _keepAlive;
+        internal readonly Http2KeepAlive? _keepAlive;
         internal readonly Dictionary<int, Http2Stream> _streams = new Dictionary<int, Http2Stream>();
         internal Http2StreamStack StreamPool;
         // Max tracked streams is double max concurrent streams.
@@ -174,6 +174,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         public void HandleReadDataRateTimeout()
         {
+            Debug.Assert(Limits.MinRequestBodyDataRate != null);
+
             Log.RequestBodyMinimumDataRateNotSatisfied(ConnectionId, null, Limits.MinRequestBodyDataRate.BytesPerSecond);
             Abort(new ConnectionAbortedException(CoreStrings.BadRequest_RequestBodyTimeout));
         }
@@ -188,9 +190,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
         }
 
-        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> application)
+        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
-            Exception error = null;
+            Exception? error = null;
             var errorCode = Http2ErrorCode.NO_ERROR;
 
             try
@@ -317,6 +319,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
             catch (HPackDecodingException ex)
             {
+                Debug.Assert(_currentHeadersStream != null);
+
                 Log.HPackDecodingError(ConnectionId, _currentHeadersStream.StreamId, ex);
                 error = ex;
                 errorCode = Http2ErrorCode.COMPRESSION_ERROR;
@@ -330,7 +334,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             finally
             {
                 var connectionError = error as ConnectionAbortedException
-                    ?? new ConnectionAbortedException(CoreStrings.Http2ConnectionFaulted, error);
+                    ?? new ConnectionAbortedException(CoreStrings.Http2ConnectionFaulted, error!);
 
                 try
                 {
@@ -455,7 +459,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             return true;
         }
 
-        private Task ProcessFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload)
+        private Task ProcessFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload) where TContext : notnull
         {
             // http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1
             // Streams initiated by a client MUST use odd-numbered stream identifiers; ...
@@ -538,7 +542,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamClosed(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.STREAM_CLOSED);
         }
 
-        private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload)
+        private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload) where TContext : notnull
         {
             if (_currentHeadersStream != null)
             {
@@ -628,7 +632,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
         }
 
-        private Http2Stream GetStream<TContext>(IHttpApplication<TContext> application)
+        private Http2Stream GetStream<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             if (StreamPool.TryPop(out var stream))
             {
@@ -643,24 +647,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         private Http2StreamContext CreateHttp2StreamContext()
         {
-            return new Http2StreamContext
-            {
-                ConnectionId = ConnectionId,
-                StreamId = _incomingFrame.StreamId,
-                ServiceContext = _context.ServiceContext,
-                ConnectionFeatures = _context.ConnectionFeatures,
-                MemoryPool = _context.MemoryPool,
-                LocalEndPoint = _context.LocalEndPoint,
-                RemoteEndPoint = _context.RemoteEndPoint,
-                StreamLifetimeHandler = this,
-                ClientPeerSettings = _clientSettings,
-                ServerPeerSettings = _serverSettings,
-                FrameWriter = _frameWriter,
-                ConnectionInputFlowControl = _inputFlowControl,
-                ConnectionOutputFlowControl = _outputFlowControl,
-                TimeoutControl = TimeoutControl,
-                InitialExecutionContext = _context.InitialExecutionContext,
-            };
+            var streamContext = new Http2StreamContext(
+                ConnectionId,
+                protocols: default,
+                _context.ServiceContext,
+                _context.ConnectionFeatures,
+                _context.MemoryPool,
+                _context.LocalEndPoint,
+                _context.RemoteEndPoint,
+                _incomingFrame.StreamId,
+                streamLifetimeHandler: this,
+                _clientSettings,
+                _serverSettings,
+                _frameWriter,
+                _inputFlowControl,
+                _outputFlowControl);
+            streamContext.TimeoutControl = _context.TimeoutControl;
+            streamContext.InitialExecutionContext = _context.InitialExecutionContext;
+
+            return streamContext;
         }
 
         private void ReturnStream(Http2Stream stream)
@@ -972,6 +977,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         private Task DecodeHeadersAsync(bool endHeaders, in ReadOnlySequence<byte> payload)
         {
+            Debug.Assert(_currentHeadersStream != null);
+
             try
             {
                 _highestOpenedStreamId = _currentHeadersStream.StreamId;
@@ -995,6 +1002,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         private Task DecodeTrailersAsync(bool endHeaders, in ReadOnlySequence<byte> payload)
         {
+            Debug.Assert(_currentHeadersStream != null);
+
             _hpackDecoder.Decode(payload, endHeaders, handler: this);
 
             if (endHeaders)
@@ -1008,6 +1017,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         private void StartStream()
         {
+            Debug.Assert(_currentHeadersStream != null);
+
             // The stream now exists and must be tracked and drained even if Http2StreamErrorException is thrown before dispatching to the application.
             _streams[_incomingFrame.StreamId] = _currentHeadersStream;
             IncrementActiveClientStreamCount();
@@ -1126,7 +1137,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         private void UpdateCompletedStreams()
         {
-            Http2Stream firstRequedStream = null;
+            Http2Stream? firstRequedStream = null;
             var now = SystemClock.UtcNowTicks;
 
             while (_completedStreams.TryDequeue(out var stream))
@@ -1264,6 +1275,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         // rework the flow so that the remaining headers are drained and the decompression state is maintained.
         private void OnHeaderCore(int? index, bool indexedValue, ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
         {
+            Debug.Assert(_currentHeadersStream != null);
+
             // https://tools.ietf.org/html/rfc7540#section-6.5.2
             // "The value is based on the uncompressed size of header fields, including the length of the name and value in octets plus an overhead of 32 octets for each header field.";
             _totalParsedHeaderSize += HeaderField.RfcOverhead + name.Length + value.Length;
@@ -1312,7 +1325,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         }
 
         public void OnHeadersComplete(bool endStream)
-            => _currentHeadersStream.OnHeadersComplete();
+            => _currentHeadersStream!.OnHeadersComplete();
 
         private void ValidateHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
         {
@@ -1498,7 +1511,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         private async Task ReadInputAsync()
         {
-            Exception error = null;
+            Exception? error = null;
             try
             {
                 while (true)
@@ -1551,7 +1564,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
             // Initialize to completed so UpdateCompletedStreams runs at least once during connection teardown
             // if there are still active streams.
-            private Action _callback = _callbackCompleted;
+            private Action? _callback = _callbackCompleted;
 
             public StreamCloseAwaitable GetAwaiter() => this;
             public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);

+ 4 - 4
src/Servers/Kestrel/Core/src/Internal/Http2/Http2FrameWriter.cs

@@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         private readonly string _connectionId;
         private readonly IKestrelTrace _log;
         private readonly ITimeoutControl _timeoutControl;
-        private readonly MinDataRate _minResponseDataRate;
+        private readonly MinDataRate? _minResponseDataRate;
         private readonly TimingPipeFlusher _flusher;
         private readonly HPackEncoder _hpackEncoder;
 
@@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             Http2Connection http2Connection,
             OutputFlowControl connectionOutputFlowControl,
             ITimeoutControl timeoutControl,
-            MinDataRate minResponseDataRate,
+            MinDataRate? minResponseDataRate,
             string connectionId,
             MemoryPool<byte> memoryPool,
             ServiceContext serviceContext)
@@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
         }
 
-        public ValueTask<FlushResult> FlushAsync(IHttpOutputAborter outputAborter, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FlushAsync(IHttpOutputAborter? outputAborter, CancellationToken cancellationToken)
         {
             lock (_writeLock)
             {
@@ -413,7 +413,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
             while (dataLength > 0)
             {
-                ValueTask<object> availabilityTask;
+                ValueTask<object?> availabilityTask;
                 var writeTask = default(ValueTask<FlushResult>);
 
                 lock (_writeLock)

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Http2/Http2HeaderEnumerator.cs

@@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         private HeadersType _headersType;
         private HttpResponseHeaders.Enumerator _headersEnumerator;
         private HttpResponseTrailers.Enumerator _trailersEnumerator;
-        private IEnumerator<KeyValuePair<string, StringValues>> _genericEnumerator;
+        private IEnumerator<KeyValuePair<string, StringValues>>? _genericEnumerator;
         private StringValues.Enumerator _stringValuesEnumerator;
         private bool _hasMultipleValues;
         private KnownHeaderType _knownHeaderType;
@@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
             else
             {
-                return _genericEnumerator.MoveNext()
+                return _genericEnumerator!.MoveNext()
                     ? SetCurrent(_genericEnumerator.Current.Key, _genericEnumerator.Current.Value, default)
                     : false;
             }
@@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
             else
             {
-                _genericEnumerator.Reset();
+                _genericEnumerator!.Reset();
             }
             _stringValuesEnumerator = default;
             _knownHeaderType = default;

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs

@@ -103,13 +103,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             return _readResult;
         }
 
-        public override void Complete(Exception exception)
+        public override void Complete(Exception? exception)
         {
             _context.ReportApplicationError(exception);
             _context.RequestBodyPipe.Reader.Complete();
         }
 
-        public override ValueTask CompleteAsync(Exception exception)
+        public override ValueTask CompleteAsync(Exception? exception)
         {
             _context.ReportApplicationError(exception);
             return _context.RequestBodyPipe.Reader.CompleteAsync();

+ 6 - 6
src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs

@@ -34,8 +34,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         private readonly Pipe _pipe;
         private readonly ConcurrentPipeWriter _pipeWriter;
         private readonly PipeReader _pipeReader;
-        private readonly ManualResetValueTaskSource<object> _resetAwaitable = new ManualResetValueTaskSource<object>();
-        private IMemoryOwner<byte> _fakeMemoryOwner;
+        private readonly ManualResetValueTaskSource<object?> _resetAwaitable = new ManualResetValueTaskSource<object?>();
+        private IMemoryOwner<byte>? _fakeMemoryOwner;
         private bool _startedWritingDataFrames;
         private bool _streamCompleted;
         private bool _suffixSent;
@@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         // associated with the implementation is just delegated to the ManualResetValueTaskSourceCore.
         private ValueTask<FlushResult> GetWaiterTask() => new ValueTask<FlushResult>(this, _responseCompleteTaskSource.Version);
         ValueTaskSourceStatus IValueTaskSource<FlushResult>.GetStatus(short token) => _responseCompleteTaskSource.GetStatus(token);
-        void IValueTaskSource<FlushResult>.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _responseCompleteTaskSource.OnCompleted(continuation, state, token, flags);
+        void IValueTaskSource<FlushResult>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => _responseCompleteTaskSource.OnCompleted(continuation, state, token, flags);
         FlushResult IValueTaskSource<FlushResult>.GetResult(short token) => _responseCompleteTaskSource.GetResult(token);
 
         public Http2OutputProducer(Http2Stream stream, Http2StreamContext context, StreamOutputFlowControl flowControl)
@@ -180,7 +180,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
         }
 
-        public void WriteResponseHeaders(int statusCode, string ReasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appCompleted)
+        public void WriteResponseHeaders(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appCompleted)
         {
             lock (_dataWriterLock)
             {
@@ -353,7 +353,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
         }
 
-        public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
         {
             lock (_dataWriterLock)
             {
@@ -368,7 +368,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             throw new NotImplementedException();
         }
 
-        public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }

+ 7 - 2
src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.FeatureCollection.cs

@@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
                                          IHttpResponseTrailersFeature
 
     {
-        private IHeaderDictionary _userTrailers;
+        private IHeaderDictionary? _userTrailers;
 
         IHeaderDictionary IHttpResponseTrailersFeature.Trailers
         {
@@ -35,13 +35,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
             set
             {
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(value));
+                }
+
                 _userTrailers = value;
             }
         }
 
         int IHttp2StreamIdFeature.StreamId => _context.StreamId;
 
-        MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate
+        MinDataRate? IHttpMinRequestBodyDataRateFeature.MinDataRate
         {
             get => throw new NotSupportedException(CoreStrings.Http2MinDataRateNotSupported);
             set 

+ 8 - 8
src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs

@@ -22,15 +22,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 {
     internal abstract partial class Http2Stream : HttpProtocol, IThreadPoolWorkItem, IDisposable
     {
-        private Http2StreamContext _context;
-        private Http2OutputProducer _http2Output;
-        private StreamInputFlowControl _inputFlowControl;
-        private StreamOutputFlowControl _outputFlowControl;
-        private Http2MessageBody _messageBody;
+        private Http2StreamContext _context = default!;
+        private Http2OutputProducer _http2Output = default!;
+        private StreamInputFlowControl _inputFlowControl = default!;
+        private StreamOutputFlowControl _outputFlowControl = default!;
+        private Http2MessageBody? _messageBody;
 
         private bool _decrementCalled;
 
-        public Pipe RequestBodyPipe { get; set; }
+        public Pipe RequestBodyPipe { get; private set; } = default!;
 
         internal long DrainExpirationTicks { get; set; }
 
@@ -270,7 +270,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             }
 
             // Approximate MaxRequestLineSize by totaling the required pseudo header field lengths.
-            var requestLineLength = _methodText.Length + Scheme.Length + hostText.Length + path.Length;
+            var requestLineLength = _methodText!.Length + Scheme!.Length + hostText.Length + path.Length;
             if (requestLineLength > ServerOptions.Limits.MaxRequestLineSize)
             {
                 ResetAndAbort(new ConnectionAbortedException(CoreStrings.BadRequest_RequestLineTooLong), Http2ErrorCode.PROTOCOL_ERROR);
@@ -386,7 +386,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
                     pathBuffer[i] = (byte)ch;
                 }
 
-                Path = PathNormalizer.DecodePath(pathBuffer, pathEncoded, RawTarget, QueryString.Length);
+                Path = PathNormalizer.DecodePath(pathBuffer, pathEncoded, RawTarget!, QueryString!.Length);
 
                 return true;
             }

+ 37 - 6
src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamContext.cs

@@ -1,18 +1,49 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Buffers;
+using System.IO.Pipelines;
+using System.Net;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 {
     internal sealed class Http2StreamContext : HttpConnectionContext
     {
+        public Http2StreamContext(
+            string connectionId,
+            HttpProtocols protocols,
+            ServiceContext serviceContext,
+            IFeatureCollection connectionFeatures,
+            MemoryPool<byte> memoryPool,
+            IPEndPoint? localEndPoint,
+            IPEndPoint? remoteEndPoint,
+            int streamId,
+            IHttp2StreamLifetimeHandler streamLifetimeHandler,
+            Http2PeerSettings clientPeerSettings,
+            Http2PeerSettings serverPeerSettings,
+            Http2FrameWriter frameWriter,
+            InputFlowControl connectionInputFlowControl,
+            OutputFlowControl connectionOutputFlowControl) : base(connectionId, protocols, connectionContext: null!, serviceContext, connectionFeatures, memoryPool, localEndPoint, remoteEndPoint, transport: null!)
+        {
+            StreamId = streamId;
+            StreamLifetimeHandler = streamLifetimeHandler;
+            ClientPeerSettings = clientPeerSettings;
+            ServerPeerSettings = serverPeerSettings;
+            FrameWriter = frameWriter;
+            ConnectionInputFlowControl = connectionInputFlowControl;
+            ConnectionOutputFlowControl = connectionOutputFlowControl;
+        }
+
+        public IHttp2StreamLifetimeHandler StreamLifetimeHandler { get; }
+        public Http2PeerSettings ClientPeerSettings { get; }
+        public Http2PeerSettings ServerPeerSettings { get; }
+        public Http2FrameWriter FrameWriter { get; }
+        public InputFlowControl ConnectionInputFlowControl { get; }
+        public OutputFlowControl ConnectionOutputFlowControl { get; }
+
         public int StreamId { get; set; }
-        public IHttp2StreamLifetimeHandler StreamLifetimeHandler { get; set; }
-        public Http2PeerSettings ClientPeerSettings { get; set; }
-        public Http2PeerSettings ServerPeerSettings { get; set; }
-        public Http2FrameWriter FrameWriter { get; set; }
-        public InputFlowControl ConnectionInputFlowControl { get; set; }
-        public OutputFlowControl ConnectionOutputFlowControl { get; set; }
     }
 }

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamOfT.cs

@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 {
-    internal sealed class Http2Stream<TContext> : Http2Stream, IHostContextContainer<TContext>
+    internal sealed class Http2Stream<TContext> : Http2Stream, IHostContextContainer<TContext> where TContext : notnull
     {
         private readonly IHttpApplication<TContext> _application;
 
@@ -25,6 +25,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
         }
 
         // Pooled Host context
-        TContext IHostContextContainer<TContext>.HostContext { get; set; }
+        TContext? IHostContextContainer<TContext>.HostContext { get; set; }
     }
 }

+ 3 - 2
src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamStack.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
@@ -21,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         public int Count => _size;
 
-        public bool TryPop(out Http2Stream result)
+        public bool TryPop([NotNullWhen(true)] out Http2Stream? result)
         {
             int size = _size - 1;
             Http2StreamAsValueType[] array = _array;
@@ -38,7 +39,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
             return true;
         }
 
-        public bool TryPeek(out Http2Stream result)
+        public bool TryPeek([NotNullWhen(true)] out Http2Stream? result)
         {
             int size = _size - 1;
             Http2StreamAsValueType[] array = _array;

+ 35 - 37
src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs

@@ -23,9 +23,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
     {
         public DynamicTable DynamicTable { get; set; }
 
-        public Http3ControlStream ControlStream { get; set; }
-        public Http3ControlStream EncoderStream { get; set; }
-        public Http3ControlStream DecoderStream { get; set; }
+        public Http3ControlStream? ControlStream { get; set; }
+        public Http3ControlStream? EncoderStream { get; set; }
+        public Http3ControlStream? DecoderStream { get; set; }
 
         internal readonly Dictionary<long, Http3Stream> _streams = new Dictionary<long, Http3Stream>();
 
@@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 
         private IKestrelTrace Log => _context.ServiceContext.Log;
 
-        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
+        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication) where TContext : notnull
         {
             try
             {
@@ -87,10 +87,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
                 connectionHeartbeatFeature?.OnHeartbeat(state => ((Http3Connection)state).Tick(), this);
 
                 // Register for graceful shutdown of the server
-                using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((Http3Connection)state).StopProcessingNextRequest(), this);
+                using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((Http3Connection)state!).StopProcessingNextRequest(), this);
 
                 // Register for connection close
-                using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((Http3Connection)state).OnConnectionClosed(), this);
+                using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((Http3Connection)state!).OnConnectionClosed(), this);
 
                 await InnerProcessRequestsAsync(httpApplication);
             }
@@ -189,7 +189,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             }
         }
 
-        internal async Task InnerProcessRequestsAsync<TContext>(IHttpApplication<TContext> application)
+        internal async Task InnerProcessRequestsAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             // Start other three unidirectional streams here.
             var controlTask = CreateControlStream(application);
@@ -211,19 +211,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 
                     Debug.Assert(quicStreamFeature != null);
 
-                    var httpConnectionContext = new Http3StreamContext
-                    {
-                        ConnectionId = streamContext.ConnectionId,
-                        StreamContext = streamContext,
-                        // TODO connection context is null here. Should we set it to anything?
-                        ServiceContext = _context.ServiceContext,
-                        ConnectionFeatures = streamContext.Features,
-                        MemoryPool = _context.MemoryPool,
-                        Transport = streamContext.Transport,
-                        TimeoutControl = _context.TimeoutControl,
-                        LocalEndPoint = streamContext.LocalEndPoint as IPEndPoint,
-                        RemoteEndPoint = streamContext.RemoteEndPoint as IPEndPoint
-                    };
+                    var httpConnectionContext = new Http3StreamContext(
+                        streamContext.ConnectionId,
+                        protocols: default,
+                        connectionContext: null!, // TODO connection context is null here. Should we set it to anything?
+                        _context.ServiceContext,
+                        streamContext.Features,
+                        _context.MemoryPool,
+                        streamContext.LocalEndPoint as IPEndPoint,
+                        streamContext.RemoteEndPoint as IPEndPoint,
+                        streamContext.Transport,
+                        streamContext);
+                    httpConnectionContext.TimeoutControl = _context.TimeoutControl;
 
                     if (!quicStreamFeature.CanWrite)
                     {
@@ -269,7 +268,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             }
         }
 
-        private async ValueTask CreateControlStream<TContext>(IHttpApplication<TContext> application)
+        private async ValueTask CreateControlStream<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             var stream = await CreateNewUnidirectionalStreamAsync(application);
             ControlStream = stream;
@@ -277,38 +276,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             await stream.SendSettingsFrameAsync();
         }
 
-        private async ValueTask CreateEncoderStream<TContext>(IHttpApplication<TContext> application)
+        private async ValueTask CreateEncoderStream<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             var stream = await CreateNewUnidirectionalStreamAsync(application);
             EncoderStream = stream;
             await stream.SendStreamIdAsync(id: 2);
         }
 
-        private async ValueTask CreateDecoderStream<TContext>(IHttpApplication<TContext> application)
+        private async ValueTask CreateDecoderStream<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             var stream = await CreateNewUnidirectionalStreamAsync(application);
             DecoderStream = stream;
             await stream.SendStreamIdAsync(id: 3);
         }
 
-        private async ValueTask<Http3ControlStream> CreateNewUnidirectionalStreamAsync<TContext>(IHttpApplication<TContext> application)
+        private async ValueTask<Http3ControlStream> CreateNewUnidirectionalStreamAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             var features = new FeatureCollection();
             features.Set<IStreamDirectionFeature>(new DefaultStreamDirectionFeature(canRead: false, canWrite: true));
             var streamContext = await _multiplexedContext.ConnectAsync(features);
-            var httpConnectionContext = new Http3StreamContext
-            {
-                //ConnectionId = "", TODO getting stream ID from stream that isn't started throws an exception.
-                StreamContext = streamContext,
-                Protocols = HttpProtocols.Http3,
-                ServiceContext = _context.ServiceContext,
-                ConnectionFeatures = streamContext.Features,
-                MemoryPool = _context.MemoryPool,
-                Transport = streamContext.Transport,
-                TimeoutControl = _context.TimeoutControl,
-                LocalEndPoint = streamContext.LocalEndPoint as IPEndPoint,
-                RemoteEndPoint = streamContext.RemoteEndPoint as IPEndPoint
-            };
+            var httpConnectionContext = new Http3StreamContext(
+                connectionId: null!, // TODO getting stream ID from stream that isn't started throws an exception.
+                HttpProtocols.Http3,
+                connectionContext: null!, // TODO connection context is null here. Should we set it to anything?
+                _context.ServiceContext,
+                streamContext.Features,
+                _context.MemoryPool,
+                streamContext.LocalEndPoint as IPEndPoint,
+                streamContext.RemoteEndPoint as IPEndPoint,
+                streamContext.Transport,
+                streamContext);
+            httpConnectionContext.TimeoutControl = _context.TimeoutControl;
 
             return new Http3ControlStream<TContext>(application, this, httpConnectionContext);
         }

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Http3/Http3ControlStream.cs

@@ -133,7 +133,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 
         internal async ValueTask SendSettingsFrameAsync()
         {
-            await _frameWriter.WriteSettingsAsync(null);
+            await _frameWriter.WriteSettingsAsync(new List<Http3PeerSettings>());
         }
 
         private async ValueTask<long> TryReadStreamIdAsync()
@@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             return -1;
         }
 
-        public async Task ProcessRequestAsync<TContext>(IHttpApplication<TContext> application)
+        public async Task ProcessRequestAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
             var streamType = await TryReadStreamIdAsync();
 
@@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             }
 
             _haveReceivedSettingsFrame = true;
-            using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((Http3ControlStream)state).OnStreamClosed(), this);
+            using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((Http3ControlStream)state!).OnStreamClosed(), this);
 
             while (true)
             {

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http3/Http3ControlStreamOfT.cs

@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Hosting.Server.Abstractions;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 {
-    internal sealed class Http3ControlStream<TContext> : Http3ControlStream, IHostContextContainer<TContext>
+    internal sealed class Http3ControlStream<TContext> : Http3ControlStream, IHostContextContainer<TContext> where TContext : notnull
     {
         private readonly IHttpApplication<TContext> _application;
 
@@ -21,6 +21,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
         }
 
         // Pooled Host context
-        TContext IHostContextContainer<TContext>.HostContext { get; set; }
+        TContext? IHostContextContainer<TContext>.HostContext { get; set; }
     }
 }

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Http3/Http3FrameWriter.cs

@@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
         private readonly PipeWriter _outputWriter;
         private readonly ConnectionContext _connectionContext;
         private readonly ITimeoutControl _timeoutControl;
-        private readonly MinDataRate _minResponseDataRate;
+        private readonly MinDataRate? _minResponseDataRate;
         private readonly MemoryPool<byte> _memoryPool;
         private readonly IKestrelTrace _log;
         private readonly Http3RawFrame _outgoingFrame;
@@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 
         //private int _unflushedBytes;
 
-        public Http3FrameWriter(PipeWriter output, ConnectionContext connectionContext, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, MemoryPool<byte> memoryPool, IKestrelTrace log)
+        public Http3FrameWriter(PipeWriter output, ConnectionContext connectionContext, ITimeoutControl timeoutControl, MinDataRate? minResponseDataRate, string connectionId, MemoryPool<byte> memoryPool, IKestrelTrace log)
         {
             _outputWriter = output;
             _connectionContext = connectionContext;
@@ -239,7 +239,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             return _flusher.FlushAsync(_minResponseDataRate, bytesWritten);
         }
 
-        public ValueTask<FlushResult> FlushAsync(IHttpOutputAborter outputAborter, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FlushAsync(IHttpOutputAborter? outputAborter, CancellationToken cancellationToken)
         {
             lock (_writeLock)
             {

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http3/Http3MessageBody.cs

@@ -90,13 +90,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             return _readResult;
         }
 
-        public override void Complete(Exception exception)
+        public override void Complete(Exception? exception)
         {
             _context.ReportApplicationError(exception);
             _context.RequestBodyPipe.Reader.Complete();
         }
 
-        public override ValueTask CompleteAsync(Exception exception)
+        public override ValueTask CompleteAsync(Exception? exception)
         {
             _context.ReportApplicationError(exception);
             return _context.RequestBodyPipe.Reader.CompleteAsync();

+ 4 - 4
src/Servers/Kestrel/Core/src/Internal/Http3/Http3OutputProducer.cs

@@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
         private bool _completed;
         private bool _disposed;
         private bool _suffixSent;
-        private IMemoryOwner<byte> _fakeMemoryOwner;
+        private IMemoryOwner<byte>? _fakeMemoryOwner;
 
         public Http3OutputProducer(
              Http3FrameWriter frameWriter,
@@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             }
         }
 
-        public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FirstWriteAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
         {
             lock (_dataWriterLock)
             {
@@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             }
         }
 
-        public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
+        public ValueTask<FlushResult> FirstWriteChunkedAsync(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, ReadOnlySpan<byte> data, CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }
@@ -296,7 +296,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             }
         }
 
-        public void WriteResponseHeaders(int statusCode, string reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appCompleted)
+        public void WriteResponseHeaders(int statusCode, string? reasonPhrase, HttpResponseHeaders responseHeaders, bool autoChunk, bool appCompleted)
         {
             lock (_dataWriterLock)
             {

+ 10 - 8
src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs

@@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 
         private readonly Http3Connection _http3Connection;
         private bool _receivedHeaders;
-        private TaskCompletionSource _appCompleted;
+        private TaskCompletionSource? _appCompleted;
 
         public Pipe RequestBodyPipe { get; }
 
@@ -287,6 +287,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 
         public void HandleReadDataRateTimeout()
         {
+            Debug.Assert(Limits.MinRequestBodyDataRate != null);
+
             Log.RequestBodyMinimumDataRateNotSatisfied(ConnectionId, null, Limits.MinRequestBodyDataRate.BytesPerSecond);
             Abort(new ConnectionAbortedException(CoreStrings.BadRequest_RequestBodyTimeout), Http3ErrorCode.RequestRejected);
         }
@@ -321,9 +323,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             return false;
         }
 
-        public async Task ProcessRequestAsync<TContext>(IHttpApplication<TContext> application)
+        public async Task ProcessRequestAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull
         {
-            Exception error = null;
+            Exception? error = null;
 
             try
             {
@@ -371,7 +373,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             finally
             {
                 var streamError = error as ConnectionAbortedException
-                    ?? new ConnectionAbortedException("The stream has completed.", error);
+                    ?? new ConnectionAbortedException("The stream has completed.", error!);
 
                 await Input.CompleteAsync();
 
@@ -414,7 +416,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             return RequestBodyPipe.Writer.CompleteAsync();
         }
 
-        private Task ProcessHttp3Stream<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload)
+        private Task ProcessHttp3Stream<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload) where TContext : notnull
         {
             switch (_incomingFrame.Type)
             {
@@ -441,7 +443,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             return Task.CompletedTask;
         }
 
-        private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application, ReadOnlySequence<byte> payload)
+        private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application, ReadOnlySequence<byte> payload) where TContext : notnull
         {
             QPackDecoder.Decode(payload, handler: this);
 
@@ -593,7 +595,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
             }
 
             // Approximate MaxRequestLineSize by totaling the required pseudo header field lengths.
-            var requestLineLength = _methodText.Length + Scheme.Length + hostText.Length + path.Length;
+            var requestLineLength = _methodText!.Length + Scheme!.Length + hostText.Length + path.Length;
             if (requestLineLength > ServerOptions.Limits.MaxRequestLineSize)
             {
                 Abort(new ConnectionAbortedException(CoreStrings.BadRequest_RequestLineTooLong), Http3ErrorCode.ProtocolError);
@@ -710,7 +712,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
                     pathBuffer[i] = (byte)ch;
                 }
 
-                Path = PathNormalizer.DecodePath(pathBuffer, pathEncoded, RawTarget, QueryString.Length);
+                Path = PathNormalizer.DecodePath(pathBuffer, pathEncoded, RawTarget!, QueryString!.Length);
 
                 return true;
             }

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Http3/Http3StreamOfT.cs

@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
 {
-    class Http3Stream<TContext> : Http3Stream, IHostContextContainer<TContext>
+    class Http3Stream<TContext> : Http3Stream, IHostContextContainer<TContext> where TContext : notnull
     {
         private readonly IHttpApplication<TContext> _application;
 
@@ -31,6 +31,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
         }
 
         // Pooled Host context
-        TContext IHostContextContainer<TContext>.HostContext { get; set; }
+        TContext? IHostContextContainer<TContext>.HostContext { get; set; }
     }
 }

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Http3/QPack/EncoderStreamReader.cs

@@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3.QPack
         private readonly byte[] _stringOctets;
         private readonly byte[] _headerNameOctets;
         private readonly byte[] _headerValueOctets;
-        private byte[] _headerName;
+        private byte[]? _headerName;
         private int _headerNameLength;
         private int _headerValueLength;
         private int _stringLength;

+ 27 - 8
src/Servers/Kestrel/Core/src/Internal/Http3ConnectionContext.cs

@@ -12,13 +12,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
     internal class Http3ConnectionContext
     {
-        public string ConnectionId { get; set; }
-        public MultiplexedConnectionContext ConnectionContext { get; set; }
-        public ServiceContext ServiceContext { get; set; }
-        public IFeatureCollection ConnectionFeatures { get; set; }
-        public MemoryPool<byte> MemoryPool { get; set; }
-        public IPEndPoint LocalEndPoint { get; set; }
-        public IPEndPoint RemoteEndPoint { get; set; }
-        public ITimeoutControl TimeoutControl { get; set; }
+        public Http3ConnectionContext(
+            string connectionId,
+            MultiplexedConnectionContext connectionContext,
+            ServiceContext serviceContext,
+            IFeatureCollection connectionFeatures,
+            MemoryPool<byte> memoryPool,
+            IPEndPoint? localEndPoint,
+            IPEndPoint? remoteEndPoint)
+        {
+            ConnectionId = connectionId;
+            ConnectionContext = connectionContext;
+            ServiceContext = serviceContext;
+            ConnectionFeatures = connectionFeatures;
+            MemoryPool = memoryPool;
+            LocalEndPoint = localEndPoint;
+            RemoteEndPoint = remoteEndPoint;
+        }
+
+        public string ConnectionId { get; }
+        public MultiplexedConnectionContext ConnectionContext { get; }
+        public ServiceContext ServiceContext { get; }
+        public IFeatureCollection ConnectionFeatures { get; }
+        public MemoryPool<byte> MemoryPool { get; }
+        public IPEndPoint? LocalEndPoint { get; }
+        public IPEndPoint? RemoteEndPoint { get; }
+
+        public ITimeoutControl TimeoutControl { get; set; } = default!; // Always set by HttpConnection
     }
 }

+ 20 - 1
src/Servers/Kestrel/Core/src/Internal/Http3StreamContext.cs

@@ -1,12 +1,31 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Buffers;
+using System.IO.Pipelines;
+using System.Net;
 using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Http.Features;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
     internal class Http3StreamContext : HttpConnectionContext
     {
-        public ConnectionContext StreamContext { get; set; }
+        public Http3StreamContext(
+            string connectionId,
+            HttpProtocols protocols,
+            ConnectionContext connectionContext,
+            ServiceContext serviceContext,
+            IFeatureCollection connectionFeatures,
+            MemoryPool<byte> memoryPool,
+            IPEndPoint? localEndPoint,
+            IPEndPoint? remoteEndPoint,
+            IDuplexPipe transport,
+            ConnectionContext streamContext) : base(connectionId, protocols, connectionContext, serviceContext, connectionFeatures, memoryPool, localEndPoint, remoteEndPoint, transport)
+        {
+            StreamContext = streamContext;
+        }
+
+        public ConnectionContext StreamContext { get; }
     }
 }

+ 14 - 14
src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs

@@ -29,8 +29,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         private readonly object _protocolSelectionLock = new object();
         private ProtocolSelectionState _protocolSelectionState = ProtocolSelectionState.Initializing;
-        private IRequestProcessor _requestProcessor;
-        private Http1Connection _http1Connection;
+        private IRequestProcessor? _requestProcessor;
+        private Http1Connection? _http1Connection;
 
         public HttpConnection(HttpConnectionContext context)
         {
@@ -45,14 +45,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         private IKestrelTrace Log => _context.ServiceContext.Log;
 
-        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
+        public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication) where TContext : notnull
         {
             try
             {
                 // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs.
                 _timeoutControl.Initialize(_systemClock.UtcNowTicks);
 
-                IRequestProcessor requestProcessor = null;
+                IRequestProcessor? requestProcessor = null;
 
                 switch (SelectProtocol())
                 {
@@ -95,10 +95,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
                     connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this);
 
                     // Register for graceful shutdown of the server
-                    using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this);
+                    using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state!).StopProcessingNextRequest(), this);
 
                     // Register for connection close
-                    using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state).OnConnectionClosed(), this);
+                    using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state!).OnConnectionClosed(), this);
 
                     await requestProcessor.ProcessRequestsAsync(httpApplication);
                 }
@@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             switch (previousState)
             {
                 case ProtocolSelectionState.Selected:
-                    _requestProcessor.StopProcessingNextRequest();
+                    _requestProcessor!.StopProcessingNextRequest();
                     break;
                 case ProtocolSelectionState.Aborted:
                     break;
@@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             switch (previousState)
             {
                 case ProtocolSelectionState.Selected:
-                    _requestProcessor.OnInputOrOutputCompleted();
+                    _requestProcessor!.OnInputOrOutputCompleted();
                     break;
                 case ProtocolSelectionState.Aborted:
                     break;
@@ -184,7 +184,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             switch (previousState)
             {
                 case ProtocolSelectionState.Selected:
-                    _requestProcessor.Abort(ex);
+                    _requestProcessor!.Abort(ex);
                     break;
                 case ProtocolSelectionState.Aborted:
                     break;
@@ -199,7 +199,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             var http1Enabled = (_context.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1;
             var http2Enabled = (_context.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2;
 
-            string error = null;
+            string? error = null;
 
             if (_context.Protocols == HttpProtocols.None)
             {
@@ -238,7 +238,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             // It's safe to use UtcNowUnsynchronized since Tick is called by the Heartbeat.
             var now = _systemClock.UtcNowUnsynchronized;
             _timeoutControl.Tick(now);
-            _requestProcessor.Tick(now);
+            _requestProcessor!.Tick(now);
         }
 
         public void OnTimeout(TimeoutReason reason)
@@ -248,13 +248,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             switch (reason)
             {
                 case TimeoutReason.KeepAlive:
-                    _requestProcessor.StopProcessingNextRequest();
+                    _requestProcessor!.StopProcessingNextRequest();
                     break;
                 case TimeoutReason.RequestHeaders:
-                    _requestProcessor.HandleRequestHeadersTimeout();
+                    _requestProcessor!.HandleRequestHeadersTimeout();
                     break;
                 case TimeoutReason.ReadDataRate:
-                    _requestProcessor.HandleReadDataRateTimeout();
+                    _requestProcessor!.HandleReadDataRateTimeout();
                     break;
                 case TimeoutReason.WriteDataRate:
                     Log.ResponseMinimumDataRateNotSatisfied(_context.ConnectionId, _http1Connection?.TraceIdentifier);

+ 34 - 11
src/Servers/Kestrel/Core/src/Internal/HttpConnectionContext.cs

@@ -13,16 +13,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
     internal class HttpConnectionContext
     {
-        public string ConnectionId { get; set; }
-        public HttpProtocols Protocols { get; set; }
-        public ConnectionContext ConnectionContext { get; set; }
-        public ServiceContext ServiceContext { get; set; }
-        public IFeatureCollection ConnectionFeatures { get; set; }
-        public MemoryPool<byte> MemoryPool { get; set; }
-        public IPEndPoint LocalEndPoint { get; set; }
-        public IPEndPoint RemoteEndPoint { get; set; }
-        public ITimeoutControl TimeoutControl { get; set; }
-        public IDuplexPipe Transport { get; set; }
-        public ExecutionContext InitialExecutionContext { get; set; }
+        public HttpConnectionContext(
+            string connectionId,
+            HttpProtocols protocols,
+            ConnectionContext connectionContext,
+            ServiceContext serviceContext,
+            IFeatureCollection connectionFeatures,
+            MemoryPool<byte> memoryPool,
+            IPEndPoint? localEndPoint,
+            IPEndPoint? remoteEndPoint,
+            IDuplexPipe transport)
+        {
+            ConnectionId = connectionId;
+            Protocols = protocols;
+            ConnectionContext = connectionContext;
+            ServiceContext = serviceContext;
+            ConnectionFeatures = connectionFeatures;
+            MemoryPool = memoryPool;
+            LocalEndPoint = localEndPoint;
+            RemoteEndPoint = remoteEndPoint;
+            Transport = transport;
+        }
+
+        public string ConnectionId { get; }
+        public HttpProtocols Protocols { get; }
+        public ConnectionContext ConnectionContext { get; }
+        public ServiceContext ServiceContext { get; }
+        public IFeatureCollection ConnectionFeatures { get; }
+        public MemoryPool<byte> MemoryPool { get; }
+        public IPEndPoint? LocalEndPoint { get; }
+        public IPEndPoint? RemoteEndPoint { get; }
+        public IDuplexPipe Transport { get; }
+
+        public ITimeoutControl TimeoutControl { get; set; } = default!; // Always set by HttpConnection
+        public ExecutionContext? InitialExecutionContext { get; set; }
     }
 }

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/IRequestProcessor.cs

@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
     internal interface IRequestProcessor
     {
-        Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> application);
+        Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> application) where TContext : notnull;
         void StopProcessingNextRequest();
         void HandleRequestHeadersTimeout();
         void HandleReadDataRateTimeout();
@@ -18,4 +18,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
         void Tick(DateTimeOffset now);
         void Abort(ConnectionAbortedException ex);
     }
-}
+}

+ 2 - 1
src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionReference.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics.CodeAnalysis;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
 {
@@ -23,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
 
         public string ConnectionId { get; }
 
-        public bool TryGetConnection(out KestrelConnection connection)
+        public bool TryGetConnection([NotNullWhen(true)] out KestrelConnection? connection)
         {
             return _weakReference.TryGetTarget(out connection);
         }

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Infrastructure/Disposable.cs

@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
     /// </summary>
     internal class Disposable : IDisposable
     {
-        private Action _dispose;
+        private Action? _dispose;
         private bool _disposedValue = false; // To detect redundant calls
 
         public Disposable(Action dispose)
@@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             {
                 if (disposing)
                 {
-                    _dispose.Invoke();
+                    _dispose!.Invoke();
                 }
 
                 _dispose = null;
@@ -40,4 +40,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             GC.SuppressFinalize(this);
         }
     }
-}
+}

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Infrastructure/Heartbeat.cs

@@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             _debugger = debugger;
             _trace = trace;
             _interval = Interval;
-            _timerThread = new Thread(state => ((Heartbeat)state).TimerLoop())
+            _timerThread = new Thread(state => ((Heartbeat)state!).TimerLoop())
             {
                 Name = "Kestrel Timer",
                 IsBackground = true

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.cs

@@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             }
         }
 
-        public static string GetRequestHeaderString(this ReadOnlySpan<byte> span, string name, Func<string, Encoding> encodingSelector)
+        public static string GetRequestHeaderString(this ReadOnlySpan<byte> span, string name, Func<string, Encoding?> encodingSelector)
         {
             if (ReferenceEquals(KestrelServerOptions.DefaultRequestHeaderEncodingSelector, encodingSelector))
             {
@@ -421,7 +421,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             };
         }
 
-        public static string MethodToString(HttpMethod method)
+        public static string? MethodToString(HttpMethod method)
         {
             var methodIndex = (int)method;
             var methodNames = _methodNames;
@@ -432,7 +432,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             return null;
         }
 
-        public static string SchemeToString(HttpScheme scheme)
+        public static string? SchemeToString(HttpScheme scheme)
         {
             return scheme switch
             {

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Infrastructure/IKestrelTrace.cs

@@ -51,9 +51,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
 
         void RequestBodyDrainTimedOut(string connectionId, string traceIdentifier);
 
-        void RequestBodyMinimumDataRateNotSatisfied(string connectionId, string traceIdentifier, double rate);
+        void RequestBodyMinimumDataRateNotSatisfied(string connectionId, string? traceIdentifier, double rate);
 
-        void ResponseMinimumDataRateNotSatisfied(string connectionId, string traceIdentifier);
+        void ResponseMinimumDataRateNotSatisfied(string connectionId, string? traceIdentifier);
 
         void ApplicationAbortedConnection(string connectionId, string traceIdentifier);
 

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelConnection.cs

@@ -13,10 +13,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
 {
     internal abstract class KestrelConnection : IConnectionHeartbeatFeature, IConnectionCompleteFeature, IConnectionLifetimeNotificationFeature
     {
-        private List<(Action<object> handler, object state)> _heartbeatHandlers;
+        private List<(Action<object> handler, object state)>? _heartbeatHandlers;
         private readonly object _heartbeatLock = new object();
 
-        private Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
+        private Stack<KeyValuePair<Func<object, Task>, object>>? _onCompleted;
         private bool _completed;
 
         private readonly CancellationTokenSource _connectionClosingCts = new CancellationTokenSource();
@@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             _connectionClosingCts.Dispose();
         }
 
-        protected IDisposable BeginConnectionScope(BaseConnectionContext connectionContext)
+        protected IDisposable? BeginConnectionScope(BaseConnectionContext connectionContext)
         {
             if (Logger.IsEnabled(LogLevel.Critical))
             {

+ 15 - 15
src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelEventSource.cs

@@ -17,16 +17,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
     {
         public static readonly KestrelEventSource Log = new KestrelEventSource();
 
-        private IncrementingPollingCounter _connectionsPerSecondCounter;
-        private IncrementingPollingCounter _tlsHandshakesPerSecondCounter;
-        private PollingCounter _totalConnectionsCounter;
-        private PollingCounter _currentConnectionsCounter;
-        private PollingCounter _totalTlsHandshakesCounter;
-        private PollingCounter _currentTlsHandshakesCounter;
-        private PollingCounter _failedTlsHandshakesCounter;
-        private PollingCounter _connectionQueueLengthCounter;
-        private PollingCounter _httpRequestQueueLengthCounter;
-        private PollingCounter _currrentUpgradedHttpRequestsCounter;
+        private IncrementingPollingCounter? _connectionsPerSecondCounter;
+        private IncrementingPollingCounter? _tlsHandshakesPerSecondCounter;
+        private PollingCounter? _totalConnectionsCounter;
+        private PollingCounter? _currentConnectionsCounter;
+        private PollingCounter? _totalTlsHandshakesCounter;
+        private PollingCounter? _currentTlsHandshakesCounter;
+        private PollingCounter? _failedTlsHandshakesCounter;
+        private PollingCounter? _connectionQueueLengthCounter;
+        private PollingCounter? _httpRequestQueueLengthCounter;
+        private PollingCounter? _currrentUpgradedHttpRequestsCounter;
 
         private long _totalConnections;
         private long _currentConnections;
@@ -68,8 +68,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
         [MethodImpl(MethodImplOptions.NoInlining)]
         [Event(1, Level = EventLevel.Informational)]
         private void ConnectionStart(string connectionId,
-            string localEndPoint,
-            string remoteEndPoint)
+            string? localEndPoint,
+            string? remoteEndPoint)
         {
             WriteEvent(
                 1,
@@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             // avoid allocating the trace identifier unless logging is enabled
             if (IsEnabled())
             {
-                RequestStart(httpProtocol.ConnectionIdFeature, httpProtocol.TraceIdentifier, httpProtocol.HttpVersion, httpProtocol.Path, httpProtocol.MethodText);
+                RequestStart(httpProtocol.ConnectionIdFeature, httpProtocol.TraceIdentifier, httpProtocol.HttpVersion, httpProtocol.Path!, httpProtocol.MethodText);
             }
         }
 
@@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             // avoid allocating the trace identifier unless logging is enabled
             if (IsEnabled())
             {
-                RequestStop(httpProtocol.ConnectionIdFeature, httpProtocol.TraceIdentifier, httpProtocol.HttpVersion, httpProtocol.Path, httpProtocol.MethodText);
+                RequestStop(httpProtocol.ConnectionIdFeature, httpProtocol.TraceIdentifier, httpProtocol.HttpVersion, httpProtocol.Path!, httpProtocol.MethodText);
             }
         }
 
@@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
         }
 
         [NonEvent]
-        public void TlsHandshakeStop(BaseConnectionContext connectionContext, TlsConnectionFeature feature)
+        public void TlsHandshakeStop(BaseConnectionContext connectionContext, TlsConnectionFeature? feature)
         {
             Interlocked.Decrement(ref _currentTlsHandshakes);
             if (IsEnabled())

+ 30 - 30
src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelTrace.cs

@@ -11,62 +11,62 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
 {
     internal class KestrelTrace : IKestrelTrace
     {
-        private static readonly Action<ILogger, string, Exception> _connectionStart =
+        private static readonly Action<ILogger, string, Exception?> _connectionStart =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(1, "ConnectionStart"), @"Connection id ""{ConnectionId}"" started.");
 
-        private static readonly Action<ILogger, string, Exception> _connectionStop =
+        private static readonly Action<ILogger, string, Exception?> _connectionStop =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(2, "ConnectionStop"), @"Connection id ""{ConnectionId}"" stopped.");
 
-        private static readonly Action<ILogger, string, Exception> _connectionPause =
+        private static readonly Action<ILogger, string, Exception?> _connectionPause =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(4, "ConnectionPause"), @"Connection id ""{ConnectionId}"" paused.");
 
-        private static readonly Action<ILogger, string, Exception> _connectionResume =
+        private static readonly Action<ILogger, string, Exception?> _connectionResume =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(5, "ConnectionResume"), @"Connection id ""{ConnectionId}"" resumed.");
 
-        private static readonly Action<ILogger, string, Exception> _connectionKeepAlive =
+        private static readonly Action<ILogger, string, Exception?> _connectionKeepAlive =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(9, "ConnectionKeepAlive"), @"Connection id ""{ConnectionId}"" completed keep alive response.");
 
-        private static readonly Action<ILogger, string, Exception> _connectionDisconnect =
+        private static readonly Action<ILogger, string, Exception?> _connectionDisconnect =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(10, "ConnectionDisconnect"), @"Connection id ""{ConnectionId}"" disconnecting.");
 
         private static readonly Action<ILogger, string, string, Exception> _applicationError =
             LoggerMessage.Define<string, string>(LogLevel.Error, new EventId(13, "ApplicationError"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": An unhandled exception was thrown by the application.");
 
-        private static readonly Action<ILogger, Exception> _notAllConnectionsClosedGracefully =
+        private static readonly Action<ILogger, Exception?> _notAllConnectionsClosedGracefully =
             LoggerMessage.Define(LogLevel.Debug, new EventId(16, "NotAllConnectionsClosedGracefully"), "Some connections failed to close gracefully during server shutdown.");
 
         private static readonly Action<ILogger, string, string, Exception> _connectionBadRequest =
             LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(17, "ConnectionBadRequest"), @"Connection id ""{ConnectionId}"" bad request data: ""{message}""");
 
-        private static readonly Action<ILogger, string, long, Exception> _connectionHeadResponseBodyWrite =
+        private static readonly Action<ILogger, string, long, Exception?> _connectionHeadResponseBodyWrite =
             LoggerMessage.Define<string, long>(LogLevel.Debug, new EventId(18, "ConnectionHeadResponseBodyWrite"), @"Connection id ""{ConnectionId}"" write of ""{count}"" body bytes to non-body HEAD response.");
 
         private static readonly Action<ILogger, string, Exception> _requestProcessingError =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(20, "RequestProcessingError"), @"Connection id ""{ConnectionId}"" request processing ended abnormally.");
 
-        private static readonly Action<ILogger, Exception> _notAllConnectionsAborted =
+        private static readonly Action<ILogger, Exception?> _notAllConnectionsAborted =
             LoggerMessage.Define(LogLevel.Debug, new EventId(21, "NotAllConnectionsAborted"), "Some connections failed to abort during server shutdown.");
 
-        private static readonly Action<ILogger, TimeSpan, TimeSpan, DateTimeOffset, Exception> _heartbeatSlow =
+        private static readonly Action<ILogger, TimeSpan, TimeSpan, DateTimeOffset, Exception?> _heartbeatSlow =
             LoggerMessage.Define<TimeSpan, TimeSpan, DateTimeOffset>(LogLevel.Warning, new EventId(22, "HeartbeatSlow"), @"As of ""{now}"", the heartbeat has been running for ""{heartbeatDuration}"" which is longer than ""{interval}"". This could be caused by thread pool starvation.");
 
-        private static readonly Action<ILogger, string, Exception> _applicationNeverCompleted =
+        private static readonly Action<ILogger, string, Exception?> _applicationNeverCompleted =
             LoggerMessage.Define<string>(LogLevel.Critical, new EventId(23, "ApplicationNeverCompleted"), @"Connection id ""{ConnectionId}"" application never completed");
 
-        private static readonly Action<ILogger, string, Exception> _connectionRejected =
+        private static readonly Action<ILogger, string, Exception?> _connectionRejected =
             LoggerMessage.Define<string>(LogLevel.Warning, new EventId(24, "ConnectionRejected"), @"Connection id ""{ConnectionId}"" rejected because the maximum number of concurrent connections has been reached.");
 
-        private static readonly Action<ILogger, string, string, Exception> _requestBodyStart =
+        private static readonly Action<ILogger, string, string, Exception?> _requestBodyStart =
             LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(25, "RequestBodyStart"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": started reading request body.");
 
-        private static readonly Action<ILogger, string, string, Exception> _requestBodyDone =
+        private static readonly Action<ILogger, string, string, Exception?> _requestBodyDone =
             LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(26, "RequestBodyDone"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": done reading request body.");
 
-        private static readonly Action<ILogger, string, string, double, Exception> _requestBodyMinimumDataRateNotSatisfied =
-            LoggerMessage.Define<string, string, double>(LogLevel.Debug, new EventId(27, "RequestBodyMinimumDataRateNotSatisfied"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": the request timed out because it was not sent by the client at a minimum of {Rate} bytes/second.");
+        private static readonly Action<ILogger, string, string?, double, Exception?> _requestBodyMinimumDataRateNotSatisfied =
+            LoggerMessage.Define<string, string?, double>(LogLevel.Debug, new EventId(27, "RequestBodyMinimumDataRateNotSatisfied"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": the request timed out because it was not sent by the client at a minimum of {Rate} bytes/second.");
 
-        private static readonly Action<ILogger, string, string, Exception> _responseMinimumDataRateNotSatisfied =
-            LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(28, "ResponseMinimumDataRateNotSatisfied"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": the connection was closed because the response was not read by the client at the specified minimum data rate.");
+        private static readonly Action<ILogger, string, string?, Exception?> _responseMinimumDataRateNotSatisfied =
+            LoggerMessage.Define<string, string?>(LogLevel.Debug, new EventId(28, "ResponseMinimumDataRateNotSatisfied"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": the connection was closed because the response was not read by the client at the specified minimum data rate.");
 
         private static readonly Action<ILogger, string, Exception> _http2ConnectionError =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(29, "Http2ConnectionError"), @"Connection id ""{ConnectionId}"": HTTP/2 connection error.");
@@ -77,32 +77,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
         private static readonly Action<ILogger, string, int, Exception> _hpackDecodingError =
             LoggerMessage.Define<string, int>(LogLevel.Debug, new EventId(31, "HPackDecodingError"), @"Connection id ""{ConnectionId}"": HPACK decoding error while decoding headers for stream ID {StreamId}.");
 
-        private static readonly Action<ILogger, string, string, Exception> _requestBodyNotEntirelyRead =
+        private static readonly Action<ILogger, string, string, Exception?> _requestBodyNotEntirelyRead =
             LoggerMessage.Define<string, string>(LogLevel.Information, new EventId(32, "RequestBodyNotEntirelyRead"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": the application completed without reading the entire request body.");
 
-        private static readonly Action<ILogger, string, string, Exception> _requestBodyDrainTimedOut =
+        private static readonly Action<ILogger, string, string, Exception?> _requestBodyDrainTimedOut =
             LoggerMessage.Define<string, string>(LogLevel.Information, new EventId(33, "RequestBodyDrainTimedOut"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": automatic draining of the request body timed out after taking over 5 seconds.");
 
-        private static readonly Action<ILogger, string, string, Exception> _applicationAbortedConnection =
+        private static readonly Action<ILogger, string, string, Exception?> _applicationAbortedConnection =
             LoggerMessage.Define<string, string>(LogLevel.Information, new EventId(34, "RequestBodyDrainTimedOut"), @"Connection id ""{ConnectionId}"", Request id ""{TraceIdentifier}"": the application aborted the connection.");
 
         private static readonly Action<ILogger, string, Http2ErrorCode, Exception> _http2StreamResetError =
             LoggerMessage.Define<string, Http2ErrorCode>(LogLevel.Debug, new EventId(35, "Http2StreamResetAbort"),
                 @"Trace id ""{TraceIdentifier}"": HTTP/2 stream error ""{error}"". A Reset is being sent to the stream.");
 
-        private static readonly Action<ILogger, string, Exception> _http2ConnectionClosing =
+        private static readonly Action<ILogger, string, Exception?> _http2ConnectionClosing =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(36, "Http2ConnectionClosing"),
                 @"Connection id ""{ConnectionId}"" is closing.");
 
-        private static readonly Action<ILogger, string, int, Exception> _http2ConnectionClosed =
+        private static readonly Action<ILogger, string, int, Exception?> _http2ConnectionClosed =
             LoggerMessage.Define<string, int>(LogLevel.Debug, new EventId(36, "Http2ConnectionClosed"),
                 @"Connection id ""{ConnectionId}"" is closed. The last processed stream ID was {HighestOpenedStreamId}.");
 
-        private static readonly Action<ILogger, string, Http2FrameType, int, int, object, Exception> _http2FrameReceived =
+        private static readonly Action<ILogger, string, Http2FrameType, int, int, object, Exception?> _http2FrameReceived =
             LoggerMessage.Define<string, Http2FrameType, int, int, object>(LogLevel.Trace, new EventId(37, "Http2FrameReceived"),
                 @"Connection id ""{ConnectionId}"" received {type} frame for stream ID {id} with length {length} and flags {flags}");
 
-        private static readonly Action<ILogger, string, Http2FrameType, int, int, object, Exception> _http2FrameSending =
+        private static readonly Action<ILogger, string, Http2FrameType, int, int, object, Exception?> _http2FrameSending =
             LoggerMessage.Define<string, Http2FrameType, int, int, object>(LogLevel.Trace, new EventId(37, "Http2FrameReceived"),
                 @"Connection id ""{ConnectionId}"" sending {type} frame for stream ID {id} with length {length} and flags {flags}");
 
@@ -110,14 +110,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             LoggerMessage.Define<string, int>(LogLevel.Information, new EventId(38, "HPackEncodingError"),
                 @"Connection id ""{ConnectionId}"": HPACK encoding error while encoding headers for stream ID {StreamId}.");
 
-        private static readonly Action<ILogger, string, Exception> _connectionAccepted =
+        private static readonly Action<ILogger, string, Exception?> _connectionAccepted =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(39, "ConnectionAccepted"), @"Connection id ""{ConnectionId}"" accepted.");
 
-        private static readonly Action<ILogger, string, Exception> _http2MaxConcurrentStreamsReached =
+        private static readonly Action<ILogger, string, Exception?> _http2MaxConcurrentStreamsReached =
             LoggerMessage.Define<string>(LogLevel.Debug, new EventId(40, "Http2MaxConcurrentStreamsReached"),
                 @"Connection id ""{ConnectionId}"" reached the maximum number of concurrent HTTP/2 streams allowed.");
 
-        private static readonly Action<ILogger, Exception> _invalidResponseHeaderRemoved =
+        private static readonly Action<ILogger, Exception?> _invalidResponseHeaderRemoved =
             LoggerMessage.Define(LogLevel.Warning, new EventId(41, "InvalidResponseHeaderRemoved"),
                 "One or more of the following response headers have been removed because they are invalid for HTTP/2 and HTTP/3 responses: 'Connection', 'Transfer-Encoding', 'Keep-Alive', 'Upgrade' and 'Proxy-Connection'.");
 
@@ -218,7 +218,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             _requestBodyDone(_logger, connectionId, traceIdentifier, null);
         }
 
-        public virtual void RequestBodyMinimumDataRateNotSatisfied(string connectionId, string traceIdentifier, double rate)
+        public virtual void RequestBodyMinimumDataRateNotSatisfied(string connectionId, string? traceIdentifier, double rate)
         {
             _requestBodyMinimumDataRateNotSatisfied(_logger, connectionId, traceIdentifier, rate, null);
         }
@@ -233,7 +233,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
             _requestBodyDrainTimedOut(_logger, connectionId, traceIdentifier, null);
         }
 
-        public virtual void ResponseMinimumDataRateNotSatisfied(string connectionId, string traceIdentifier)
+        public virtual void ResponseMinimumDataRateNotSatisfied(string connectionId, string? traceIdentifier)
         {
             _responseMinimumDataRateNotSatisfied(_logger, connectionId, traceIdentifier, null);
         }

+ 7 - 5
src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/BufferSegment.cs

@@ -10,8 +10,8 @@ namespace System.IO.Pipelines
     // Copied from https://github.com/dotnet/corefx/blob/de3902bb56f1254ec1af4bf7d092fc2c048734cc/src/System.IO.Pipelines/src/System/IO/Pipelines/BufferSegment.cs
     internal sealed class BufferSegment : ReadOnlySequenceSegment<byte>
     {
-        private object _memoryOwner;
-        private BufferSegment _next;
+        private object? _memoryOwner;
+        private BufferSegment? _next;
         private int _end;
 
         /// <summary>
@@ -37,7 +37,7 @@ namespace System.IO.Pipelines
         /// working memory. The "active" memory is grown when bytes are copied in, End is increased, and Next is assigned. The "active"
         /// memory is shrunk when bytes are consumed, Start is increased, and blocks are returned to the pool.
         /// </summary>
-        public BufferSegment NextSegment
+        public BufferSegment? NextSegment
         {
             get => _next;
             set
@@ -61,6 +61,8 @@ namespace System.IO.Pipelines
 
         public void ResetMemory()
         {
+            Debug.Assert(_memoryOwner != null);
+
             if (_memoryOwner is IMemoryOwner<byte> owner)
             {
                 owner.Dispose();
@@ -83,7 +85,7 @@ namespace System.IO.Pipelines
         }
 
         // Exposed for testing
-        internal object MemoryOwner => _memoryOwner;
+        internal object? MemoryOwner => _memoryOwner;
 
         public Memory<byte> AvailableMemory { get; private set; }
 
@@ -106,7 +108,7 @@ namespace System.IO.Pipelines
 
             while (segment.Next != null)
             {
-                segment.NextSegment.RunningIndex = segment.RunningIndex + segment.Length;
+                segment.NextSegment!.RunningIndex = segment.RunningIndex + segment.Length;
                 segment = segment.NextSegment;
             }
         }

+ 2 - 1
src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/BufferSegmentStack.cs

@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 
 namespace System.IO.Pipelines
@@ -19,7 +20,7 @@ namespace System.IO.Pipelines
 
         public int Count => _size;
 
-        public bool TryPop(out BufferSegment result)
+        public bool TryPop([NotNullWhen(true)] out BufferSegment? result)
         {
             int size = _size - 1;
             SegmentAsValueType[] array = _array;

+ 15 - 8
src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/ConcurrentPipeWriter.cs

@@ -29,8 +29,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
         private readonly MemoryPool<byte> _pool;
         private readonly BufferSegmentStack _bufferSegmentPool = new BufferSegmentStack(InitialSegmentPoolSize);
 
-        private BufferSegment _head;
-        private BufferSegment _tail;
+        private BufferSegment? _head;
+        private BufferSegment? _tail;
         private Memory<byte> _tailMemory;
         private int _tailBytesBuffered;
         private long _bytesBuffered;
@@ -44,14 +44,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
         // In either case, we need to manually append buffer segments until the loop in the current or next call to FlushAsync()
         // flushes all the buffers putting the ConcurrentPipeWriter back into passthrough mode.
         // The manual buffer appending logic is borrowed from corefx's StreamPipeWriter.
-        private TaskCompletionSource<FlushResult> _currentFlushTcs;
+        private TaskCompletionSource<FlushResult>? _currentFlushTcs;
         private bool _bufferedWritePending;
 
         // We're trusting the Http2FrameWriter and Http1OutputProducer to not call into the PipeWriter after calling Abort() or Complete().
         // If an Abort() is called while a flush is in progress, we clean up after the next flush completes, and don't flush again.
         private bool _aborted;
         // If an Complete() is called while a flush is in progress, we clean up after the flush loop completes, and call Complete() on the inner PipeWriter.
-        private Exception _completeException;
+        private Exception? _completeException;
 
         public ConcurrentPipeWriter(PipeWriter innerPipeWriter, MemoryPool<byte> pool, object sync)
         {
@@ -163,6 +163,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
 
                         if (flushResult.IsCanceled)
                         {
+                            Debug.Assert(_currentFlushTcs != null);
+
                             // Complete anyone currently awaiting a flush with the canceled FlushResult since CancelPendingFlush() was called.
                             _currentFlushTcs.SetResult(flushResult);
                             // Reset _currentFlushTcs, so we don't enter passthrough mode while we're still flushing.
@@ -191,7 +193,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
         }
 
         // To return all the segments without completing the inner pipe, call Abort().
-        public override void Complete(Exception exception = null)
+        public override void Complete(Exception? exception = null)
         {
             // Store the exception or sentinel in a field so that if a flush is ongoing, we call the
             // inner Complete() method with the correct exception or lack thereof once the flush loop ends.
@@ -223,7 +225,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
 
         private void CleanupSegmentsUnsynchronized()
         {
-            BufferSegment segment = _head;
+            BufferSegment? segment = _head;
             while (segment != null)
             {
                 BufferSegment returnSegment = segment;
@@ -238,6 +240,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
 
         private void CopyAndReturnSegmentsUnsynchronized()
         {
+            Debug.Assert(_tail != null);
+
             // Update any buffered data
             _tail.End += _tailBytesBuffered;
             _tailBytesBuffered = 0;
@@ -275,7 +279,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
             _bytesBuffered = 0;
         }
 
-        private void CompleteFlushUnsynchronized(FlushResult flushResult, Exception flushEx)
+        private void CompleteFlushUnsynchronized(FlushResult flushResult, Exception? flushEx)
         {
             // Ensure all blocks are returned prior to the last call to FlushAsync() completing.
             if (_completeException != null || _aborted)
@@ -292,6 +296,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
                 _innerPipeWriter.Complete(_completeException);
             }
 
+            Debug.Assert(_currentFlushTcs != null);
             if (flushEx != null)
             {
                 _currentFlushTcs.SetException(flushEx);
@@ -324,6 +329,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
 
                 if (bytesLeftInBuffer == 0 || bytesLeftInBuffer < sizeHint)
                 {
+                    Debug.Assert(_tail != null);
+
                     if (_tailBytesBuffered > 0)
                     {
                         // Flush buffered data to the segment
@@ -361,7 +368,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
 
         private BufferSegment CreateSegmentUnsynchronized()
         {
-            if (_bufferSegmentPool.TryPop(out BufferSegment segment))
+            if (_bufferSegmentPool.TryPop(out var segment))
             {
                 return segment;
             }

+ 5 - 5
src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/TimingPipeFlusher.cs

@@ -20,12 +20,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
     internal class TimingPipeFlusher
     {
         private readonly PipeWriter _writer;
-        private readonly ITimeoutControl _timeoutControl;
+        private readonly ITimeoutControl? _timeoutControl;
         private readonly IKestrelTrace _log;
 
         public TimingPipeFlusher(
             PipeWriter writer,
-            ITimeoutControl timeoutControl,
+            ITimeoutControl? timeoutControl,
             IKestrelTrace log)
         {
             _writer = writer;
@@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
 
             if (minRate is object)
             {
-                _timeoutControl.BytesWrittenToBuffer(minRate, count);
+                _timeoutControl!.BytesWrittenToBuffer(minRate, count);
             }
 
             if (pipeFlushTask.IsCompletedSuccessfully)
@@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
         {
             if (minRate is object)
             {
-                _timeoutControl.StartTimingWrite();
+                _timeoutControl!.StartTimingWrite();
             }
 
             try
@@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW
             {
                 if (minRate is object)
                 {
-                    _timeoutControl.StopTimingWrite();
+                    _timeoutControl!.StopTimingWrite();
                 }
 
                 cancellationToken.ThrowIfCancellationRequested();

+ 1 - 1
src/Servers/Kestrel/Core/src/Internal/Infrastructure/ThreadPoolAwaitable.cs

@@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
 
         public void OnCompleted(Action continuation)
         {
-            ThreadPool.UnsafeQueueUserWorkItem(state => ((Action)state)(), continuation);
+            ThreadPool.UnsafeQueueUserWorkItem(state => ((Action)state!)(), continuation);
         }
 
         public void UnsafeOnCompleted(Action continuation)

+ 4 - 2
src/Servers/Kestrel/Core/src/Internal/Infrastructure/TimeoutControl.cs

@@ -17,12 +17,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
         private long _timeoutTimestamp = long.MaxValue;
 
         private readonly object _readTimingLock = new object();
-        private MinDataRate _minReadRate;
+        private MinDataRate? _minReadRate;
         private bool _readTimingEnabled;
         private bool _readTimingPauseRequested;
         private long _readTimingElapsedTicks;
         private long _readTimingBytesRead;
-        private InputFlowControl _connectionInputFlowControl;
+        private InputFlowControl? _connectionInputFlowControl;
         // The following are always 0 or 1 for HTTP/1.x
         private int _concurrentIncompleteRequestBodies;
         private int _concurrentAwaitingReads;
@@ -100,6 +100,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
                 // Don't count extra time between ticks against the rate limit.
                 _readTimingElapsedTicks += Math.Min(timestamp - _lastTimestamp, Heartbeat.Interval.Ticks);
 
+                Debug.Assert(_minReadRate != null);
+
                 if (_minReadRate.BytesPerSecond > 0 && _readTimingElapsedTicks > _minReadRate.GracePeriod.Ticks)
                 {
                     var elapsedSeconds = (double)_readTimingElapsedTicks / TimeSpan.TicksPerSecond;

+ 2 - 2
src/Servers/Kestrel/Core/src/Internal/Infrastructure/TimeoutControlExtensions.cs

@@ -1,11 +1,11 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
 {
     internal static class TimeoutControlExtensions
     {
-        public static void StartDrainTimeout(this ITimeoutControl timeoutControl, MinDataRate minDataRate, long? maxResponseBufferSize)
+        public static void StartDrainTimeout(this ITimeoutControl timeoutControl, MinDataRate? minDataRate, long? maxResponseBufferSize)
         {
             // If maxResponseBufferSize has no value, there's no backpressure and we can't reasonably time out draining.
             if (minDataRate == null || maxResponseBufferSize == null)

+ 4 - 4
src/Servers/Kestrel/Core/src/Internal/Infrastructure/WrappingStream.cs

@@ -95,10 +95,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
         public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
             => _inner.CopyToAsync(destination, bufferSize, cancellationToken);
 
-        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
             => _inner.BeginRead(buffer, offset, count, callback, state);
 
-        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
             => _inner.BeginWrite(buffer, offset, count, callback, state);
 
         public override int EndRead(IAsyncResult asyncResult)
@@ -114,13 +114,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
         public override void Close()
             => _inner.Close();
 
-        public override bool Equals(object obj)
+        public override bool Equals(object? obj)
             => _inner.Equals(obj);
 
         public override int GetHashCode()
             => _inner.GetHashCode();
 
-        public override string ToString()
+        public override string? ToString()
             => _inner.ToString();
 
         protected override void Dispose(bool disposing)

+ 21 - 24
src/Servers/Kestrel/Core/src/Internal/KestrelServerImpl.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO.Pipelines;
 using System.Linq;
 using System.Threading;
@@ -25,8 +26,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
     {
         private readonly ServerAddressesFeature _serverAddresses;
         private readonly TransportManager _transportManager;
-        private readonly IConnectionListenerFactory _transportFactory;
-        private readonly IMultiplexedConnectionListenerFactory _multiplexedTransportFactory;
+        private readonly IConnectionListenerFactory? _transportFactory;
+        private readonly IMultiplexedConnectionListenerFactory? _multiplexedTransportFactory;
 
         private readonly SemaphoreSlim _bindSemaphore = new SemaphoreSlim(initialCount: 1);
         private bool _hasStarted;
@@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         private readonly CancellationTokenSource _stopCts = new CancellationTokenSource();
         private readonly TaskCompletionSource _stoppedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
 
-        private IDisposable _configChangedRegistration;
+        private IDisposable? _configChangedRegistration;
 
         public KestrelServerImpl(
             IOptions<KestrelServerOptions> options,
@@ -62,7 +63,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         // For testing
         internal KestrelServerImpl(
             IEnumerable<IConnectionListenerFactory> transportFactories,
-            IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
+            IEnumerable<IMultiplexedConnectionListenerFactory>? multiplexedFactories,
             ServiceContext serviceContext)
         {
             if (transportFactories == null)
@@ -70,7 +71,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
                 throw new ArgumentNullException(nameof(transportFactories));
             }
 
-            _transportFactory = transportFactories?.LastOrDefault();
+            _transportFactory = transportFactories.LastOrDefault();
             _multiplexedTransportFactory = multiplexedFactories?.LastOrDefault();
 
             if (_transportFactory == null && _multiplexedTransportFactory == null)
@@ -119,13 +120,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
             return new ServiceContext
             {
                 Log = trace,
-                HttpParser = new HttpParser<Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)),
                 Scheduler = PipeScheduler.ThreadPool,
+                HttpParser = new HttpParser<Http1ParsingHandler>(trace.IsEnabled(LogLevel.Information)),
                 SystemClock = heartbeatManager,
                 DateHeaderValueManager = dateHeaderValueManager,
                 ConnectionManager = connectionManager,
                 Heartbeat = heartbeat,
-                ServerOptions = serverOptions,
+                ServerOptions = serverOptions
             };
         }
 
@@ -137,9 +138,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
 
         private IKestrelTrace Trace => ServiceContext.Log;
 
-        private AddressBindContext AddressBindContext { get; set; }
+        private AddressBindContext? AddressBindContext { get; set; }
 
-        public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
+        public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) where TContext : notnull
         {
             try
             {
@@ -200,13 +201,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
                     }
                 }
 
-                AddressBindContext = new AddressBindContext
-                {
-                    ServerAddressesFeature = _serverAddresses,
-                    ServerOptions = Options,
-                    Logger = Trace,
-                    CreateBinding = OnBind,
-                };
+                AddressBindContext = new AddressBindContext(_serverAddresses, Options, Trace, OnBind);
 
                 await BindAsync(cancellationToken).ConfigureAwait(false);
             }
@@ -269,7 +264,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
                     throw new InvalidOperationException("Kestrel has already been stopped.");
                 }
 
-                IChangeToken reloadToken = null;
+                IChangeToken? reloadToken = null;
 
                 _serverAddresses.InternalCollection.PreventPublicMutation();
 
@@ -280,7 +275,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
 
                 Options.ConfigurationLoader?.Load();
 
-                await AddressBinder.BindAsync(Options.ListenOptions, AddressBindContext).ConfigureAwait(false);
+                await AddressBinder.BindAsync(Options.ListenOptions, AddressBindContext!).ConfigureAwait(false);
                 _configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await ((KestrelServerImpl)state).RebindAsync(), this);
             }
             finally
@@ -293,7 +288,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         {
             await _bindSemaphore.WaitAsync();
 
-            IChangeToken reloadToken = null;
+            IChangeToken? reloadToken = null;
 
             try
             {
@@ -302,6 +297,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
                     return;
                 }
 
+                Debug.Assert(Options.ConfigurationLoader != null, "Rebind can only happen when there is a ConfigurationLoader.");
+
                 reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken();
                 var (endpointsToStop, endpointsToStart) = Options.ConfigurationLoader.Reload();
 
@@ -309,7 +306,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
 
                 if (endpointsToStop.Count > 0)
                 {
-                    var urlsToStop = endpointsToStop.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
+                    var urlsToStop = endpointsToStop.Select(lo => lo.EndpointConfig!.Url);
                     Trace.LogInformation("Config changed. Stopping the following endpoints: '{endpoints}'", string.Join("', '", urlsToStop));
 
                     // 5 is the default value for WebHost's "shutdownTimeoutSeconds", so use that.
@@ -318,7 +315,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
 
                     // TODO: It would be nice to start binding to new endpoints immediately and reconfigured endpoints as soon
                     // as the unbinding finished for the given endpoint rather than wait for all transports to unbind first.
-                    var configsToStop = endpointsToStop.Select(lo => lo.EndpointConfig).ToList();
+                    var configsToStop = endpointsToStop.Select(lo => lo.EndpointConfig!).ToList();
                     await _transportManager.StopEndpointsAsync(configsToStop, combinedCts.Token).ConfigureAwait(false);
 
                     foreach (var listenOption in endpointsToStop)
@@ -330,7 +327,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
 
                 if (endpointsToStart.Count > 0)
                 {
-                    var urlsToStart = endpointsToStart.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
+                    var urlsToStart = endpointsToStart.Select(lo => lo.EndpointConfig!.Url);
                     Trace.LogInformation("Config changed. Starting the following endpoints: '{endpoints}'", string.Join("', '", urlsToStart));
 
                     foreach (var listenOption in endpointsToStart)
@@ -338,11 +335,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
                         try
                         {
                             // TODO: This should probably be canceled by the _stopCts too, but we don't currently support bind cancellation even in StartAsync().
-                            await listenOption.BindAsync(AddressBindContext).ConfigureAwait(false);
+                            await listenOption.BindAsync(AddressBindContext!).ConfigureAwait(false);
                         }
                         catch (Exception ex)
                         {
-                            Trace.LogCritical(0, ex, "Unable to bind to '{url}' on config reload.", listenOption.EndpointConfig.Url ?? "<unknown>");
+                            Trace.LogCritical(0, ex, "Unable to bind to '{url}' on config reload.", listenOption.EndpointConfig!.Url);
                         }
                     }
                 }

+ 8 - 8
src/Servers/Kestrel/Core/src/Internal/LoggerExtensions.cs

@@ -10,49 +10,49 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
     internal static class LoggerExtensions
     {
         // Category: DefaultHttpsProvider
-        private static readonly Action<ILogger, string, string, Exception> _locatedDevelopmentCertificate =
+        private static readonly Action<ILogger, string, string, Exception?> _locatedDevelopmentCertificate =
             LoggerMessage.Define<string, string>(
                 LogLevel.Debug,
                 new EventId(0, "LocatedDevelopmentCertificate"),
                 "Using development certificate: {certificateSubjectName} (Thumbprint: {certificateThumbprint})");
 
-        private static readonly Action<ILogger, Exception> _unableToLocateDevelopmentCertificate =
+        private static readonly Action<ILogger, Exception?> _unableToLocateDevelopmentCertificate =
             LoggerMessage.Define(
                 LogLevel.Debug,
                 new EventId(1, "UnableToLocateDevelopmentCertificate"),
                 "Unable to locate an appropriate development https certificate.");
 
-        private static readonly Action<ILogger, string, Exception> _failedToLocateDevelopmentCertificateFile =
+        private static readonly Action<ILogger, string, Exception?> _failedToLocateDevelopmentCertificateFile =
             LoggerMessage.Define<string>(
                 LogLevel.Debug,
                 new EventId(2, "FailedToLocateDevelopmentCertificateFile"),
                 "Failed to locate the development https certificate at '{certificatePath}'.");
 
-        private static readonly Action<ILogger, string, Exception> _failedToLoadDevelopmentCertificate =
+        private static readonly Action<ILogger, string, Exception?> _failedToLoadDevelopmentCertificate =
             LoggerMessage.Define<string>(
                 LogLevel.Debug,
                 new EventId(3, "FailedToLoadDevelopmentCertificate"),
                 "Failed to load the development https certificate at '{certificatePath}'.");
 
-        private static readonly Action<ILogger, Exception> _badDeveloperCertificateState =
+        private static readonly Action<ILogger, Exception?> _badDeveloperCertificateState =
             LoggerMessage.Define(
                 LogLevel.Error,
                 new EventId(4, "BadDeveloperCertificateState"),
                 CoreStrings.BadDeveloperCertificateState);
 
-        private static readonly Action<ILogger, string, Exception> _developerCertificateFirstRun =
+        private static readonly Action<ILogger, string, Exception?> _developerCertificateFirstRun =
             LoggerMessage.Define<string>(
                 LogLevel.Warning,
                 new EventId(5, "DeveloperCertificateFirstRun"),
                 "{Message}");
 
-        private static readonly Action<ILogger, string, Exception> _failedToLoadCertificate =
+        private static readonly Action<ILogger, string, Exception?> _failedToLoadCertificate =
             LoggerMessage.Define<string>(
                 LogLevel.Error,
                 new EventId(6, "MissingOrInvalidCertificateFile"),
                 "The certificate file at '{CertificateFilePath}' can not be found, contains malformed data or does not contain a certificate.");
 
-        private static readonly Action<ILogger, string, Exception> _failedToLoadCertificateKey =
+        private static readonly Action<ILogger, string, Exception?> _failedToLoadCertificateKey =
             LoggerMessage.Define<string>(
             LogLevel.Error,
             new EventId(7, "MissingOrInvalidCertificateKeyFile"),

+ 11 - 8
src/Servers/Kestrel/Core/src/Internal/ServiceContext.cs

@@ -7,22 +7,25 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
+    // Ideally this type should be readonly and initialized with a constructor.
+    // Tests use TestServiceContext which inherits from this type and sets properties.
+    // Changing this type would be a lot of work.
     internal class ServiceContext
     {
-        public IKestrelTrace Log { get; set; }
+        public IKestrelTrace Log { get; set; } = default!;
 
-        public PipeScheduler Scheduler { get; set; }
+        public PipeScheduler Scheduler { get; set; } = default!;
 
-        public IHttpParser<Http1ParsingHandler> HttpParser { get; set; }
+        public IHttpParser<Http1ParsingHandler> HttpParser { get; set; } = default!;
 
-        public ISystemClock SystemClock { get; set; }
+        public ISystemClock SystemClock { get; set; } = default!;
 
-        public DateHeaderValueManager DateHeaderValueManager { get; set; }
+        public DateHeaderValueManager DateHeaderValueManager { get; set; } = default!;
 
-        public ConnectionManager ConnectionManager { get; set; }
+        public ConnectionManager ConnectionManager { get; set; } = default!;
 
-        public Heartbeat Heartbeat { get; set; }
+        public Heartbeat Heartbeat { get; set; } = default!;
 
-        public KestrelServerOptions ServerOptions { get; set; }
+        public KestrelServerOptions ServerOptions { get; set; } = default!;
     }
 }

+ 15 - 13
src/Servers/Kestrel/Core/src/Internal/SniOptionsSelector.cs

@@ -25,12 +25,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         private readonly string _endpointName;
 
-        private readonly Func<ConnectionContext, string, X509Certificate2> _fallbackServerCertificateSelector;
-        private readonly Action<ConnectionContext, SslServerAuthenticationOptions> _onAuthenticateCallback;
+        private readonly Func<ConnectionContext, string?, X509Certificate2?>? _fallbackServerCertificateSelector;
+        private readonly Action<ConnectionContext, SslServerAuthenticationOptions>? _onAuthenticateCallback;
 
         private readonly Dictionary<string, SniOptions> _exactNameOptions = new Dictionary<string, SniOptions>(StringComparer.OrdinalIgnoreCase);
         private readonly SortedList<string, SniOptions> _wildcardPrefixOptions = new SortedList<string, SniOptions>(LongestStringFirstComparer.Instance);
-        private readonly SniOptions _wildcardOptions;
+        private readonly SniOptions? _wildcardOptions;
 
         public SniOptionsSelector(
             string endpointName,
@@ -94,11 +94,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
                 httpProtocols = HttpsConnectionMiddleware.ValidateAndNormalizeHttpProtocols(httpProtocols, logger);
                 HttpsConnectionMiddleware.ConfigureAlpn(sslOptions, httpProtocols);
 
-                var sniOptions = new SniOptions
-                {
-                    SslOptions = sslOptions,
-                    HttpProtocols = httpProtocols,
-                };
+                var sniOptions = new SniOptions(sslOptions, httpProtocols);
 
                 if (name.Equals(WildcardHost, StringComparison.Ordinal))
                 {
@@ -118,7 +114,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         public SslServerAuthenticationOptions GetOptions(ConnectionContext connection, string serverName)
         {
-            SniOptions sniOptions = null;
+            SniOptions? sniOptions = null;
 
             if (!string.IsNullOrEmpty(serverName) && !_exactNameOptions.TryGetValue(serverName, out sniOptions))
             {
@@ -204,8 +200,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         private class SniOptions
         {
-            public SslServerAuthenticationOptions SslOptions { get; set; }
-            public HttpProtocols HttpProtocols { get; set; }
+            public SniOptions(SslServerAuthenticationOptions sslOptions, HttpProtocols httpProtocols)
+            {
+                SslOptions = sslOptions;
+                HttpProtocols = httpProtocols;
+            }
+
+            public SslServerAuthenticationOptions SslOptions { get; }
+            public HttpProtocols HttpProtocols { get; }
         }
 
         private class LongestStringFirstComparer : IComparer<string>
@@ -216,10 +218,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
             {
             }
 
-            public int Compare(string x, string y)
+            public int Compare(string? x, string? y)
             {
                 // Flip x and y to put the longest instead of the shortest string first in the SortedList.
-                return y.Length.CompareTo(x.Length);
+                return y!.Length.CompareTo(x!.Length);
             }
         }
     }

+ 3 - 3
src/Servers/Kestrel/Core/src/Internal/TlsConnectionFeature.cs

@@ -14,9 +14,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
     internal class TlsConnectionFeature : ITlsConnectionFeature, ITlsApplicationProtocolFeature, ITlsHandshakeFeature
     {
-        public X509Certificate2 ClientCertificate { get; set; }
+        public X509Certificate2? ClientCertificate { get; set; }
 
-        public string HostName { get; set; }
+        public string? HostName { get; set; }
 
         public ReadOnlyMemory<byte> ApplicationProtocol { get; set; }
 
@@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 
         public int KeyExchangeStrength { get; set; }
 
-        public Task<X509Certificate2> GetClientCertificateAsync(CancellationToken cancellationToken)
+        public Task<X509Certificate2?> GetClientCertificateAsync(CancellationToken cancellationToken)
         {
             return Task.FromResult(ClientCertificate);
         }

+ 16 - 15
src/Servers/Kestrel/Core/src/KestrelConfigurationLoader.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
 using System.Net;
@@ -78,7 +79,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
         // Actions that will be delayed until Load so that they aren't applied if the configuration loader is replaced.
         private IList<Action> EndpointsToAdd { get; } = new List<Action>();
 
-        private CertificateConfig DefaultCertificateConfig { get; set; }
+        private CertificateConfig? DefaultCertificateConfig { get; set; }
 
         /// <summary>
         /// Specifies a configuration Action to run when an endpoint with the given name is loaded from configuration.
@@ -436,16 +437,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel
             }
         }
 
-        private (X509Certificate2, CertificateConfig) FindDeveloperCertificateFile()
+        private (X509Certificate2?, CertificateConfig?) FindDeveloperCertificateFile()
         {
-            string certificatePath = null;
-            try
+            string? certificatePath = null;
+            if (ConfigurationReader.Certificates.TryGetValue("Development", out var certificateConfig) &&
+                certificateConfig.Path == null &&
+                certificateConfig.Password != null &&
+                TryGetCertificatePath(out certificatePath) &&
+                File.Exists(certificatePath))
             {
-                if (ConfigurationReader.Certificates.TryGetValue("Development", out var certificateConfig) &&
-                    certificateConfig.Path == null &&
-                    certificateConfig.Password != null &&
-                    TryGetCertificatePath(out certificatePath) &&
-                    File.Exists(certificatePath))
+                try
                 {
                     var certificate = new X509Certificate2(certificatePath, certificateConfig.Password);
 
@@ -454,14 +455,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel
                         return (certificate, certificateConfig);
                     }
                 }
-                else if (!string.IsNullOrEmpty(certificatePath))
+                catch (CryptographicException)
                 {
-                    Logger.FailedToLocateDevelopmentCertificateFile(certificatePath);
+                    Logger.FailedToLoadDevelopmentCertificate(certificatePath);
                 }
             }
-            catch (CryptographicException)
+            else if (!string.IsNullOrEmpty(certificatePath))
             {
-                Logger.FailedToLoadDevelopmentCertificate(certificatePath);
+                Logger.FailedToLocateDevelopmentCertificateFile(certificatePath);
             }
 
             return (null, null);
@@ -476,7 +477,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
 
             foreach (var ext in certificate.Extensions)
             {
-                if (string.Equals(ext.Oid.Value, CertificateManager.AspNetHttpsOid, StringComparison.Ordinal))
+                if (string.Equals(ext.Oid?.Value, CertificateManager.AspNetHttpsOid, StringComparison.Ordinal))
                 {
                     return true;
                 }
@@ -485,7 +486,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
             return false;
         }
 
-        private bool TryGetCertificatePath(out string path)
+        private bool TryGetCertificatePath([NotNullWhen(true)] out string? path)
         {
             // This will go away when we implement
             // https://github.com/aspnet/Hosting/issues/1294

+ 1 - 1
src/Servers/Kestrel/Core/src/KestrelServer.cs

@@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         public KestrelServerOptions Options => _innerKestrelServer.Options;
 
         /// <inheritdoc />
-        public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
+        public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) where TContext : notnull
         {
             return _innerKestrelServer.StartAsync(application, cancellationToken);
         }

+ 2 - 2
src/Servers/Kestrel/Core/src/KestrelServerLimits.cs

@@ -275,7 +275,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// </summary>
         /// <remarks>
         /// </remarks>
-        public MinDataRate MinRequestBodyDataRate { get; set; } =
+        public MinDataRate? MinRequestBodyDataRate { get; set; } =
             // Matches the default IIS minBytesPerSecond
             new MinDataRate(bytesPerSecond: 240, gracePeriod: TimeSpan.FromSeconds(5));
 
@@ -299,7 +299,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// The connection is aborted if the write has not completed by the time that timer expires.
         /// </para>
         /// </remarks>
-        public MinDataRate MinResponseDataRate { get; set; } =
+        public MinDataRate? MinResponseDataRate { get; set; } =
             // Matches the default IIS minBytesPerSecond
             new MinDataRate(bytesPerSecond: 240, gracePeriod: TimeSpan.FromSeconds(5));
     }

+ 11 - 9
src/Servers/Kestrel/Core/src/KestrelServerOptions.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Net;
@@ -27,9 +28,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
     public class KestrelServerOptions
     {
         // internal to fast-path header decoding when RequestHeaderEncodingSelector is unchanged.
-        internal static readonly Func<string, Encoding> DefaultRequestHeaderEncodingSelector = _ => null;
+        internal static readonly Func<string, Encoding?> DefaultRequestHeaderEncodingSelector = _ => null;
 
-        private Func<string, Encoding> _requestHeaderEncodingSelector = DefaultRequestHeaderEncodingSelector;
+        private Func<string, Encoding?> _requestHeaderEncodingSelector = DefaultRequestHeaderEncodingSelector;
 
         // The following two lists configure the endpoints that Kestrel should listen to. If both lists are empty, the "urls" config setting (e.g. UseUrls) is used.
         internal List<ListenOptions> CodeBackedListenOptions { get; } = new List<ListenOptions>();
@@ -86,7 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// Gets or sets a callback that returns the <see cref="Encoding"/> to decode the value for the specified request header name,
         /// or <see langword="null"/> to use the default <see cref="UTF8Encoding"/>.
         /// </summary>
-        public Func<string, Encoding> RequestHeaderEncodingSelector
+        public Func<string, Encoding?> RequestHeaderEncodingSelector
         {
             get => _requestHeaderEncodingSelector;
             set => _requestHeaderEncodingSelector = value ?? throw new ArgumentNullException(nameof(value));
@@ -96,7 +97,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// Enables the Listen options callback to resolve and use services registered by the application during startup.
         /// Typically initialized by UseKestrel().
         /// </summary>
-        public IServiceProvider ApplicationServices { get; set; }
+        public IServiceProvider ApplicationServices { get; set; } = default!; // This should typically be set
 
         /// <summary>
         /// Provides access to request limit options.
@@ -107,7 +108,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// Provides a configuration source where endpoints will be loaded from on server start.
         /// The default is <see langword="null"/>.
         /// </summary>
-        public KestrelConfigurationLoader ConfigurationLoader { get; set; }
+        public KestrelConfigurationLoader? ConfigurationLoader { get; set; }
 
         /// <summary>
         /// A default configuration action for all endpoints. Use for Listen, configuration, the default url, and URLs.
@@ -122,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// <summary>
         /// The default server certificate for https endpoints. This is applied lazily after HttpsDefaults and user options.
         /// </summary>
-        internal X509Certificate2 DefaultCertificate { get; set; }
+        internal X509Certificate2? DefaultCertificate { get; set; }
 
         /// <summary>
         /// Has the default dev certificate load been attempted?
@@ -186,18 +187,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
                     if (DefaultCertificate != null)
                     {
                         var status = CertificateManager.Instance.CheckCertificateState(DefaultCertificate, interactive: false);
-                        if (!status.Result)
+                        if (!status.Success)
                         {
                             // Display a warning indicating to the user that a prompt might appear and provide instructions on what to do in that
                             // case. The underlying implementation of this check is specific to Mac OS and is handled within CheckCertificateState.
                             // Kestrel must NEVER cause a UI prompt on a production system. We only attempt this here because Mac OS is not supported
                             // in production.
-                            logger.DeveloperCertificateFirstRun(status.Message);
+                            Debug.Assert(status.FailureMessage != null, "Status with a failure result must have a message.");
+                            logger.DeveloperCertificateFirstRun(status.FailureMessage);
 
                             // Now that we've displayed a warning in the logs so that the user gets a notification that a prompt might appear, try
                             // and access the certificate key, which might trigger a prompt.
                             status = CertificateManager.Instance.CheckCertificateState(DefaultCertificate, interactive: true);
-                            if (!status.Result)
+                            if (!status.Success)
                             {
                                 logger.BadDeveloperCertificateState();
                             }

+ 7 - 7
src/Servers/Kestrel/Core/src/ListenOptions.cs

@@ -50,20 +50,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         public EndPoint EndPoint { get; internal set; }
 
         // For comparing bound endpoints to changed config during endpoint config reload.
-        internal EndpointConfig EndpointConfig { get; set; }
+        internal EndpointConfig? EndpointConfig { get; set; }
 
         // IPEndPoint is mutable so port 0 can be updated to the bound port.
         /// <summary>
         /// The <see cref="IPEndPoint"/> to bind to.
         /// Only set if the <see cref="ListenOptions"/> <see cref="Type"/> is <see cref="IPEndPoint"/>.
         /// </summary>
-        public IPEndPoint IPEndPoint => EndPoint as IPEndPoint;
+        public IPEndPoint? IPEndPoint => EndPoint as IPEndPoint;
 
         /// <summary>
         /// The absolute path to a Unix domain socket to bind to.
         /// Only set if the <see cref="ListenOptions"/> <see cref="Type"/> is <see cref="UnixDomainSocketEndPoint"/>.
         /// </summary>
-        public string SocketPath => (EndPoint as UnixDomainSocketEndPoint)?.ToString();
+        public string? SocketPath => (EndPoint as UnixDomainSocketEndPoint)?.ToString();
 
         /// <summary>
         /// A file descriptor for the socket to open.
@@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// Enables connection middleware to resolve and use services registered by the application during startup.
         /// Only set if accessed from the callback of a <see cref="KestrelServerOptions"/> Listen* method.
         /// </summary>
-        public KestrelServerOptions KestrelServerOptions { get; internal set; }
+        public KestrelServerOptions KestrelServerOptions { get; internal set; } = default!; // Set via ConfigureKestrel callback
 
         /// <summary>
         /// The protocols enabled on this endpoint.
@@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// <summary>
         /// Gets the application <see cref="IServiceProvider"/>.
         /// </summary>
-        public IServiceProvider ApplicationServices => KestrelServerOptions?.ApplicationServices;
+        public IServiceProvider ApplicationServices => KestrelServerOptions?.ApplicationServices!; // TODO - Always available?
 
         internal string Scheme
         {
@@ -117,7 +117,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         }
 
         /// <inheritdoc />
-        public override string ToString() => GetDisplayName();
+        public override string? ToString() => GetDisplayName();
 
         /// <summary>
         /// Adds a middleware delegate to the connection pipeline.
@@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
                 return Task.CompletedTask;
             };
 
-            for (int i = _middleware.Count - 1; i >= 0; i--)
+            for (var i = _middleware.Count - 1; i >= 0; i--)
             {
                 var component = _middleware[i];
                 app = component(app);

+ 2 - 2
src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs

@@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Hosting
         /// content files.</param>
         /// <param name="password">The password required to access the X.509 certificate data.</param>
         /// <returns>The <see cref="ListenOptions"/>.</returns>
-        public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string password)
+        public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string? password)
         {
             var env = listenOptions.ApplicationServices.GetRequiredService<IHostEnvironment>();
             return listenOptions.UseHttps(new X509Certificate2(Path.Combine(env.ContentRootPath, fileName), password));
@@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Hosting
         /// <param name="password">The password required to access the X.509 certificate data.</param>
         /// <param name="configureOptions">An Action to configure the <see cref="HttpsConnectionAdapterOptions"/>.</param>
         /// <returns>The <see cref="ListenOptions"/>.</returns>
-        public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string password,
+        public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string? password,
             Action<HttpsConnectionAdapterOptions> configureOptions)
         {
             var env = listenOptions.ApplicationServices.GetRequiredService<IHostEnvironment>();

+ 2 - 2
src/Servers/Kestrel/Core/src/LocalhostListenOptions.cs

@@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         /// </summary>
         internal override string GetDisplayName()
         {
-            return $"{Scheme}://localhost:{IPEndPoint.Port}";
+            return $"{Scheme}://localhost:{IPEndPoint!.Port}";
         }
 
         internal override async Task BindAsync(AddressBindContext context)
@@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
         // used for cloning to two IPEndpoints
         internal ListenOptions Clone(IPAddress address)
         {
-            var options = new ListenOptions(new IPEndPoint(address, IPEndPoint.Port))
+            var options = new ListenOptions(new IPEndPoint(address, IPEndPoint!.Port))
             {
                 KestrelServerOptions = KestrelServerOptions,
                 Protocols = Protocols,

+ 1 - 0
src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj

@@ -10,6 +10,7 @@
     <NoWarn>CS1591;$(NoWarn)</NoWarn>
     <IsPackable>false</IsPackable>
     <DefineConstants>$(DefineConstants);KESTREL</DefineConstants>
+    <Nullable>enable</Nullable>
   </PropertyGroup>
 
   <ItemGroup>

+ 9 - 11
src/Servers/Kestrel/Core/src/Middleware/Http3ConnectionMiddleware.cs

@@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
 {
-    internal class Http3ConnectionMiddleware<TContext>
+    internal class Http3ConnectionMiddleware<TContext> where TContext : notnull
     {
         private readonly ServiceContext _serviceContext;
         private readonly IHttpApplication<TContext> _application;
@@ -26,16 +26,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
         {
             var memoryPoolFeature = connectionContext.Features.Get<IMemoryPoolFeature>();
 
-            var http3ConnectionContext = new Http3ConnectionContext
-            {
-                ConnectionId = connectionContext.ConnectionId,
-                ConnectionContext = connectionContext,
-                ServiceContext = _serviceContext,
-                ConnectionFeatures = connectionContext.Features,
-                MemoryPool = memoryPoolFeature?.MemoryPool ?? System.Buffers.MemoryPool<byte>.Shared,
-                LocalEndPoint = connectionContext.LocalEndPoint as IPEndPoint,
-                RemoteEndPoint = connectionContext.RemoteEndPoint as IPEndPoint
-            };
+            var http3ConnectionContext = new Http3ConnectionContext(
+                connectionContext.ConnectionId,
+                connectionContext,
+                _serviceContext,
+                connectionContext.Features,
+                memoryPoolFeature?.MemoryPool ?? System.Buffers.MemoryPool<byte>.Shared,
+                connectionContext.LocalEndPoint as IPEndPoint,
+                connectionContext.RemoteEndPoint as IPEndPoint);
 
             var connection = new Http3Connection(http3ConnectionContext);
 

Some files were not shown because too many files changed in this diff