public async Task InvokeAsync(HttpContext context, RequestDelegate next) { // TODO: It would be possible to redirect to the installer here in debug mode while // still showing the error. This would be a lot more friendly than just the YSOD. // We could also then have a different installer view for when package migrations fails // and to retry each one individually. Perhaps this can happen in the future. if (_runtimeState.Level == RuntimeLevel.BootFailed) { // short circuit if (_hostingEnvironment.IsDebugMode) { BootFailedException.Rethrow(_runtimeState.BootFailedException); } else // Print a nice error page { context.Response.Clear(); context.Response.StatusCode = 500; var file = GetBootErrorFileName(); var viewContent = await File.ReadAllTextAsync(file); await context.Response.WriteAsync(viewContent, Encoding.UTF8); } } else { await next(context); } }
public async Task InvokeAsync(HttpContext context, RequestDelegate next) { // TODO: It would be possible to redirect to the installer here in debug mode while // still showing the error. This would be a lot more friendly than just the YSOD. // We could also then have a different installer view for when package migrations fails // and to retry each one individually. Perhaps this can happen in the future. if (_runtimeState.Level == RuntimeLevel.BootFailed) { // short circuit if (_hostingEnvironment.IsDebugMode) { BootFailedException.Rethrow(_runtimeState.BootFailedException); } else { // Print a nice error page context.Response.Clear(); context.Response.StatusCode = 500; IFileInfo?fileInfo = GetBootErrorFileInfo(); if (fileInfo is not null) { using var sr = new StreamReader(fileInfo.CreateReadStream(), Encoding.UTF8); await context.Response.WriteAsync(await sr.ReadToEndAsync(), Encoding.UTF8); } } } else { await next(context); } }
/// <inheritdoc /> public void Init(HttpApplication context) { try { // using the service locator here - no other way, really Module = Current.Factory.GetInstance <TModule>(); } catch { // if GetInstance fails, it may be because of a boot error, in // which case that is the error we actually want to report IRuntimeState runtimeState = null; try { runtimeState = Current.Factory.GetInstance <IRuntimeState>(); } catch { /* don't make it worse */ } if (runtimeState?.BootFailedException != null) { BootFailedException.Rethrow(runtimeState.BootFailedException); } // else... throw what we have throw; } // initialize Module.Init(context); }
public void Configure(RuntimeLevel level, RuntimeLevelReason reason, Exception bootFailedException = null) { Level = level; Reason = reason; if (bootFailedException != null) { BootFailedException = new BootFailedException(bootFailedException.Message, bootFailedException); } }
private UmbracoDatabaseState GetUmbracoDatabaseState(IUmbracoDatabaseFactory databaseFactory) { try { if (!TryDbConnect(databaseFactory)) { return(UmbracoDatabaseState.CannotConnect); } // no scope, no service - just directly accessing the database using (var database = databaseFactory.CreateDatabase()) { if (!database.IsUmbracoInstalled()) { return(UmbracoDatabaseState.NotInstalled); } // Make ONE SQL call to determine Umbraco upgrade vs package migrations state. // All will be prefixed with the same key. IReadOnlyDictionary <string, string> keyValues = database.GetFromKeyValueTable(Constants.Conventions.Migrations.KeyValuePrefix); // This could need both an upgrade AND package migrations to execute but // we will process them one at a time, first the upgrade, then the package migrations. if (DoesUmbracoRequireUpgrade(keyValues)) { return(UmbracoDatabaseState.NeedsUpgrade); } IReadOnlyList <string> packagesRequiringMigration = _packageMigrationState.GetPendingPackageMigrations(keyValues); if (packagesRequiringMigration.Count > 0) { _startupState[PendingPacakgeMigrationsStateKey] = packagesRequiringMigration; return(UmbracoDatabaseState.NeedsPackageMigration); } } return(UmbracoDatabaseState.Ok); } catch (Exception e) { // can connect to the database so cannot check the upgrade state... oops _logger.LogWarning(e, "Could not check the upgrade state."); // else it is bad enough that we want to throw Reason = RuntimeLevelReason.BootFailedCannotCheckUpgradeState; BootFailedException = new BootFailedException("Could not check the upgrade state.", e); throw BootFailedException; } }
/// <inheritdoc /> public void Init(HttpApplication context) { try { // using the service locator here - no other way, really Module = Current.Factory.GetInstance <TModule>(); } catch { // if GetInstance fails, it may be because of a boot error, in // which case that is the error we actually want to report IRuntimeState runtimeState = null; try { runtimeState = Current.Factory.GetInstance <IRuntimeState>(); } catch { /* don't make it worse */ } if (runtimeState?.BootFailedException != null) { // if we throw the exception here the HttpApplication.Application_Error method will never be hit // and our custom error page will not be shown, so we need to wait for the request to begin // before we throw the exception. context.BeginRequest += (sender, args) => { BootFailedException.Rethrow(runtimeState.BootFailedException); }; return; } // else... throw what we have throw; } // initialize Module.Init(context); }
/// <summary> /// Initialize the module, this will trigger for each new application /// and there may be more than 1 application per application domain /// </summary> /// <param name="app"></param> public void Init(HttpApplication app) { if (_runtime.Level == RuntimeLevel.BootFailed) { // there's nothing we can do really app.BeginRequest += (sender, args) => { // would love to avoid throwing, and instead display a customized Umbraco 500 // page - however if we don't throw here, something else might go wrong, and // it's this later exception that would be reported. could not figure out how // to prevent it, either with httpContext.Response.End() or .ApplicationInstance // .CompleteRequest() // also, if something goes wrong with our DI setup, the logging subsystem may // not even kick in, so here we try to give as much detail as possible BootFailedException.Rethrow(Core.Composing.Current.RuntimeState.BootFailedException); }; return; } app.BeginRequest += (sender, e) => { var httpContext = ((HttpApplication)sender).Context; LogHttpRequest.TryGetCurrentHttpRequestId(out var httpRequestId); _logger.Verbose <UmbracoModule>("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, httpContext.Request.Url); BeginRequest(new HttpContextWrapper(httpContext)); }; //disable asp.net headers (security) // This is the correct place to modify headers according to MS: // https://our.umbraco.com/forum/umbraco-7/using-umbraco-7/65241-Heap-error-from-header-manipulation?p=0#comment220889 app.PostReleaseRequestState += (sender, args) => { var httpContext = ((HttpApplication)sender).Context; try { httpContext.Response.Headers.Remove("Server"); //this doesn't normally work since IIS sets it but we'll keep it here anyways. httpContext.Response.Headers.Remove("X-Powered-By"); httpContext.Response.Headers.Remove("X-AspNet-Version"); httpContext.Response.Headers.Remove("X-AspNetMvc-Version"); } catch (PlatformNotSupportedException) { // can't remove headers this way on IIS6 or cassini. } }; app.PostAuthenticateRequest += (sender, e) => { var httpContext = ((HttpApplication)sender).Context; //ensure the thread culture is set httpContext.User?.Identity?.EnsureCulture(); }; app.PostResolveRequestCache += (sender, e) => { var httpContext = ((HttpApplication)sender).Context; ProcessRequest(new HttpContextWrapper(httpContext)); }; app.EndRequest += (sender, args) => { var httpContext = ((HttpApplication)sender).Context; if (Current.UmbracoContext != null && Current.UmbracoContext.IsFrontEndUmbracoRequest) { LogHttpRequest.TryGetCurrentHttpRequestId(out var httpRequestId); _logger.Verbose <UmbracoModule>("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, httpContext.Request.Url, DateTime.Now.Subtract(Current.UmbracoContext.ObjectCreated).TotalMilliseconds); } UmbracoModule.OnEndRequest(this, new UmbracoRequestEventArgs(Current.UmbracoContext, new HttpContextWrapper(httpContext))); DisposeHttpContextItems(httpContext); }; }
/// <inheritdoc /> public void DetermineRuntimeLevel() { if (_databaseFactory.Configured == false) { // local version *does* match code version, but the database is not configured // install - may happen with Deploy/Cloud/etc _logger.LogDebug("Database is not configured, need to install Umbraco."); Level = RuntimeLevel.Install; Reason = RuntimeLevelReason.InstallNoDatabase; return; } // Check if we have multiple controllers with the same name. if (_conflictingRouteService.HasConflictingRoutes(out string controllerName)) { Level = RuntimeLevel.BootFailed; Reason = RuntimeLevelReason.BootFailedOnException; BootFailedException = new BootFailedException($"Conflicting routes, you cannot have multiple controllers with the same name: {controllerName}"); return; } // Check the database state, whether we can connect or if it's in an upgrade or empty state, etc... switch (GetUmbracoDatabaseState(_databaseFactory)) { case UmbracoDatabaseState.CannotConnect: { // cannot connect to configured database, this is bad, fail _logger.LogDebug("Could not connect to database."); if (_globalSettings.Value.InstallMissingDatabase || CanAutoInstallMissingDatabase(_databaseFactory)) { // ok to install on a configured but missing database Level = RuntimeLevel.Install; Reason = RuntimeLevelReason.InstallMissingDatabase; return; } // else it is bad enough that we want to throw Reason = RuntimeLevelReason.BootFailedCannotConnectToDatabase; BootFailedException = new BootFailedException("A connection string is configured but Umbraco could not connect to the database."); throw BootFailedException; } case UmbracoDatabaseState.NotInstalled: { // ok to install on an empty database Level = RuntimeLevel.Install; Reason = RuntimeLevelReason.InstallEmptyDatabase; return; } case UmbracoDatabaseState.NeedsUpgrade: { // the db version does not match... but we do have a migration table // so, at least one valid table, so we quite probably are installed & need to upgrade // although the files version matches the code version, the database version does not // which means the local files have been upgraded but not the database - need to upgrade _logger.LogDebug("Has not reached the final upgrade step, need to upgrade Umbraco."); Level = _unattendedSettings.Value.UpgradeUnattended ? RuntimeLevel.Run : RuntimeLevel.Upgrade; Reason = RuntimeLevelReason.UpgradeMigrations; } break; case UmbracoDatabaseState.NeedsPackageMigration: // no matter what the level is run for package migrations. // they either run unattended, or only manually via the back office. Level = RuntimeLevel.Run; if (_unattendedSettings.Value.PackageMigrationsUnattended) { _logger.LogDebug("Package migrations need to execute."); Reason = RuntimeLevelReason.UpgradePackageMigrations; } else { _logger.LogInformation("Package migrations need to execute but unattended package migrations is disabled. They will need to be run from the back office."); Reason = RuntimeLevelReason.Run; } break; case UmbracoDatabaseState.Ok: default: { // the database version matches the code & files version, all clear, can run Level = RuntimeLevel.Run; Reason = RuntimeLevelReason.Run; } break; } }