using System; using System.Net; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Serilog.Context; using Smidge; using Smidge.Nuglify; using StackExchange.Profiling; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Hosting; using Umbraco.Core.Runtime; using Umbraco.Infrastructure.Logging.Serilog.Enrichers; using Umbraco.Web.Common.Middleware; namespace Umbraco.Extensions { public static class ApplicationBuilderExtensions { /// /// Returns true if Umbraco is greater than /// /// /// public static bool UmbracoCanBoot(this IApplicationBuilder app) { var runtime = app.ApplicationServices.GetRequiredService(); // can't continue if boot failed return runtime.State.Level > RuntimeLevel.BootFailed; } /// /// Start Umbraco /// /// /// public static IApplicationBuilder UseUmbracoCore(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); if (!app.UmbracoCanBoot()) return app; var runtime = app.ApplicationServices.GetRequiredService(); // Register a listener for application shutdown in order to terminate the runtime var hostLifetime = app.ApplicationServices.GetRequiredService(); var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime); hostLifetime.RegisterObject(runtimeShutdown); // Register our global threadabort enricher for logging var threadAbortEnricher = app.ApplicationServices.GetRequiredService(); LogContext.Push(threadAbortEnricher); // NOTE: We are not in a using clause because we are not removing it, it is on the global context // Start the runtime! runtime.Start(app.ApplicationServices); return app; } /// /// Enables middlewares required to run Umbraco /// /// /// // TODO: Could be internal or part of another call - this is a required system so should't be 'opt-in' public static IApplicationBuilder UseUmbracoRouting(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); if (!app.UmbracoCanBoot()) { app.UseMiddleware(); } else { app.UseMiddleware(); app.UseMiddleware(); // TODO: Both of these need to be done before any endpoints but after UmbracoRequestMiddleware // because they rely on an UmbracoContext. But should they be here? app.UseAuthentication(); app.UseAuthorization(); } return app; } /// /// Adds request based serilog enrichers to the LogContext for each request /// /// /// public static IApplicationBuilder UseUmbracoRequestLogging(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); if (!app.UmbracoCanBoot()) return app; app.UseMiddleware(); return app; } /// /// Enables runtime minification for Umbraco /// /// /// public static IApplicationBuilder UseUmbracoRuntimeMinification(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); if (!app.UmbracoCanBoot()) return app; app.UseSmidge(); app.UseSmidgeNuglify(); return app; } /// /// Ensures the runtime is shutdown when the application is shutting down /// private class CoreRuntimeShutdown : IRegisteredObject { public CoreRuntimeShutdown(IRuntime runtime, IApplicationShutdownRegistry hostLifetime) { _runtime = runtime; _hostLifetime = hostLifetime; } private bool _completed = false; private readonly IRuntime _runtime; private readonly IApplicationShutdownRegistry _hostLifetime; public void Stop(bool immediate) { if (!_completed) { _completed = true; _runtime.Terminate(); _hostLifetime.UnregisterObject(this); } } } } }