// 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; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.Builder; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Hosting.Views; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.StackTrace.Sources; namespace Microsoft.AspNetCore.Hosting.Internal { public class WebHost : IWebHost { private static readonly 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 IServiceProvider _applicationServices; private RequestDelegate _application; private ILogger _logger; // Used for testing only internal WebHostOptions Options => _options; private IServer Server { get; set; } public WebHost( IServiceCollection appServices, IServiceProvider hostingServiceProvider, WebHostOptions options, IConfiguration config) { if (appServices == null) { throw new ArgumentNullException(nameof(appServices)); } if (hostingServiceProvider == null) { throw new ArgumentNullException(nameof(hostingServiceProvider)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } _config = config; _options = options; _applicationServiceCollection = appServices; _hostingServiceProvider = hostingServiceProvider; _applicationServiceCollection.AddSingleton(); _applicationServiceCollection.AddSingleton(); } public IServiceProvider Services { get { EnsureApplicationServices(); return _applicationServices; } } public IFeatureCollection ServerFeatures { get { return Server?.Features; } } public void Initialize() { if (_application == null) { _application = BuildApplication(); } } public virtual void Start() { HostingEventSource.Log.HostStart(); _logger = _applicationServices.GetRequiredService>(); _logger.Starting(); Initialize(); _applicationLifetime = _applicationServices.GetRequiredService() as ApplicationLifetime; _hostedServiceExecutor = _applicationServices.GetRequiredService(); var diagnosticSource = _applicationServices.GetRequiredService(); var httpContextFactory = _applicationServices.GetRequiredService(); Server.Start(new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory)); // Fire IApplicationLifetime.Started _applicationLifetime?.NotifyStarted(); // Fire IHostedService.Start _hostedServiceExecutor.Start(); _logger.Started(); } private void EnsureApplicationServices() { if (_applicationServices == null) { EnsureStartup(); _applicationServices = _startup.ConfigureServices(_applicationServiceCollection); } } private void EnsureStartup() { if (_startup != null) { return; } _startup = _hostingServiceProvider.GetRequiredService(); } private RequestDelegate BuildApplication() { try { EnsureApplicationServices(); EnsureServer(); var builderFactory = _applicationServices.GetRequiredService(); var builder = builderFactory.CreateBuilder(Server.Features); builder.ApplicationServices = _applicationServices; var startupFilters = _applicationServices.GetService>(); Action configure = _startup.Configure; foreach (var filter in startupFilters.Reverse()) { configure = filter.Configure(configure); } configure(builder); return builder.Build(); } catch (Exception ex) when (_options.CaptureStartupErrors) { // EnsureApplicationServices may have failed due to a missing or throwing Startup class. if (_applicationServices == null) { _applicationServices = _applicationServiceCollection.BuildServiceProvider(); } EnsureServer(); // Write errors to standard out so they can be retrieved when not in development mode. Console.Out.WriteLine("Application startup exception: " + ex.ToString()); var logger = _applicationServices.GetRequiredService>(); logger.ApplicationError(ex); // Generate an HTML error page. var hostingEnv = _applicationServices.GetRequiredService(); var showDetailedErrors = hostingEnv.IsDevelopment() || _options.DetailedErrors; var model = new ErrorPageModel(); var runtimeType = Microsoft.Extensions.Internal.RuntimeEnvironment.RuntimeType; model.RuntimeDisplayName = (runtimeType == "CoreCLR") ? ".NET Core" : runtimeType == "CLR" ? ".NET Framework" : "Mono"; #if NETSTANDARD1_3 || NETSTANDARD1_5 var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).GetTypeInfo().Assembly; var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString(); var clrVersion = assemblyVersion; #else var clrVersion = Environment.Version.ToString(); #endif model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString(); var currentAssembly = typeof(ErrorPage).GetTypeInfo().Assembly; model.CurrentAssemblyVesion = currentAssembly .GetCustomAttribute() .InformationalVersion; model.ClrVersion = clrVersion; model.OperatingSystemDescription = RuntimeInformation.OSDescription; if (showDetailedErrors) { var exceptionDetailProvider = new ExceptionDetailsProvider( hostingEnv.ContentRootFileProvider, sourceCodeLineCount: 6); model.ErrorDetails = exceptionDetailProvider.GetDetails(ex); } else { model.ErrorDetails = new ExceptionDetails[0]; } var errorPage = new ErrorPage(model); return context => { context.Response.StatusCode = 500; context.Response.Headers["Cache-Control"] = "no-cache"; return errorPage.ExecuteAsync(context); }; } } private void EnsureServer() { if (Server == null) { Server = _applicationServices.GetRequiredService(); var addresses = Server.Features?.Get()?.Addresses; if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0) { var urls = _config[WebHostDefaults.ServerUrlsKey] ?? _config[DeprecatedServerUrlsKey]; if (!string.IsNullOrEmpty(urls)) { foreach (var value in urls.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { addresses.Add(value); } } } } } public void Dispose() { _logger?.Shutdown(); // Fire IApplicationLifetime.Stopping _applicationLifetime?.StopApplication(); // Fire the IHostedService.Stop _hostedServiceExecutor?.Stop(); (_hostingServiceProvider as IDisposable)?.Dispose(); (_applicationServices as IDisposable)?.Dispose(); // Fire IApplicationLifetime.Stopped _applicationLifetime?.NotifyStopped(); HostingEventSource.Log.HostStop(); } } }