diff --git a/src/Hosting/TestHost/src/ClientHandler.cs b/src/Hosting/TestHost/src/ClientHandler.cs index bb3d98105a9f..299b84bea6bd 100644 --- a/src/Hosting/TestHost/src/ClientHandler.cs +++ b/src/Hosting/TestHost/src/ClientHandler.cs @@ -184,7 +184,7 @@ protected override async Task SendAsync( // Copy trailers to the response message when the response stream is complete contextBuilder.RegisterResponseReadCompleteCallback(context => { - var responseTrailersFeature = context.Features.Get(); + var responseTrailersFeature = context.Features.Get()!; foreach (var trailer in responseTrailersFeature.Trailers) { @@ -196,7 +196,7 @@ protected override async Task SendAsync( var httpContext = await contextBuilder.SendAsync(cancellationToken); response.StatusCode = (HttpStatusCode)httpContext.Response.StatusCode; - response.ReasonPhrase = httpContext.Features.Get().ReasonPhrase; + response.ReasonPhrase = httpContext.Features.Get()!.ReasonPhrase; response.RequestMessage = request; response.Version = request.Version; diff --git a/src/Hosting/TestHost/src/HttpContextBuilder.cs b/src/Hosting/TestHost/src/HttpContextBuilder.cs index 8fb74385c1a7..ef9b6e5faee3 100644 --- a/src/Hosting/TestHost/src/HttpContextBuilder.cs +++ b/src/Hosting/TestHost/src/HttpContextBuilder.cs @@ -225,7 +225,7 @@ internal async Task ReturnResponseMessageAsync() { newFeatures[pair.Key] = pair.Value; } - var serverResponseFeature = _httpContext.Features.Get(); + var serverResponseFeature = _httpContext.Features.Get()!; // The client gets a deep copy of this so they can interact with the body stream independently of the server. var clientResponseFeature = new HttpResponseFeature() { diff --git a/src/Http/Http.Extensions/src/ResponseExtensions.cs b/src/Http/Http.Extensions/src/ResponseExtensions.cs index 5fe1614be4a2..46bb247944c3 100644 --- a/src/Http/Http.Extensions/src/ResponseExtensions.cs +++ b/src/Http/Http.Extensions/src/ResponseExtensions.cs @@ -16,7 +16,7 @@ public static void Clear(this HttpResponse response) throw new InvalidOperationException("The response cannot be cleared, it has already started sending."); } response.StatusCode = 200; - response.HttpContext.Features.Get().ReasonPhrase = null; + response.HttpContext.Features.Get()!.ReasonPhrase = null; response.Headers.Clear(); if (response.Body.CanSeek) { diff --git a/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs b/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs index a9fd30908b9a..a888c096112d 100644 --- a/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs +++ b/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs @@ -143,7 +143,7 @@ private static async Task SendFileAsyncCore(HttpResponse response, string fileNa { var useRequestAborted = !cancellationToken.CanBeCanceled; var localCancel = useRequestAborted ? response.HttpContext.RequestAborted : cancellationToken; - var sendFile = response.HttpContext.Features.Get(); + var sendFile = response.HttpContext.Features.Get()!; try { diff --git a/src/Http/Http.Features/src/FeatureCollection.cs b/src/Http/Http.Features/src/FeatureCollection.cs index d46a1e38e5bd..626277ee134f 100644 --- a/src/Http/Http.Features/src/FeatureCollection.cs +++ b/src/Http/Http.Features/src/FeatureCollection.cs @@ -98,7 +98,7 @@ public IEnumerator> GetEnumerator() return (TFeature?)this[typeof(TFeature)]; } - public void Set(TFeature instance) + public void Set(TFeature? instance) { this[typeof(TFeature)] = instance; } diff --git a/src/Http/Http.Features/src/IFeatureCollection.cs b/src/Http/Http.Features/src/IFeatureCollection.cs index 6d087df337d7..ff1f3ec69676 100644 --- a/src/Http/Http.Features/src/IFeatureCollection.cs +++ b/src/Http/Http.Features/src/IFeatureCollection.cs @@ -33,13 +33,13 @@ public interface IFeatureCollection : IEnumerable> /// /// The feature key. /// The requested feature, or null if it is not present. - TFeature Get(); + TFeature? Get(); /// /// Sets the given feature in the collection. /// /// The feature key. /// The feature value. - void Set(TFeature instance); + void Set(TFeature? instance); } } diff --git a/src/Http/Http.Features/src/PublicAPI.Shipped.txt b/src/Http/Http.Features/src/PublicAPI.Shipped.txt index 6c0b614457a9..18866d4bcb71 100644 --- a/src/Http/Http.Features/src/PublicAPI.Shipped.txt +++ b/src/Http/Http.Features/src/PublicAPI.Shipped.txt @@ -26,7 +26,7 @@ Microsoft.AspNetCore.Http.Features.FeatureCollection.FeatureCollection(Microsoft Microsoft.AspNetCore.Http.Features.FeatureCollection.Get() -> TFeature? Microsoft.AspNetCore.Http.Features.FeatureCollection.GetEnumerator() -> System.Collections.Generic.IEnumerator>! Microsoft.AspNetCore.Http.Features.FeatureCollection.IsReadOnly.get -> bool -Microsoft.AspNetCore.Http.Features.FeatureCollection.Set(TFeature instance) -> void +Microsoft.AspNetCore.Http.Features.FeatureCollection.Set(TFeature? instance) -> void Microsoft.AspNetCore.Http.Features.FeatureCollection.this[System.Type! key].get -> object? Microsoft.AspNetCore.Http.Features.FeatureCollection.this[System.Type! key].set -> void Microsoft.AspNetCore.Http.Features.FeatureReference @@ -46,10 +46,10 @@ Microsoft.AspNetCore.Http.Features.HttpsCompressionMode.Compress = 2 -> Microsof Microsoft.AspNetCore.Http.Features.HttpsCompressionMode.Default = 0 -> Microsoft.AspNetCore.Http.Features.HttpsCompressionMode Microsoft.AspNetCore.Http.Features.HttpsCompressionMode.DoNotCompress = 1 -> Microsoft.AspNetCore.Http.Features.HttpsCompressionMode Microsoft.AspNetCore.Http.Features.IFeatureCollection -Microsoft.AspNetCore.Http.Features.IFeatureCollection.Get() -> TFeature +Microsoft.AspNetCore.Http.Features.IFeatureCollection.Get() -> TFeature? Microsoft.AspNetCore.Http.Features.IFeatureCollection.IsReadOnly.get -> bool Microsoft.AspNetCore.Http.Features.IFeatureCollection.Revision.get -> int -Microsoft.AspNetCore.Http.Features.IFeatureCollection.Set(TFeature instance) -> void +Microsoft.AspNetCore.Http.Features.IFeatureCollection.Set(TFeature? instance) -> void Microsoft.AspNetCore.Http.Features.IFeatureCollection.this[System.Type! key].get -> object? Microsoft.AspNetCore.Http.Features.IFeatureCollection.this[System.Type! key].set -> void Microsoft.AspNetCore.Http.Features.IFormFeature diff --git a/src/Http/Http/src/Internal/DefaultHttpResponse.cs b/src/Http/Http/src/Internal/DefaultHttpResponse.cs index 6caa939444ee..554616bd7536 100644 --- a/src/Http/Http/src/Internal/DefaultHttpResponse.cs +++ b/src/Http/Http/src/Internal/DefaultHttpResponse.cs @@ -69,7 +69,7 @@ public override Stream Body get { return HttpResponseBodyFeature.Stream; } set { - var otherFeature = _features.Collection.Get(); + var otherFeature = _features.Collection.Get()!; if (otherFeature is StreamResponseBodyFeature streamFeature && streamFeature.PriorFeature != null diff --git a/src/Http/Http/src/Internal/ResponseCookies.cs b/src/Http/Http/src/Internal/ResponseCookies.cs index e6e582bfda0b..4a34b055d604 100644 --- a/src/Http/Http/src/Internal/ResponseCookies.cs +++ b/src/Http/Http/src/Internal/ResponseCookies.cs @@ -28,7 +28,7 @@ internal class ResponseCookies : IResponseCookies internal ResponseCookies(IFeatureCollection features) { _features = features; - Headers = _features.Get().Headers; + Headers = _features.Get()!.Headers; } private IHeaderDictionary Headers { get; set; } diff --git a/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs b/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs index 0d0715db213a..e37b39dde7d2 100644 --- a/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs +++ b/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.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.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; @@ -54,6 +55,8 @@ public async Task Invoke(HttpContext context) var originalBodyFeature = context.Features.Get(); var originalCompressionFeature = context.Features.Get(); + Debug.Assert(originalBodyFeature != null); + var compressionBody = new ResponseCompressionBody(context, _provider, originalBodyFeature); context.Features.Set(compressionBody); context.Features.Set(compressionBody); diff --git a/src/Middleware/Rewrite/src/UrlActions/CustomResponseAction.cs b/src/Middleware/Rewrite/src/UrlActions/CustomResponseAction.cs index ecd63fd32d6c..23fdfba6c105 100644 --- a/src/Middleware/Rewrite/src/UrlActions/CustomResponseAction.cs +++ b/src/Middleware/Rewrite/src/UrlActions/CustomResponseAction.cs @@ -26,7 +26,7 @@ public override void ApplyAction(RewriteContext context, BackReferenceCollection if (!string.IsNullOrEmpty(StatusReason)) { - context.HttpContext.Features.Get().ReasonPhrase = StatusReason; + context.HttpContext.Features.Get()!.ReasonPhrase = StatusReason; } if (!string.IsNullOrEmpty(StatusDescription)) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.Generated.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.Generated.cs index bc37696cb529..d8ad519873de 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.Generated.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.Generated.cs @@ -12,6 +12,8 @@ using Microsoft.Net.Http.Headers; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; +#nullable enable + namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { internal enum KnownHeaderType @@ -6712,7 +6714,7 @@ protected override bool CopyToFast(KeyValuePair[] array, i array[arrayIndex] = new KeyValuePair(HeaderNames.ContentLength, HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value)); ++arrayIndex; } - ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); + ((ICollection>?)MaybeUnknown)?.CopyTo(array, arrayIndex); return true; } @@ -12724,7 +12726,7 @@ protected override bool CopyToFast(KeyValuePair[] array, i array[arrayIndex] = new KeyValuePair(HeaderNames.ContentLength, HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value)); ++arrayIndex; } - ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); + ((ICollection>?)MaybeUnknown)?.CopyTo(array, arrayIndex); return true; } @@ -12816,7 +12818,7 @@ internal unsafe void CopyToFast(ref BufferWriter output) { tempBits ^= 0x8000000000000000L; output.Write(HeaderBytes.Slice(640, 18)); - output.WriteNumeric((ulong)ContentLength.Value); + output.WriteNumeric((ulong)ContentLength.GetValueOrDefault()); if (tempBits == 0) { return; @@ -13266,10 +13268,10 @@ private struct HeaderReferences public StringValues _AccessControlExposeHeaders; public StringValues _AccessControlMaxAge; - public byte[] _rawConnection; - public byte[] _rawDate; - public byte[] _rawTransferEncoding; - public byte[] _rawServer; + public byte[]? _rawConnection; + public byte[]? _rawDate; + public byte[]? _rawTransferEncoding; + public byte[]? _rawServer; } public partial struct Enumerator @@ -14149,7 +14151,7 @@ protected override bool CopyToFast(KeyValuePair[] array, i array[arrayIndex] = new KeyValuePair(HeaderNames.ContentLength, HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value)); ++arrayIndex; } - ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); + ((ICollection>?)MaybeUnknown)?.CopyTo(array, arrayIndex); return true; } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs index 52c0308f89a1..f6420cc0c955 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs @@ -9,42 +9,44 @@ using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; +#nullable enable + namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { internal partial class HttpProtocol : IFeatureCollection { - private object _currentIHttpRequestFeature; - private object _currentIHttpRequestBodyDetectionFeature; - private object _currentIHttpResponseFeature; - private object _currentIHttpResponseBodyFeature; - private object _currentIRequestBodyPipeFeature; - private object _currentIHttpRequestIdentifierFeature; - private object _currentIServiceProvidersFeature; - private object _currentIHttpRequestLifetimeFeature; - private object _currentIHttpConnectionFeature; - private object _currentIRouteValuesFeature; - private object _currentIEndpointFeature; - private object _currentIHttpAuthenticationFeature; - private object _currentIHttpRequestTrailersFeature; - private object _currentIQueryFeature; - private object _currentIFormFeature; - private object _currentIHttpUpgradeFeature; - private object _currentIHttp2StreamIdFeature; - private object _currentIHttpResponseTrailersFeature; - private object _currentIResponseCookiesFeature; - private object _currentIItemsFeature; - private object _currentITlsConnectionFeature; - private object _currentIHttpWebSocketFeature; - private object _currentISessionFeature; - private object _currentIHttpMaxRequestBodySizeFeature; - private object _currentIHttpMinRequestBodyDataRateFeature; - private object _currentIHttpMinResponseDataRateFeature; - private object _currentIHttpBodyControlFeature; - private object _currentIHttpResetFeature; + private object? _currentIHttpRequestFeature; + private object? _currentIHttpRequestBodyDetectionFeature; + private object? _currentIHttpResponseFeature; + private object? _currentIHttpResponseBodyFeature; + private object? _currentIRequestBodyPipeFeature; + private object? _currentIHttpRequestIdentifierFeature; + private object? _currentIServiceProvidersFeature; + private object? _currentIHttpRequestLifetimeFeature; + private object? _currentIHttpConnectionFeature; + private object? _currentIRouteValuesFeature; + private object? _currentIEndpointFeature; + private object? _currentIHttpAuthenticationFeature; + private object? _currentIHttpRequestTrailersFeature; + private object? _currentIQueryFeature; + private object? _currentIFormFeature; + private object? _currentIHttpUpgradeFeature; + private object? _currentIHttp2StreamIdFeature; + private object? _currentIHttpResponseTrailersFeature; + private object? _currentIResponseCookiesFeature; + private object? _currentIItemsFeature; + private object? _currentITlsConnectionFeature; + private object? _currentIHttpWebSocketFeature; + private object? _currentISessionFeature; + private object? _currentIHttpMaxRequestBodySizeFeature; + private object? _currentIHttpMinRequestBodyDataRateFeature; + private object? _currentIHttpMinResponseDataRateFeature; + private object? _currentIHttpBodyControlFeature; + private object? _currentIHttpResetFeature; private int _featureRevision; - private List> MaybeExtra; + private List>? MaybeExtra; private void FastReset() { @@ -87,7 +89,7 @@ internal void ResetFeatureCollection() _featureRevision++; } - private object ExtraFeatureGet(Type key) + private object? ExtraFeatureGet(Type key) { if (MaybeExtra == null) { @@ -104,33 +106,50 @@ private object ExtraFeatureGet(Type key) return null; } - private void ExtraFeatureSet(Type key, object value) + private void ExtraFeatureSet(Type key, object? value) { - if (MaybeExtra == null) + if (value == null) { - MaybeExtra = new List>(2); + if (MaybeExtra == null) + { + return; + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra.RemoveAt(i); + return; + } + } } - - for (var i = 0; i < MaybeExtra.Count; i++) + else { - if (MaybeExtra[i].Key == key) + if (MaybeExtra == null) { - MaybeExtra[i] = new KeyValuePair(key, value); - return; + MaybeExtra = new List>(2); + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra[i] = new KeyValuePair(key, value); + return; + } } + MaybeExtra.Add(new KeyValuePair(key, value)); } - MaybeExtra.Add(new KeyValuePair(key, value)); } bool IFeatureCollection.IsReadOnly => false; int IFeatureCollection.Revision => _featureRevision; - object IFeatureCollection.this[Type key] + object? IFeatureCollection.this[Type key] { get { - object feature = null; + object? feature = null; if (key == typeof(IHttpRequestFeature)) { feature = _currentIHttpRequestFeature; @@ -374,124 +393,124 @@ object IFeatureCollection.this[Type key] } } - TFeature IFeatureCollection.Get() + TFeature? IFeatureCollection.Get() where TFeature : default { - TFeature feature = default; + TFeature? feature = default; if (typeof(TFeature) == typeof(IHttpRequestFeature)) { - feature = (TFeature)_currentIHttpRequestFeature; + feature = (TFeature?)_currentIHttpRequestFeature; } else if (typeof(TFeature) == typeof(IHttpRequestBodyDetectionFeature)) { - feature = (TFeature)_currentIHttpRequestBodyDetectionFeature; + feature = (TFeature?)_currentIHttpRequestBodyDetectionFeature; } else if (typeof(TFeature) == typeof(IHttpResponseFeature)) { - feature = (TFeature)_currentIHttpResponseFeature; + feature = (TFeature?)_currentIHttpResponseFeature; } else if (typeof(TFeature) == typeof(IHttpResponseBodyFeature)) { - feature = (TFeature)_currentIHttpResponseBodyFeature; + feature = (TFeature?)_currentIHttpResponseBodyFeature; } else if (typeof(TFeature) == typeof(IRequestBodyPipeFeature)) { - feature = (TFeature)_currentIRequestBodyPipeFeature; + feature = (TFeature?)_currentIRequestBodyPipeFeature; } else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature)) { - feature = (TFeature)_currentIHttpRequestIdentifierFeature; + feature = (TFeature?)_currentIHttpRequestIdentifierFeature; } else if (typeof(TFeature) == typeof(IServiceProvidersFeature)) { - feature = (TFeature)_currentIServiceProvidersFeature; + feature = (TFeature?)_currentIServiceProvidersFeature; } else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature)) { - feature = (TFeature)_currentIHttpRequestLifetimeFeature; + feature = (TFeature?)_currentIHttpRequestLifetimeFeature; } else if (typeof(TFeature) == typeof(IHttpConnectionFeature)) { - feature = (TFeature)_currentIHttpConnectionFeature; + feature = (TFeature?)_currentIHttpConnectionFeature; } else if (typeof(TFeature) == typeof(IRouteValuesFeature)) { - feature = (TFeature)_currentIRouteValuesFeature; + feature = (TFeature?)_currentIRouteValuesFeature; } else if (typeof(TFeature) == typeof(IEndpointFeature)) { - feature = (TFeature)_currentIEndpointFeature; + feature = (TFeature?)_currentIEndpointFeature; } else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) { - feature = (TFeature)_currentIHttpAuthenticationFeature; + feature = (TFeature?)_currentIHttpAuthenticationFeature; } else if (typeof(TFeature) == typeof(IHttpRequestTrailersFeature)) { - feature = (TFeature)_currentIHttpRequestTrailersFeature; + feature = (TFeature?)_currentIHttpRequestTrailersFeature; } else if (typeof(TFeature) == typeof(IQueryFeature)) { - feature = (TFeature)_currentIQueryFeature; + feature = (TFeature?)_currentIQueryFeature; } else if (typeof(TFeature) == typeof(IFormFeature)) { - feature = (TFeature)_currentIFormFeature; + feature = (TFeature?)_currentIFormFeature; } else if (typeof(TFeature) == typeof(IHttpUpgradeFeature)) { - feature = (TFeature)_currentIHttpUpgradeFeature; + feature = (TFeature?)_currentIHttpUpgradeFeature; } else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) { - feature = (TFeature)_currentIHttp2StreamIdFeature; + feature = (TFeature?)_currentIHttp2StreamIdFeature; } else if (typeof(TFeature) == typeof(IHttpResponseTrailersFeature)) { - feature = (TFeature)_currentIHttpResponseTrailersFeature; + feature = (TFeature?)_currentIHttpResponseTrailersFeature; } else if (typeof(TFeature) == typeof(IResponseCookiesFeature)) { - feature = (TFeature)_currentIResponseCookiesFeature; + feature = (TFeature?)_currentIResponseCookiesFeature; } else if (typeof(TFeature) == typeof(IItemsFeature)) { - feature = (TFeature)_currentIItemsFeature; + feature = (TFeature?)_currentIItemsFeature; } else if (typeof(TFeature) == typeof(ITlsConnectionFeature)) { - feature = (TFeature)_currentITlsConnectionFeature; + feature = (TFeature?)_currentITlsConnectionFeature; } else if (typeof(TFeature) == typeof(IHttpWebSocketFeature)) { - feature = (TFeature)_currentIHttpWebSocketFeature; + feature = (TFeature?)_currentIHttpWebSocketFeature; } else if (typeof(TFeature) == typeof(ISessionFeature)) { - feature = (TFeature)_currentISessionFeature; + feature = (TFeature?)_currentISessionFeature; } else if (typeof(TFeature) == typeof(IHttpMaxRequestBodySizeFeature)) { - feature = (TFeature)_currentIHttpMaxRequestBodySizeFeature; + feature = (TFeature?)_currentIHttpMaxRequestBodySizeFeature; } else if (typeof(TFeature) == typeof(IHttpMinRequestBodyDataRateFeature)) { - feature = (TFeature)_currentIHttpMinRequestBodyDataRateFeature; + feature = (TFeature?)_currentIHttpMinRequestBodyDataRateFeature; } else if (typeof(TFeature) == typeof(IHttpMinResponseDataRateFeature)) { - feature = (TFeature)_currentIHttpMinResponseDataRateFeature; + feature = (TFeature?)_currentIHttpMinResponseDataRateFeature; } else if (typeof(TFeature) == typeof(IHttpBodyControlFeature)) { - feature = (TFeature)_currentIHttpBodyControlFeature; + feature = (TFeature?)_currentIHttpBodyControlFeature; } else if (typeof(TFeature) == typeof(IHttpResetFeature)) { - feature = (TFeature)_currentIHttpResetFeature; + feature = (TFeature?)_currentIHttpResetFeature; } else if (MaybeExtra != null) { - feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); + feature = (TFeature?)(ExtraFeatureGet(typeof(TFeature))); } if (feature == null) @@ -502,7 +521,7 @@ TFeature IFeatureCollection.Get() return feature; } - void IFeatureCollection.Set(TFeature feature) + void IFeatureCollection.Set(TFeature? feature) where TFeature : default { _featureRevision++; if (typeof(TFeature) == typeof(IHttpRequestFeature)) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs index 41254f837d40..05e017cbfea1 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs @@ -96,7 +96,7 @@ private void AppendContentLength(ReadOnlySpan value) } [MethodImpl(MethodImplOptions.NoInlining)] - private void AppendContentLengthCustomEncoding(ReadOnlySpan value, Encoding customEncoding) + private void AppendContentLengthCustomEncoding(ReadOnlySpan value, Encoding? customEncoding) { if (_contentLength.HasValue) { @@ -105,7 +105,7 @@ private void AppendContentLengthCustomEncoding(ReadOnlySpan value, Encodin // long.MaxValue = 9223372036854775807 (19 chars) Span decodedChars = stackalloc char[20]; - var numChars = customEncoding.GetChars(value, decodedChars); + var numChars = customEncoding!.GetChars(value, decodedChars); long parsed = -1; if (numChars > 19 || diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.Generated.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.Generated.cs index 290fea38f5ab..fb271a155cdd 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.Generated.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.Generated.cs @@ -3,6 +3,8 @@ using System; +#nullable enable + namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { internal partial class Http2Connection diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs index fb8a6d9adef3..f640c274c14a 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs @@ -210,6 +210,7 @@ internal async Task InnerProcessRequestsAsync(IHttpApplication(); Debug.Assert(quicStreamFeature != null); + Debug.Assert(streamIdFeature != null); var httpConnectionContext = new Http3StreamContext( streamContext.ConnectionId, diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs index 51f570acaa7a..46b3d6d9a942 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs @@ -62,8 +62,8 @@ public Http3Stream(Http3Connection http3Connection, Http3StreamContext context) _http3Connection = http3Connection; _context = context; - _errorCodeFeature = _context.ConnectionFeatures.Get(); - _streamIdFeature = _context.ConnectionFeatures.Get(); + _errorCodeFeature = _context.ConnectionFeatures.Get()!; + _streamIdFeature = _context.ConnectionFeatures.Get()!; _frameWriter = new Http3FrameWriter( context.Transport.Output, diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.Generated.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.Generated.cs index 89443f3de0f2..684efdb0f227 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.Generated.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.Generated.cs @@ -6,6 +6,8 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +#nullable enable + namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure { internal static partial class HttpUtilities diff --git a/src/Servers/Kestrel/Core/test/Http1HttpProtocolFeatureCollectionTests.cs b/src/Servers/Kestrel/Core/test/Http1HttpProtocolFeatureCollectionTests.cs index d71a5865de2b..d7d15d61250f 100644 --- a/src/Servers/Kestrel/Core/test/Http1HttpProtocolFeatureCollectionTests.cs +++ b/src/Servers/Kestrel/Core/test/Http1HttpProtocolFeatureCollectionTests.cs @@ -159,6 +159,19 @@ public void Http1HasIHttpMinResponseDataRateFeature() Assert.NotNull(_collection.Get()); } + [Fact] + public void SetExtraFeatureAsNull() + { + _collection[typeof(string)] = null; + Assert.Equal(0, _collection.Count(kv => kv.Key == typeof(string))); + + _collection[typeof(string)] = "A string"; + Assert.Equal(1, _collection.Count(kv => kv.Key == typeof(string))); + + _collection[typeof(string)] = null; + Assert.Equal(0, _collection.Count(kv => kv.Key == typeof(string))); + } + private void CompareGenericGetterToIndexer() { Assert.Same(_collection.Get(), _collection[typeof(IHttpRequestFeature)]); diff --git a/src/Servers/Kestrel/Transport.Quic/src/Internal/QuicConnectionContext.cs b/src/Servers/Kestrel/Transport.Quic/src/Internal/QuicConnectionContext.cs index 21da83c29c90..07b23d6c0969 100644 --- a/src/Servers/Kestrel/Transport.Quic/src/Internal/QuicConnectionContext.cs +++ b/src/Servers/Kestrel/Transport.Quic/src/Internal/QuicConnectionContext.cs @@ -96,7 +96,7 @@ public override ValueTask ConnectAsync(IFeatureCollection? fe if (features != null) { - var streamDirectionFeature = features.Get(); + var streamDirectionFeature = features.Get()!; if (streamDirectionFeature.CanRead) { quicStream = _connection.OpenBidirectionalStream(); diff --git a/src/Servers/Kestrel/shared/KnownHeaders.cs b/src/Servers/Kestrel/shared/KnownHeaders.cs index 16d65a833d1a..e6541998aa29 100644 --- a/src/Servers/Kestrel/shared/KnownHeaders.cs +++ b/src/Servers/Kestrel/shared/KnownHeaders.cs @@ -723,6 +723,8 @@ public static string GeneratedFile() using Microsoft.Net.Http.Headers; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; +#nullable enable + namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http {{ internal enum KnownHeaderType @@ -1026,7 +1028,7 @@ protected override bool CopyToFast(KeyValuePair[] array, i array[arrayIndex] = new KeyValuePair(HeaderNames.ContentLength, HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value)); ++arrayIndex; }} - ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); + ((ICollection>?)MaybeUnknown)?.CopyTo(array, arrayIndex); return true; }} @@ -1070,7 +1072,7 @@ internal unsafe void CopyToFast(ref BufferWriter output) break; // OutputHeader }}")}" : $@" output.Write(HeaderBytes.Slice({hi.Header.BytesOffset}, {hi.Header.BytesCount})); - output.WriteNumeric((ulong)ContentLength.Value); + output.WriteNumeric((ulong)ContentLength.GetValueOrDefault()); if (tempBits == 0) {{ return; @@ -1148,7 +1150,7 @@ private struct HeaderReferences {{{Each(loop.Headers.Where(header => header.Identifier != "ContentLength"), header => @" public StringValues _" + header.Identifier + ";")} {Each(loop.Headers.Where(header => header.EnhancedSetter), header => @" - public byte[] _raw" + header.Identifier + ";")} + public byte[]? _raw" + header.Identifier + ";")} }} public partial struct Enumerator diff --git a/src/Servers/Kestrel/shared/TransportConnection.Generated.cs b/src/Servers/Kestrel/shared/TransportConnection.Generated.cs index a9ba1cd76284..b35954bb6fa2 100644 --- a/src/Servers/Kestrel/shared/TransportConnection.Generated.cs +++ b/src/Servers/Kestrel/shared/TransportConnection.Generated.cs @@ -8,19 +8,21 @@ using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; +#nullable enable + namespace Microsoft.AspNetCore.Connections { internal partial class TransportConnection : IFeatureCollection { - private object _currentIConnectionIdFeature; - private object _currentIConnectionTransportFeature; - private object _currentIConnectionItemsFeature; - private object _currentIMemoryPoolFeature; - private object _currentIConnectionLifetimeFeature; + private object? _currentIConnectionIdFeature; + private object? _currentIConnectionTransportFeature; + private object? _currentIConnectionItemsFeature; + private object? _currentIMemoryPoolFeature; + private object? _currentIConnectionLifetimeFeature; private int _featureRevision; - private List> MaybeExtra; + private List>? MaybeExtra; private void FastReset() { @@ -40,7 +42,7 @@ internal void ResetFeatureCollection() _featureRevision++; } - private object ExtraFeatureGet(Type key) + private object? ExtraFeatureGet(Type key) { if (MaybeExtra == null) { @@ -57,33 +59,50 @@ private object ExtraFeatureGet(Type key) return null; } - private void ExtraFeatureSet(Type key, object value) + private void ExtraFeatureSet(Type key, object? value) { - if (MaybeExtra == null) + if (value == null) { - MaybeExtra = new List>(2); + if (MaybeExtra == null) + { + return; + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra.RemoveAt(i); + return; + } + } } - - for (var i = 0; i < MaybeExtra.Count; i++) + else { - if (MaybeExtra[i].Key == key) + if (MaybeExtra == null) { - MaybeExtra[i] = new KeyValuePair(key, value); - return; + MaybeExtra = new List>(2); + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra[i] = new KeyValuePair(key, value); + return; + } } + MaybeExtra.Add(new KeyValuePair(key, value)); } - MaybeExtra.Add(new KeyValuePair(key, value)); } bool IFeatureCollection.IsReadOnly => false; int IFeatureCollection.Revision => _featureRevision; - object IFeatureCollection.this[Type key] + object? IFeatureCollection.this[Type key] { get { - object feature = null; + object? feature = null; if (key == typeof(IConnectionIdFeature)) { feature = _currentIConnectionIdFeature; @@ -143,38 +162,38 @@ object IFeatureCollection.this[Type key] } } - TFeature IFeatureCollection.Get() + TFeature? IFeatureCollection.Get() where TFeature : default { - TFeature feature = default; + TFeature? feature = default; if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - feature = (TFeature)_currentIConnectionIdFeature; + feature = (TFeature?)_currentIConnectionIdFeature; } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - feature = (TFeature)_currentIConnectionTransportFeature; + feature = (TFeature?)_currentIConnectionTransportFeature; } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - feature = (TFeature)_currentIConnectionItemsFeature; + feature = (TFeature?)_currentIConnectionItemsFeature; } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - feature = (TFeature)_currentIMemoryPoolFeature; + feature = (TFeature?)_currentIMemoryPoolFeature; } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - feature = (TFeature)_currentIConnectionLifetimeFeature; + feature = (TFeature?)_currentIConnectionLifetimeFeature; } else if (MaybeExtra != null) { - feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); + feature = (TFeature?)(ExtraFeatureGet(typeof(TFeature))); } return feature; } - void IFeatureCollection.Set(TFeature feature) + void IFeatureCollection.Set(TFeature? feature) where TFeature : default { _featureRevision++; if (typeof(TFeature) == typeof(IConnectionIdFeature)) diff --git a/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs b/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs index e7df1de198e7..cdfc0bff16c3 100644 --- a/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs +++ b/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs @@ -8,19 +8,21 @@ using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; +#nullable enable + namespace Microsoft.AspNetCore.Connections { internal partial class TransportMultiplexedConnection : IFeatureCollection { - private object _currentIConnectionIdFeature; - private object _currentIConnectionTransportFeature; - private object _currentIConnectionItemsFeature; - private object _currentIMemoryPoolFeature; - private object _currentIConnectionLifetimeFeature; + private object? _currentIConnectionIdFeature; + private object? _currentIConnectionTransportFeature; + private object? _currentIConnectionItemsFeature; + private object? _currentIMemoryPoolFeature; + private object? _currentIConnectionLifetimeFeature; private int _featureRevision; - private List> MaybeExtra; + private List>? MaybeExtra; private void FastReset() { @@ -40,7 +42,7 @@ internal void ResetFeatureCollection() _featureRevision++; } - private object ExtraFeatureGet(Type key) + private object? ExtraFeatureGet(Type key) { if (MaybeExtra == null) { @@ -57,33 +59,50 @@ private object ExtraFeatureGet(Type key) return null; } - private void ExtraFeatureSet(Type key, object value) + private void ExtraFeatureSet(Type key, object? value) { - if (MaybeExtra == null) + if (value == null) { - MaybeExtra = new List>(2); + if (MaybeExtra == null) + { + return; + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra.RemoveAt(i); + return; + } + } } - - for (var i = 0; i < MaybeExtra.Count; i++) + else { - if (MaybeExtra[i].Key == key) + if (MaybeExtra == null) { - MaybeExtra[i] = new KeyValuePair(key, value); - return; + MaybeExtra = new List>(2); + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra[i] = new KeyValuePair(key, value); + return; + } } + MaybeExtra.Add(new KeyValuePair(key, value)); } - MaybeExtra.Add(new KeyValuePair(key, value)); } bool IFeatureCollection.IsReadOnly => false; int IFeatureCollection.Revision => _featureRevision; - object IFeatureCollection.this[Type key] + object? IFeatureCollection.this[Type key] { get { - object feature = null; + object? feature = null; if (key == typeof(IConnectionIdFeature)) { feature = _currentIConnectionIdFeature; @@ -143,38 +162,38 @@ object IFeatureCollection.this[Type key] } } - TFeature IFeatureCollection.Get() + TFeature? IFeatureCollection.Get() where TFeature : default { - TFeature feature = default; + TFeature? feature = default; if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - feature = (TFeature)_currentIConnectionIdFeature; + feature = (TFeature?)_currentIConnectionIdFeature; } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - feature = (TFeature)_currentIConnectionTransportFeature; + feature = (TFeature?)_currentIConnectionTransportFeature; } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - feature = (TFeature)_currentIConnectionItemsFeature; + feature = (TFeature?)_currentIConnectionItemsFeature; } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - feature = (TFeature)_currentIMemoryPoolFeature; + feature = (TFeature?)_currentIMemoryPoolFeature; } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - feature = (TFeature)_currentIConnectionLifetimeFeature; + feature = (TFeature?)_currentIConnectionLifetimeFeature; } else if (MaybeExtra != null) { - feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); + feature = (TFeature?)(ExtraFeatureGet(typeof(TFeature))); } return feature; } - void IFeatureCollection.Set(TFeature feature) + void IFeatureCollection.Set(TFeature? feature) where TFeature : default { _featureRevision++; if (typeof(TFeature) == typeof(IConnectionIdFeature)) diff --git a/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs b/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs index 0e91c137d4e7..3f75cd4e21e6 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs @@ -27,15 +27,17 @@ public static string GenerateFile(string namespaceName, string className, string using System.Collections.Generic; {extraUsings} +#nullable enable + namespace {namespaceName} {{ internal partial class {className} : IFeatureCollection {{{Each(features, feature => $@" - private object _current{feature.Name};")} + private object? _current{feature.Name};")} private int _featureRevision; - private List> MaybeExtra; + private List>? MaybeExtra; private void FastReset() {{{Each(implementedFeatures, feature => $@" @@ -52,7 +54,7 @@ internal void ResetFeatureCollection() _featureRevision++; }} - private object ExtraFeatureGet(Type key) + private object? ExtraFeatureGet(Type key) {{ if (MaybeExtra == null) {{ @@ -69,33 +71,50 @@ private object ExtraFeatureGet(Type key) return null; }} - private void ExtraFeatureSet(Type key, object value) + private void ExtraFeatureSet(Type key, object? value) {{ - if (MaybeExtra == null) + if (value == null) {{ - MaybeExtra = new List>(2); + if (MaybeExtra == null) + {{ + return; + }} + for (var i = 0; i < MaybeExtra.Count; i++) + {{ + if (MaybeExtra[i].Key == key) + {{ + MaybeExtra.RemoveAt(i); + return; + }} + }} }} - - for (var i = 0; i < MaybeExtra.Count; i++) + else {{ - if (MaybeExtra[i].Key == key) + if (MaybeExtra == null) {{ - MaybeExtra[i] = new KeyValuePair(key, value); - return; + MaybeExtra = new List>(2); + }} + for (var i = 0; i < MaybeExtra.Count; i++) + {{ + if (MaybeExtra[i].Key == key) + {{ + MaybeExtra[i] = new KeyValuePair(key, value); + return; + }} }} + MaybeExtra.Add(new KeyValuePair(key, value)); }} - MaybeExtra.Add(new KeyValuePair(key, value)); }} bool IFeatureCollection.IsReadOnly => false; int IFeatureCollection.Revision => _featureRevision; - object IFeatureCollection.this[Type key] + object? IFeatureCollection.this[Type key] {{ get {{ - object feature = null;{Each(features, feature => $@" + object? feature = null;{Each(features, feature => $@" {(feature.Index != 0 ? "else " : "")}if (key == typeof({feature.Name})) {{ feature = _current{feature.Name}; @@ -123,16 +142,16 @@ object IFeatureCollection.this[Type key] }} }} - TFeature IFeatureCollection.Get() + TFeature? IFeatureCollection.Get() where TFeature : default {{ - TFeature feature = default;{Each(features, feature => $@" + TFeature? feature = default;{Each(features, feature => $@" {(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name})) {{ - feature = (TFeature)_current{feature.Name}; + feature = (TFeature?)_current{feature.Name}; }}")} else if (MaybeExtra != null) {{ - feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); + feature = (TFeature?)(ExtraFeatureGet(typeof(TFeature))); }}{(string.IsNullOrEmpty(fallbackFeatures) ? "" : $@" if (feature == null) @@ -143,7 +162,7 @@ TFeature IFeatureCollection.Get() return feature; }} - void IFeatureCollection.Set(TFeature feature) + void IFeatureCollection.Set(TFeature? feature) where TFeature : default {{ _featureRevision++;{Each(features, feature => $@" {(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name})) diff --git a/src/Servers/Kestrel/tools/CodeGenerator/HttpUtilities/HttpUtilities.cs b/src/Servers/Kestrel/tools/CodeGenerator/HttpUtilities/HttpUtilities.cs index 4edf81c09377..bb07d8713e5d 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/HttpUtilities/HttpUtilities.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/HttpUtilities/HttpUtilities.cs @@ -68,6 +68,8 @@ private static string GenerateFile(Tuple[] httpMethods) using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +#nullable enable + namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure {{ internal static partial class HttpUtilities diff --git a/src/Servers/Kestrel/tools/CodeGenerator/ReadOnlySpanStaticDataGenerator.cs b/src/Servers/Kestrel/tools/CodeGenerator/ReadOnlySpanStaticDataGenerator.cs index 96be0757adc0..9360db249197 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/ReadOnlySpanStaticDataGenerator.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/ReadOnlySpanStaticDataGenerator.cs @@ -24,6 +24,8 @@ public static string GenerateFile(string namespaceName, string className, IEnume using System; +#nullable enable + namespace {namespaceName} {{ internal partial class {className} diff --git a/src/SignalR/common/Http.Connections/src/Internal/Transports/ServerSentEventsServerTransport.cs b/src/SignalR/common/Http.Connections/src/Internal/Transports/ServerSentEventsServerTransport.cs index b9bc12dbae5f..ffda642fa44a 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/Transports/ServerSentEventsServerTransport.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/Transports/ServerSentEventsServerTransport.cs @@ -39,7 +39,7 @@ public async Task ProcessRequestAsync(HttpContext context, CancellationToken tok context.Response.Headers[HeaderNames.Pragma] = "no-cache"; // Make sure we disable all response buffering for SSE - var bufferingFeature = context.Features.Get(); + var bufferingFeature = context.Features.Get()!; bufferingFeature.DisableBuffering(); context.Response.Headers[HeaderNames.ContentEncoding] = "identity";