public Action <HttpMessageHandlerBuilder> Configure(Action <HttpMessageHandlerBuilder> next) { if (next == null) { throw new ArgumentNullException(nameof(next)); } return((builder) => { // Run other configuration first, we want to decorate. next(builder); string loggerName = !string.IsNullOrEmpty(builder.Name) ? builder.Name : "Default"; // We want all of our logging message to show up as-if they are coming from HttpClient, // but also to include the name of the client for more fine-grained control. ILogger outerLogger = _loggerFactory.CreateLogger($"System.Net.Http.HttpClient.{loggerName}.LogicalHandler"); ILogger innerLogger = _loggerFactory.CreateLogger($"System.Net.Http.HttpClient.{loggerName}.ClientHandler"); HttpClientFactoryOptions options = _optionsMonitor.Get(builder.Name); // The 'scope' handler goes first so it can surround everything. builder.AdditionalHandlers.Insert(0, new LoggingScopeHttpMessageHandler(outerLogger, options)); // We want this handler to be last so we can log details about the request after // service discovery and security happen. builder.AdditionalHandlers.Add(new LoggingHttpMessageHandler(innerLogger, options)); }); }
// Internal for tests internal ActiveHandlerTrackingEntry CreateHandlerEntry(string name) { IServiceProvider services = _services; var scope = (IServiceScope?)null; HttpClientFactoryOptions options = _optionsMonitor.Get(name); if (!options.SuppressHandlerScope) { scope = _scopeFactory.CreateScope(); services = scope.ServiceProvider; } try { HttpMessageHandlerBuilder builder = services.GetRequiredService <HttpMessageHandlerBuilder>(); builder.Name = name; // This is similar to the initialization pattern in: // https://github.com/aspnet/Hosting/blob/e892ed8bbdcd25a0dafc1850033398dc57f65fe1/src/Microsoft.AspNetCore.Hosting/Internal/WebHost.cs#L188 Action <HttpMessageHandlerBuilder> configure = Configure; for (int i = _filters.Length - 1; i >= 0; i--) { configure = _filters[i].Configure(configure); } configure(builder); // Wrap the handler so we can ensure the inner handler outlives the outer handler. var handler = new LifetimeTrackingHttpMessageHandler(builder.Build()); // Note that we can't start the timer here. That would introduce a very very subtle race condition // with very short expiry times. We need to wait until we've actually handed out the handler once // to start the timer. // // 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)); void Configure(HttpMessageHandlerBuilder b) { for (int i = 0; i < options.HttpMessageHandlerBuilderActions.Count; i++) { options.HttpMessageHandlerBuilderActions[i](b); } } } catch { // If something fails while creating the handler, dispose the services. scope?.Dispose(); throw; } }
public HttpClient CreateClient(string name) { ThrowHelper.ThrowIfNull(name); HttpMessageHandler handler = CreateHandler(name); var client = new HttpClient(handler, disposeHandler: false); HttpClientFactoryOptions options = _optionsMonitor.Get(name); for (int i = 0; i < options.HttpClientActions.Count; i++) { options.HttpClientActions[i](client); } return(client); }
public HttpClient CreateClient(string name) { if (name == null) { throw new ArgumentNullException(nameof(name)); } HttpMessageHandler handler = CreateHandler(name); var client = new HttpClient(handler, disposeHandler: false); HttpClientFactoryOptions options = _optionsMonitor.Get(name); for (int i = 0; i < options.HttpClientActions.Count; i++) { options.HttpClientActions[i](client); } return(client); }