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);
            }
        }
Example #2
0
    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);
        }
Example #4
0
        public void Configure(RuntimeLevel level, RuntimeLevelReason reason, Exception bootFailedException = null)
        {
            Level  = level;
            Reason = reason;

            if (bootFailedException != null)
            {
                BootFailedException = new BootFailedException(bootFailedException.Message, bootFailedException);
            }
        }
Example #5
0
        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;
            }
        }
Example #6
0
        /// <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);
        }
Example #7
0
        /// <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);
            };
        }
Example #8
0
        /// <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;
            }
        }