diff --git a/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs b/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs index c5f57d03453e7b..c01911b5fc6527 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs @@ -13,11 +13,11 @@ namespace Microsoft.Extensions.Http // for the 'expiry' pool simplifies the threading requirements significantly. internal sealed class ActiveHandlerTrackingEntry { - private static readonly TimerCallback _timerCallback = (s) => ((ActiveHandlerTrackingEntry)s).Timer_Tick(); + private static readonly TimerCallback _timerCallback = (s) => ((ActiveHandlerTrackingEntry)s!).Timer_Tick(); private readonly object _lock; private bool _timerInitialized; - private Timer _timer; - private TimerCallback _callback; + private Timer? _timer; + private TimerCallback? _callback; public ActiveHandlerTrackingEntry( string name, diff --git a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpClientFactory.cs b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpClientFactory.cs index 943a68ebf6ab9a..c76ff519b43b07 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpClientFactory.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpClientFactory.cs @@ -17,7 +17,7 @@ namespace Microsoft.Extensions.Http { internal class DefaultHttpClientFactory : IHttpClientFactory, IHttpMessageHandlerFactory { - private static readonly TimerCallback _cleanupCallback = (s) => ((DefaultHttpClientFactory)s).CleanupTimer_Tick(); + private static readonly TimerCallback _cleanupCallback = (s) => ((DefaultHttpClientFactory)s!).CleanupTimer_Tick(); private readonly ILogger _logger; private readonly IServiceProvider _services; private readonly IServiceScopeFactory _scopeFactory; @@ -37,7 +37,7 @@ internal class DefaultHttpClientFactory : IHttpClientFactory, IHttpMessageHandle // // There's no need for the factory itself to be disposable. If you stop using it, eventually everything will // get reclaimed. - private Timer _cleanupTimer; + private Timer? _cleanupTimer; private readonly object _cleanupTimerLock; private readonly object _cleanupActiveLock; @@ -116,7 +116,7 @@ public HttpMessageHandler CreateHandler(string name!!) internal ActiveHandlerTrackingEntry CreateHandlerEntry(string name) { IServiceProvider services = _services; - var scope = (IServiceScope)null; + var scope = (IServiceScope?)null; HttpClientFactoryOptions options = _optionsMonitor.Get(name); if (!options.SuppressHandlerScope) @@ -150,7 +150,7 @@ internal ActiveHandlerTrackingEntry CreateHandlerEntry(string name) // Otherwise it would be possible that we start the timer here, immediately expire it (very short // timer) and then dispose it without ever creating a client. That would be bad. It's unlikely // this would happen, but we want to be sure. - return new ActiveHandlerTrackingEntry(name, handler, scope, options.HandlerLifetime); + return new ActiveHandlerTrackingEntry(name, handler, scope!, options.HandlerLifetime); void Configure(HttpMessageHandlerBuilder b) { @@ -169,15 +169,15 @@ void Configure(HttpMessageHandlerBuilder b) } // Internal for tests - internal void ExpiryTimer_Tick(object state) + internal void ExpiryTimer_Tick(object? state) { - var active = (ActiveHandlerTrackingEntry)state; + var active = (ActiveHandlerTrackingEntry)state!; // The timer callback should be the only one removing from the active collection. If we can't find // our entry in the collection, then this is a bug. - bool removed = _activeHandlers.TryRemove(active.Name, out Lazy found); + bool removed = _activeHandlers.TryRemove(active.Name, out Lazy? found); Debug.Assert(removed, "Entry not found. We should always be able to remove the entry"); - Debug.Assert(object.ReferenceEquals(active, found.Value), "Different entry found. The entry should not have been replaced"); + Debug.Assert(object.ReferenceEquals(active, found!.Value), "Different entry found. The entry should not have been replaced"); // At this point the handler is no longer 'active' and will not be handed out to any new clients. // However we haven't dropped our strong reference to the handler, so we can't yet determine if @@ -211,12 +211,13 @@ internal virtual void StartCleanupTimer() } } + // Internal so it can be overridden in tests internal virtual void StopCleanupTimer() { lock (_cleanupTimerLock) { - _cleanupTimer.Dispose(); + _cleanupTimer?.Dispose(); _cleanupTimer = null; } } @@ -257,7 +258,7 @@ internal void CleanupTimer_Tick() for (int i = 0; i < initialCount; i++) { // Since we're the only one removing from _expired, TryDequeue must always succeed. - _expiredHandlers.TryDequeue(out ExpiredHandlerTrackingEntry entry); + _expiredHandlers.TryDequeue(out ExpiredHandlerTrackingEntry? entry); Debug.Assert(entry != null, "Entry was null, we should always get an entry back from TryDequeue"); if (entry.CanDispose) @@ -305,12 +306,12 @@ public static class EventIds public static readonly EventId HandlerExpired = new EventId(103, "HandlerExpired"); } - private static readonly Action _cleanupCycleStart = LoggerMessage.Define( + private static readonly Action _cleanupCycleStart = LoggerMessage.Define( LogLevel.Debug, EventIds.CleanupCycleStart, "Starting HttpMessageHandler cleanup cycle with {InitialCount} items"); - private static readonly Action _cleanupCycleEnd = LoggerMessage.Define( + private static readonly Action _cleanupCycleEnd = LoggerMessage.Define( LogLevel.Debug, EventIds.CleanupCycleEnd, "Ending HttpMessageHandler cleanup cycle after {ElapsedMilliseconds}ms - processed: {DisposedCount} items - remaining: {RemainingItems} items"); @@ -320,7 +321,7 @@ public static class EventIds EventIds.CleanupItemFailed, "HttpMessageHandler.Dispose() threw an unhandled exception for client: '{ClientName}'"); - private static readonly Action _handlerExpired = LoggerMessage.Define( + private static readonly Action _handlerExpired = LoggerMessage.Define( LogLevel.Debug, EventIds.HandlerExpired, "HttpMessageHandler expired after {HandlerLifetime}ms for client '{ClientName}'"); diff --git a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs index 3b8f2324289b1e..a707c4b34f08d2 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Net.Http; namespace Microsoft.Extensions.Http @@ -14,9 +15,9 @@ public DefaultHttpMessageHandlerBuilder(IServiceProvider services) Services = services; } - private string _name; + private string? _name; - public override string Name + public override string? Name { get => _name; set diff --git a/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs b/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs index 4b913fc8d4dc97..9c0714e0960f70 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs @@ -33,15 +33,19 @@ public class Cache { private static readonly Func _createActivator = () => ActivatorUtilities.CreateFactory(typeof(TClient), new Type[] { typeof(HttpClient), }); - private ObjectFactory _activator; + private ObjectFactory? _activator; private bool _initialized; - private object _lock; + private object? _lock; + // [MemberNotNull(nameof(_activator))] + // [MemberNotNull(nameof(_initialized))] + // [MemberNotNull(nameof(_lock))] + // [MemberNotNull(nameof(_createActivator))] public ObjectFactory Activator => LazyInitializer.EnsureInitialized( ref _activator, ref _initialized, ref _lock, - _createActivator); + _createActivator)!; } } } diff --git a/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientBuilderExtensions.cs index 8162d9af2ec840..281c29573b8d7c 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientBuilderExtensions.cs @@ -488,11 +488,11 @@ public static IHttpClientBuilder SetHandlerLifetime(this IHttpClientBuilder buil // See comments on HttpClientMappingRegistry. private static void ReserveClient(IHttpClientBuilder builder, Type type, string name, bool validateSingleType) { - var registry = (HttpClientMappingRegistry)builder.Services.Single(sd => sd.ServiceType == typeof(HttpClientMappingRegistry)).ImplementationInstance; + var registry = (HttpClientMappingRegistry?)builder.Services.Single(sd => sd.ServiceType == typeof(HttpClientMappingRegistry)).ImplementationInstance; Debug.Assert(registry != null); // Check for same name registered to two types. This won't work because we rely on named options for the configuration. - if (registry.NamedClientRegistrations.TryGetValue(name, out Type otherType) && + if (registry.NamedClientRegistrations.TryGetValue(name, out Type? otherType) && // Allow using the same name with multiple types in some cases (see callers). validateSingleType && diff --git a/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs b/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs index b5c53f810a26c9..92c8c9130190be 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs @@ -20,7 +20,7 @@ public ExpiredHandlerTrackingEntry(ActiveHandlerTrackingEntry other) Scope = other.Scope; _livenessTracker = new WeakReference(other.Handler); - InnerHandler = other.Handler.InnerHandler; + InnerHandler = other.Handler.InnerHandler!; } public bool CanDispose => !_livenessTracker.IsAlive; diff --git a/src/libraries/Microsoft.Extensions.Http/src/HttpMessageHandlerBuilder.cs b/src/libraries/Microsoft.Extensions.Http/src/HttpMessageHandlerBuilder.cs index 9bbe559fe6a913..4c791815d80164 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/HttpMessageHandlerBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/HttpMessageHandlerBuilder.cs @@ -26,7 +26,7 @@ public abstract class HttpMessageHandlerBuilder /// and is public for unit testing purposes only. Setting the outside of /// testing scenarios may have unpredictable results. /// - public abstract string Name { get; set; } + public abstract string? Name { get; set; } /// /// Gets or sets the primary . @@ -50,7 +50,7 @@ public abstract class HttpMessageHandlerBuilder /// (default) this will be a reference to a scoped service provider that has the same /// lifetime as the handler being created. /// - public virtual IServiceProvider Services { get; } + public virtual IServiceProvider Services { get; } = null!; /// /// Creates an . diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs index dc92d3f659dfa7..802192c4a905c9 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs @@ -14,10 +14,10 @@ internal sealed class HttpHeadersLogValue : IReadOnlyList _shouldRedactHeaderValue; - private string _formatted; - private List> _values; + private string? _formatted; + private List>? _values; - public HttpHeadersLogValue(Kind kind, HttpHeaders headers, HttpHeaders contentHeaders, Func shouldRedactHeaderValue) + public HttpHeadersLogValue(Kind kind, HttpHeaders headers, HttpHeaders? contentHeaders, Func shouldRedactHeaderValue) { _kind = kind; _shouldRedactHeaderValue = shouldRedactHeaderValue; @@ -28,7 +28,7 @@ public HttpHeadersLogValue(Kind kind, HttpHeaders headers, HttpHeaders contentHe public HttpHeaders Headers { get; } - public HttpHeaders ContentHeaders { get; } + public HttpHeaders? ContentHeaders { get; } private List> Values { diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs index c7c502be236e0c..93e56304a753e1 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -16,7 +17,7 @@ namespace Microsoft.Extensions.Http.Logging public class LoggingHttpMessageHandler : DelegatingHandler { private ILogger _logger; - private readonly HttpClientFactoryOptions _options; + private readonly HttpClientFactoryOptions? _options; private static readonly Func _shouldNotRedactHeaderValue = (header) => false; @@ -72,13 +73,13 @@ public static class EventIds private static readonly LogDefineOptions _skipEnabledCheckLogDefineOptions = new LogDefineOptions() { SkipEnabledCheck = true }; - private static readonly Action _requestStart = LoggerMessage.Define( + private static readonly Action _requestStart = LoggerMessage.Define( LogLevel.Information, EventIds.RequestStart, "Sending HTTP request {HttpMethod} {Uri}", _skipEnabledCheckLogDefineOptions); - private static readonly Action _requestEnd = LoggerMessage.Define( + private static readonly Action _requestEnd = LoggerMessage.Define( LogLevel.Information, EventIds.RequestEnd, "Received HTTP response headers after {ElapsedMilliseconds}ms - {StatusCode}"); @@ -88,7 +89,7 @@ public static void RequestStart(ILogger logger, HttpRequestMessage request, Func // We check here to avoid allocating in the GetUriString call unnecessarily if (logger.IsEnabled(LogLevel.Information)) { - _requestStart(logger, request.Method, GetUriString(request.RequestUri), null); + _requestStart(logger, request.Method, GetUriString(request.RequestUri)!, null); } if (logger.IsEnabled(LogLevel.Trace)) diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs index 6b9536b92e01ed..ce5c19c4541f5e 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs @@ -16,7 +16,7 @@ namespace Microsoft.Extensions.Http.Logging public class LoggingScopeHttpMessageHandler : DelegatingHandler { private ILogger _logger; - private readonly HttpClientFactoryOptions _options; + private readonly HttpClientFactoryOptions? _options; private static readonly Func _shouldNotRedactHeaderValue = (header) => false; @@ -72,26 +72,26 @@ public static class EventIds public static readonly EventId ResponseHeader = new EventId(103, "RequestPipelineResponseHeader"); } - private static readonly Func _beginRequestPipelineScope = LoggerMessage.DefineScope("HTTP {HttpMethod} {Uri}"); + private static readonly Func _beginRequestPipelineScope = LoggerMessage.DefineScope("HTTP {HttpMethod} {Uri}"); - private static readonly Action _requestPipelineStart = LoggerMessage.Define( + private static readonly Action _requestPipelineStart = LoggerMessage.Define( LogLevel.Information, EventIds.PipelineStart, "Start processing HTTP request {HttpMethod} {Uri}"); - private static readonly Action _requestPipelineEnd = LoggerMessage.Define( + private static readonly Action _requestPipelineEnd = LoggerMessage.Define( LogLevel.Information, EventIds.PipelineEnd, "End processing HTTP request after {ElapsedMilliseconds}ms - {StatusCode}"); - public static IDisposable BeginRequestPipelineScope(ILogger logger, HttpRequestMessage request) + public static IDisposable? BeginRequestPipelineScope(ILogger logger, HttpRequestMessage request) { - return _beginRequestPipelineScope(logger, request.Method, GetUriString(request.RequestUri)); + return _beginRequestPipelineScope(logger, request.Method, GetUriString(request.RequestUri)!); } public static void RequestPipelineStart(ILogger logger, HttpRequestMessage request, Func shouldRedactHeaderValue) { - _requestPipelineStart(logger, request.Method, GetUriString(request.RequestUri), null); + _requestPipelineStart(logger, request.Method, GetUriString(request.RequestUri)!, null); if (logger.IsEnabled(LogLevel.Trace)) { diff --git a/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj b/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj index 968c50578d8909..b8ee1f92537fae 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj +++ b/src/libraries/Microsoft.Extensions.Http/src/Microsoft.Extensions.Http.csproj @@ -6,7 +6,7 @@ false true - Annotations + enable The HttpClient factory is a pattern for configuring and retrieving named HttpClients in a composable way. The HttpClient factory provides extensibility to plug in DelegatingHandlers that address cross-cutting concerns such as service location, load balancing, and reliability. The default HttpClient factory provides built-in diagnostics and logging and manages the lifetimes of connections in a performant way. Commonly Used Types: