| File: Internal\WebHost.cs | Web Access |
| Project: src\aspnetcore\src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj (Microsoft.AspNetCore.Hosting) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.ExceptionServices; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.Builder; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.Hosting; [Obsolete("WebHost is obsolete. Use Host.CreateDefaultBuilder or WebApplication.CreateBuilder instead.")] internal sealed partial class WebHost : IWebHost, IAsyncDisposable { private const string DeprecatedServerUrlsKey = "server.urls"; private readonly IServiceCollection _applicationServiceCollection; private IStartup? _startup; private ApplicationLifetime? _applicationLifetime; private HostedServiceExecutor? _hostedServiceExecutor; private readonly IServiceProvider _hostingServiceProvider; private readonly WebHostOptions _options; private readonly IConfiguration _config; private readonly AggregateException? _hostingStartupErrors; private IServiceProvider? _applicationServices; private ExceptionDispatchInfo? _applicationServicesException; private ILogger _logger = NullLogger.Instance; private bool _stopped; private bool _startedServer; // Used for testing only internal WebHostOptions Options => _options; private IServer? Server { get; set; } public WebHost( IServiceCollection appServices, IServiceProvider hostingServiceProvider, WebHostOptions options, IConfiguration config, AggregateException? hostingStartupErrors) { ArgumentNullException.ThrowIfNull(appServices); ArgumentNullException.ThrowIfNull(hostingServiceProvider); ArgumentNullException.ThrowIfNull(config); _config = config; _hostingStartupErrors = hostingStartupErrors; _options = options; _applicationServiceCollection = appServices; _hostingServiceProvider = hostingServiceProvider; _applicationServiceCollection.AddSingleton<ApplicationLifetime>(); // There's no way to to register multiple service types per definition. See https://github.com/aspnet/DependencyInjection/issues/360 _applicationServiceCollection.AddSingleton<IHostApplicationLifetime>(services => services.GetService<ApplicationLifetime>()!); #pragma warning disable CS0618 // Type or member is obsolete _applicationServiceCollection.AddSingleton<AspNetCore.Hosting.IApplicationLifetime>(services => services.GetService<ApplicationLifetime>()!); _applicationServiceCollection.AddSingleton<Extensions.Hosting.IApplicationLifetime>(services => services.GetService<ApplicationLifetime>()!); #pragma warning restore CS0618 // Type or member is obsolete _applicationServiceCollection.AddSingleton<HostedServiceExecutor>(); } public IServiceProvider Services { get { Debug.Assert(_applicationServices != null, "Initialize must be called before accessing services."); return _applicationServices; } } public IFeatureCollection ServerFeatures { get { EnsureServer(); return Server.Features; } } // Called immediately after the constructor so the properties can rely on it. public void Initialize() { try { EnsureApplicationServices(); } catch (Exception ex) { // EnsureApplicationServices may have failed due to a missing or throwing Startup class. if (_applicationServices == null) { _applicationServices = _applicationServiceCollection.BuildServiceProvider(); } if (!_options.CaptureStartupErrors) { throw; } _applicationServicesException = ExceptionDispatchInfo.Capture(ex); } } public void Start() { StartAsync().GetAwaiter().GetResult(); } public async Task StartAsync(CancellationToken cancellationToken = default) { Debug.Assert(_applicationServices != null, "Initialize must be called first."); HostingEventSource.Log.HostStart(); _logger = _applicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("Microsoft.AspNetCore.Hosting.Diagnostics"); Log.Starting(_logger); var application = BuildApplication(); _applicationLifetime = _applicationServices.GetRequiredService<ApplicationLifetime>(); _hostedServiceExecutor = _applicationServices.GetRequiredService<HostedServiceExecutor>(); // Fire IHostedService.Start await _hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false); var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticListener>(); var activitySource = _applicationServices.GetRequiredService<ActivitySource>(); var propagator = _applicationServices.GetRequiredService<DistributedContextPropagator>(); var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>(); var hostingMetrics = _applicationServices.GetRequiredService<HostingMetrics>(); var hostingApp = new HostingApplication(application, _logger, diagnosticSource, activitySource, propagator, httpContextFactory, HostingEventSource.Log, hostingMetrics); await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false); _startedServer = true; // Fire IApplicationLifetime.Started _applicationLifetime?.NotifyStarted(); Log.Started(_logger); // Log the fact that we did load hosting startup assemblies. if (_logger.IsEnabled(LogLevel.Debug)) { foreach (var assembly in _options.GetFinalHostingStartupAssemblies()) { Log.StartupAssemblyLoaded(_logger, assembly); } } if (_hostingStartupErrors != null) { foreach (var exception in _hostingStartupErrors.InnerExceptions) { _logger.HostingStartupAssemblyError(exception); } } } private void EnsureApplicationServices() { if (_applicationServices == null) { EnsureStartup(); _applicationServices = _startup.ConfigureServices(_applicationServiceCollection); } } [MemberNotNull(nameof(_startup))] private void EnsureStartup() { if (_startup != null) { return; } var startup = _hostingServiceProvider.GetService<IStartup>(); if (startup == null) { throw new InvalidOperationException($"No application configured. Please specify startup via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, injecting {nameof(IStartup)} or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration."); } _startup = startup; } [MemberNotNull(nameof(Server))] private RequestDelegate BuildApplication() { Debug.Assert(_applicationServices != null, "Initialize must be called first."); try { _applicationServicesException?.Throw(); EnsureServer(); var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>(); var builder = builderFactory.CreateBuilder(Server.Features); builder.ApplicationServices = _applicationServices; var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>(); Action<IApplicationBuilder> configure = _startup!.Configure; if (startupFilters != null) { foreach (var filter in Enumerable.Reverse(startupFilters)) { configure = filter.Configure(configure); } } configure(builder); return builder.Build(); } catch (Exception ex) { if (!_options.SuppressStatusMessages) { // Write errors to standard out so they can be retrieved when not in development mode. Console.WriteLine("Application startup exception: " + ex.ToString()); } var logger = _applicationServices.GetRequiredService<ILogger<WebHost>>(); _logger.ApplicationError(ex); if (!_options.CaptureStartupErrors) { throw; } EnsureServer(); // Generate an HTML error page. var hostingEnv = _applicationServices.GetRequiredService<IHostEnvironment>(); var showDetailedErrors = hostingEnv.IsDevelopment() || _options.DetailedErrors; return ErrorPageBuilder.BuildErrorPageApplication(hostingEnv.ContentRootFileProvider, logger, showDetailedErrors, ex); } } [MemberNotNull(nameof(Server))] private void EnsureServer() { Debug.Assert(_applicationServices != null, "Initialize must be called first."); if (Server == null) { Server = _applicationServices.GetRequiredService<IServer>(); var serverAddressesFeature = Server.Features?.Get<IServerAddressesFeature>(); var addresses = serverAddressesFeature?.Addresses; if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0) { var urls = _config[WebHostDefaults.ServerUrlsKey] ?? _config[DeprecatedServerUrlsKey]; if (!string.IsNullOrEmpty(urls)) { serverAddressesFeature!.PreferHostingUrls = WebHostUtilities.ParseBool(_config[WebHostDefaults.PreferHostingUrlsKey]); foreach (var value in urls.Split(';', StringSplitOptions.RemoveEmptyEntries)) { addresses.Add(value); } } } } } public async Task StopAsync(CancellationToken cancellationToken = default) { if (_stopped) { return; } _stopped = true; Log.Shutdown(_logger); using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cts.CancelAfter(Options.ShutdownTimeout); cancellationToken = cts.Token; // Fire IApplicationLifetime.Stopping _applicationLifetime?.StopApplication(); if (Server != null && _startedServer) { await Server.StopAsync(cancellationToken).ConfigureAwait(false); } // Fire the IHostedService.Stop if (_hostedServiceExecutor != null) { await _hostedServiceExecutor.StopAsync(cancellationToken).ConfigureAwait(false); } // Fire IApplicationLifetime.Stopped _applicationLifetime?.NotifyStopped(); HostingEventSource.Log.HostStop(); } public void Dispose() { DisposeAsync().AsTask().GetAwaiter().GetResult(); } public async ValueTask DisposeAsync() { if (!_stopped) { try { await StopAsync().ConfigureAwait(false); } catch (Exception ex) { Log.ServerShutdownException(_logger, ex); } } await DisposeServiceProviderAsync(_applicationServices).ConfigureAwait(false); await DisposeServiceProviderAsync(_hostingServiceProvider).ConfigureAwait(false); } private static ValueTask DisposeServiceProviderAsync(IServiceProvider? serviceProvider) { switch (serviceProvider) { case IAsyncDisposable asyncDisposable: return asyncDisposable.DisposeAsync(); case IDisposable disposable: disposable.Dispose(); break; } return default; } private static partial class Log { [LoggerMessage(3, LogLevel.Debug, "Hosting starting", EventName = "Starting")] public static partial void Starting(ILogger logger); [LoggerMessage(4, LogLevel.Debug, "Hosting started", EventName = "Started")] public static partial void Started(ILogger logger); [LoggerMessage(5, LogLevel.Debug, "Hosting shutdown", EventName = "Shutdown")] public static partial void Shutdown(ILogger logger); [LoggerMessage(12, LogLevel.Debug, "Server shutdown exception", EventName = "ServerShutdownException")] public static partial void ServerShutdownException(ILogger logger, Exception ex); [LoggerMessage(13, LogLevel.Debug, "Loaded hosting startup assembly {assemblyName}", EventName = "HostingStartupAssemblyLoaded", SkipEnabledCheck = true)] public static partial void StartupAssemblyLoaded(ILogger logger, string assemblyName); } }