File: Configuration\RuntimeConfig.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="RuntimeConfig.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
using System.Collections;
using System.Configuration;
using System.Configuration.Internal;
using System.Security;
using System.Security.Permissions;
using System.Web;
using System.Web.Util;
using System.Web.Hosting;
using System.Web.Configuration;
 
namespace System.Web.Configuration {
 
    //
    // Internal, read-only access to configuration settings.
    //
    internal class RuntimeConfig {
        //
        // GetConfig() - get configuration appropriate for the current thread.
        //
        // Looks up the HttpContext on the current thread if it is available,
        // otherwise it uses the config at the app path.
        //
        // Use GetConfig(context) if a context is available, as it will avoid
        // the lookup for contxt on the current thread.
        //
        // For config derived from ConfigurationSection, this will either
        // return a non-null object or throw an exception.
        //
        // For config implemented with IConfigurationSectionHandler, this 
        // may return null, non-null, or throw an exception.
        //
        static internal RuntimeConfig GetConfig() {
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem)  {
                return GetClientRuntimeConfig();
            }
 
            HttpContext context = HttpContext.Current;
            if (context != null) {
                return GetConfig(context);
            }
            else {
                return GetAppConfig();
            }
        }
 
        //
        // GetConfig(context) - gets configuration appropriate for the HttpContext.
        // The most efficient way to get config.
        //
        // For config derived from ConfigurationSection, this will either
        // return a non-null object or throw an exception.
        //
        // For config implemented with IConfigurationSectionHandler, this 
        // may return null, non-null, or throw an exception.
        //
        static internal RuntimeConfig GetConfig(HttpContext context) {
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem)  {
                return GetClientRuntimeConfig();
            }
 
            return context.GetRuntimeConfig();
        }
 
        //
        // GetConfig(context, path) - returns the config at 'path'.
        //
        // This method is more efficient than not using context, as
        // the config cached in the context is used if it matches the
        // context path.
        //
        // For config derived from ConfigurationSection, this will either
        // return a non-null object or throw an exception.
        //
        // For config implemented with IConfigurationSectionHandler, this 
        // may return null, non-null, or throw an exception.
        //
        static internal RuntimeConfig GetConfig(HttpContext context, VirtualPath path) {
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem)  {
                return GetClientRuntimeConfig();
            }
 
            return context.GetRuntimeConfig(path);
        }
 
        //
        // GetConfig(path) - returns the config at 'path'. 
        // 
        // If 'path' is null, or is outside of the application path, then it
        // returns the application config.
        //
        // For efficientcy, use GetConfig(context) instead of this method 
        // where possible.
        //
        // For config derived from ConfigurationSection, this will either
        // return a non-null object or throw an exception.
        //
        // For config implemented with IConfigurationSectionHandler, this 
        // may return null, non-null, or throw an exception.
        //
        static internal RuntimeConfig GetConfig(string path) {
            return GetConfig(VirtualPath.CreateNonRelativeAllowNull(path));
        }
 
        static internal RuntimeConfig GetConfig(VirtualPath path) {
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem) {
                return GetClientRuntimeConfig();
            }
 
            return CachedPathData.GetVirtualPathData(path, true).RuntimeConfig;
        }
 
        //
        // GetAppConfig() - returns the application config.
        //
        // For config derived from ConfigurationSection, this will either
        // return a non-null object or throw an exception.
        //
        // For config implemented with IConfigurationSectionHandler, this 
        // may return null, non-null, or throw an exception.
        //
        static internal RuntimeConfig GetAppConfig() {
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem)  {
                return GetClientRuntimeConfig();
            }
 
            return CachedPathData.GetApplicationPathData().RuntimeConfig;
        }
 
        //
        // GetRootWebConfig() - returns the root web configuration.
        //
        // For config derived from ConfigurationSection, this will either
        // return a non-null object or throw an exception.
        //
        // For config implemented with IConfigurationSectionHandler, this 
        // may return null, non-null, or throw an exception.
        //
        static internal RuntimeConfig GetRootWebConfig() {
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem)  {
                return GetClientRuntimeConfig();
            }
 
            return CachedPathData.GetRootWebPathData().RuntimeConfig;
        }
 
        //
        // GetMachineConfig() - returns the machine configuration.
        //
        // For config derived from ConfigurationSection, this will either
        // return a non-null object or throw an exception.
        //
        // For config implemented with IConfigurationSectionHandler, this 
        // may return null, non-null, or throw an exception.
        //
        static internal RuntimeConfig GetMachineConfig() {
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem)  {
                return GetClientRuntimeConfig();
            }
 
            return CachedPathData.GetMachinePathData().RuntimeConfig;
        }
 
        //
        // GetLKGConfig(context) - gets the nearest configuration available.
        //
        // This method is to be used in the few instances where we
        // cannot throw an exception if a config file has an error.
        //
        // This method will never throw an exception. If no config
        // is available, a request for a section will return null.
        //
        static internal RuntimeConfig GetLKGConfig(HttpContext context) {
            RuntimeConfig config = null;
            bool success = false;
            try {
                config = GetConfig(context);
                success = true;
            }
            catch {
            }
 
            if (!success) {
                config = GetLKGRuntimeConfig(context.Request.FilePathObject);
            }
 
            return config.RuntimeConfigLKG;
        }
 
        //
        // GetAppLKGConfig(path) - gets the nearest configuration available,
        // starting from the application path.
        //
        // This method is to be used in the few instances where we
        // cannot throw an exception if a config file has an error.
        //
        // This method will never throw an exception. If no config
        // is available, a request for a section will return null.
        //
        static internal RuntimeConfig GetAppLKGConfig() {
            RuntimeConfig config = null;
            bool success = false;
            try {
                config = GetAppConfig();
                success = true;
            }
            catch {
            }
 
 
            if (!success) {
                config = GetLKGRuntimeConfig(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPathObject);
            }
 
            return config.RuntimeConfigLKG;
        }
 
        //
        // WHIDBEY sections
        //
 
        internal ConnectionStringsSection ConnectionStrings {
            get {
                return (ConnectionStringsSection) GetSection("connectionStrings", typeof(ConnectionStringsSection), ResultsIndex.ConnectionStrings);
            }
        }
 
        internal System.Net.Configuration.SmtpSection Smtp {
            get {
                return (System.Net.Configuration.SmtpSection) GetSection("system.net/mailSettings/smtp", typeof(System.Net.Configuration.SmtpSection));
            }
        }
 
        internal AnonymousIdentificationSection AnonymousIdentification {
            get {
                return (AnonymousIdentificationSection) GetSection("system.web/anonymousIdentification", typeof(AnonymousIdentificationSection));
            }
        }
 
        internal ProtocolsSection Protocols {
            get {
                return (ProtocolsSection) GetSection("system.web/protocols", typeof(ProtocolsSection));
            }
        }
 
        internal AuthenticationSection Authentication {
            get {
                return (AuthenticationSection) GetSection("system.web/authentication", typeof(AuthenticationSection), ResultsIndex.Authentication);
            }
        }
 
        internal AuthorizationSection Authorization {
            get {
                return (AuthorizationSection) GetSection("system.web/authorization", typeof(AuthorizationSection), ResultsIndex.Authorization);
            }
        }
 
        // may return null
        internal HttpCapabilitiesDefaultProvider BrowserCaps {
            get {
                return (HttpCapabilitiesDefaultProvider) GetHandlerSection("system.web/browserCaps", typeof(HttpCapabilitiesDefaultProvider), ResultsIndex.BrowserCaps);
            }
        }
 
        internal ClientTargetSection ClientTarget {
            get {
                return (ClientTargetSection) GetSection("system.web/clientTarget", typeof(ClientTargetSection), ResultsIndex.ClientTarget);
            }
        }
 
        internal CompilationSection Compilation {
            get {
                return (CompilationSection) GetSection("system.web/compilation", typeof(CompilationSection), ResultsIndex.Compilation);
            }
        }
 
        internal CustomErrorsSection CustomErrors {
            get {
                return (CustomErrorsSection) GetSection("system.web/customErrors", typeof(CustomErrorsSection));
            }
        }
 
        internal GlobalizationSection Globalization {
            get {
                return (GlobalizationSection) GetSection("system.web/globalization", typeof(GlobalizationSection), ResultsIndex.Globalization);
            }
        }
 
        internal DeploymentSection Deployment {
            get {
                return (DeploymentSection) GetSection("system.web/deployment", typeof(DeploymentSection));
            }
        }
 
        internal FullTrustAssembliesSection FullTrustAssemblies {
            get {
                return (FullTrustAssembliesSection)GetSection("system.web/fullTrustAssemblies", typeof(FullTrustAssembliesSection));
            }
        }
 
        internal HealthMonitoringSection HealthMonitoring {
            get {
                return (HealthMonitoringSection) GetSection("system.web/healthMonitoring", typeof(HealthMonitoringSection));
            }
        }
 
        internal HostingEnvironmentSection HostingEnvironment {
            get {
                return (HostingEnvironmentSection) GetSection("system.web/hostingEnvironment", typeof(HostingEnvironmentSection));
            }
        }
 
        internal HttpCookiesSection HttpCookies {
            get {
                return (HttpCookiesSection) GetSection("system.web/httpCookies", typeof(HttpCookiesSection), ResultsIndex.HttpCookies);
            }
        }
 
        internal HttpHandlersSection HttpHandlers {
            get {
                return (HttpHandlersSection) GetSection("system.web/httpHandlers", typeof(HttpHandlersSection), ResultsIndex.HttpHandlers);
            }
        }
 
        internal HttpModulesSection HttpModules {
            get {
                return (HttpModulesSection) GetSection("system.web/httpModules", typeof(HttpModulesSection), ResultsIndex.HttpModules);
            }
        }
 
        internal HttpRuntimeSection HttpRuntime {
            get {
                return (HttpRuntimeSection) GetSection("system.web/httpRuntime", typeof(HttpRuntimeSection), ResultsIndex.HttpRuntime);
            }
        }
 
        internal IdentitySection Identity {
            get {
                return (IdentitySection) GetSection("system.web/identity", typeof(IdentitySection), ResultsIndex.Identity);
            }
        }
 
        internal MachineKeySection MachineKey {
            get {
                return (MachineKeySection) GetSection("system.web/machineKey", typeof(MachineKeySection), ResultsIndex.MachineKey);
            }
        }
 
        internal MembershipSection Membership {
            get {
                return (MembershipSection) GetSection("system.web/membership", typeof(MembershipSection), ResultsIndex.Membership);
            }
        }
 
        internal PagesSection Pages {
            get {
                return (PagesSection) GetSection("system.web/pages", typeof(PagesSection), ResultsIndex.Pages);
            }
        }
 
        internal PartialTrustVisibleAssembliesSection PartialTrustVisibleAssemblies {
            get
            {
                return (PartialTrustVisibleAssembliesSection)GetSection("system.web/partialTrustVisibleAssemblies", typeof(PartialTrustVisibleAssembliesSection));
            }
        }
 
        internal ProcessModelSection ProcessModel {
            get {
                return (ProcessModelSection) GetSection("system.web/processModel", typeof(ProcessModelSection));
            }
        }
 
        internal ProfileSection Profile {
            get {
                return (ProfileSection) GetSection("system.web/profile", typeof(ProfileSection), ResultsIndex.Profile);
            }
        }
 
        internal RoleManagerSection RoleManager {
            get {
                return (RoleManagerSection) GetSection("system.web/roleManager", typeof(RoleManagerSection));
            }
        }
 
        internal SecurityPolicySection SecurityPolicy {
            get {
                return (SecurityPolicySection) GetSection("system.web/securityPolicy", typeof(SecurityPolicySection));
            }
        }
 
        internal SessionPageStateSection SessionPageState {
            get {
                return (SessionPageStateSection) GetSection("system.web/sessionPageState", typeof(SessionPageStateSection), ResultsIndex.SessionPageState);
            }
        }
 
        internal SessionStateSection SessionState {
            get {
                return (SessionStateSection) GetSection("system.web/sessionState", typeof(SessionStateSection));
            }
        }
 
        internal SiteMapSection SiteMap {
            get {
                return (SiteMapSection) GetSection("system.web/siteMap", typeof(SiteMapSection));
            }
        }
 
        internal TraceSection Trace {
            get {
                return (TraceSection) GetSection("system.web/trace", typeof(TraceSection));
            }
        }
 
        internal TrustSection Trust {
            get {
                return (TrustSection) GetSection("system.web/trust", typeof(TrustSection));
            }
        }
 
        internal UrlMappingsSection UrlMappings {
            get {
                return (UrlMappingsSection) GetSection("system.web/urlMappings", typeof(UrlMappingsSection), ResultsIndex.UrlMappings);
            }
        }
 
        internal Hashtable WebControls {
            get {
                return (Hashtable)GetSection("system.web/webControls", typeof(Hashtable), ResultsIndex.WebControls);
            }
        }
 
        internal WebPartsSection WebParts {
            get {
                return (WebPartsSection) GetSection("system.web/webParts", typeof(WebPartsSection), ResultsIndex.WebParts);
            }
        }
 
        internal XhtmlConformanceSection XhtmlConformance {
            get {
                return (XhtmlConformanceSection) GetSection("system.web/xhtmlConformance", typeof(XhtmlConformanceSection), ResultsIndex.XhtmlConformance);
            }
        }
 
        internal CacheSection Cache {
            get {
                return (CacheSection) GetSection("system.web/caching/cache", typeof(CacheSection));
            }
        }
 
        internal OutputCacheSection OutputCache {
            get {
                return (OutputCacheSection) GetSection("system.web/caching/outputCache", typeof(OutputCacheSection), ResultsIndex.OutputCache);
            }
        }
 
        internal OutputCacheSettingsSection OutputCacheSettings {
            get {
                return (OutputCacheSettingsSection) GetSection("system.web/caching/outputCacheSettings", typeof(OutputCacheSettingsSection), ResultsIndex.OutputCacheSettings);
            }
        }
 
        internal SqlCacheDependencySection SqlCacheDependency {
            get {
                return (SqlCacheDependencySection) GetSection("system.web/caching/sqlCacheDependency", typeof(SqlCacheDependencySection));
            }
        }
     
 
        //////////////////////////////
        //
        // IMPLEMENTATION
        //
        //////////////////////////////
 
        // Wraps calls to RuntimeConfig.GetConfig() when 
        // the web config system is not being used.
        private static RuntimeConfig    s_clientRuntimeConfig;
 
        // Wraps calls to RuntimeConfig.GetConfig() when 
        // we must return null.
        private static RuntimeConfig    s_nullRuntimeConfig;
 
        // Wraps calls to RuntimeConfig.GetConfig() when 
        // we must return an error because there was an
        // unrecoverable error creating the config record.
        private static RuntimeConfig    s_errorRuntimeConfig;
 
        // object used to indicate that result has not been evaluated
        private static object           s_unevaluatedResult;
 
        // Commonly used results on every request. We cache these by index
        // into an array so we don't need to do hash table lookups,
        // type comparisons, and handle a demand for ConfigurationPermission
        // to retreive the config.
        internal enum ResultsIndex {
            // a valid index into the results array that is always unevaluated
            UNUSED = 0,             
 
            Authentication,
            Authorization,
            BrowserCaps,
            ClientTarget,
            Compilation,
            ConnectionStrings,
            Globalization,
            HttpCookies,
            HttpHandlers,
            HttpModules,
            HttpRuntime,
            Identity,
            MachineKey,
            Membership,
            OutputCache,
            OutputCacheSettings,
            Pages,
            Profile,
            SessionPageState,
            WebControls,
            WebParts,
            UrlMappings,
            XhtmlConformance,
 
            // size of the results array, must be last in list
            SIZE
        };
 
        // cached results
        // Per-path caching for perf reason.  Available only to internal components.
        private object[]                _results;   
 
        // LKG config 
        private RuntimeConfigLKG        _runtimeConfigLKG;
 
        // for http configuration, the ConfigurationRecord on which we call GetConfig
        protected IInternalConfigRecord _configRecord;
 
        // classes implementing LKG may return null from GetSectionObject
        private bool                    _permitNull;
 
        static RuntimeConfig() {
            s_unevaluatedResult = new object();
 
            // Ensure that we have an error config record available if we
            // get an unrecoverable error situation.
            GetErrorRuntimeConfig();
        }
 
        // ctor used by CachedPathData to wrap the ConfigurationRecord
        internal RuntimeConfig(IInternalConfigRecord configRecord) : this(configRecord, false) {}
 
        protected RuntimeConfig(IInternalConfigRecord configRecord, bool permitNull) {
            _configRecord = configRecord;
            _permitNull = permitNull;
 
            // initialize results cache
            _results = new object[(int)ResultsIndex.SIZE];
            for (int i = 0; i < _results.Length; i++) {
                _results[i] = s_unevaluatedResult;
            }
        }
 
        private RuntimeConfigLKG RuntimeConfigLKG {
            get {
                if (_runtimeConfigLKG == null) {
                    lock (this) {
                        if (_runtimeConfigLKG == null) {
                            _runtimeConfigLKG = new RuntimeConfigLKG(_configRecord);
                        }
                    }
                }
                
                return _runtimeConfigLKG;
            }
        }
 
        internal IInternalConfigRecord ConfigRecord {
            get {
                return _configRecord;
            }
        }
 
        // Create the single instance of the wrapper for ConfigurationManager configuration.
        static RuntimeConfig GetClientRuntimeConfig() {
            if (s_clientRuntimeConfig == null) {
                s_clientRuntimeConfig = new ClientRuntimeConfig();
            }
 
            return s_clientRuntimeConfig;
        }
 
        // Create the single instance of the wrapper for null configuration.
        static RuntimeConfig GetNullRuntimeConfig() {
            if (s_nullRuntimeConfig == null) {
                s_nullRuntimeConfig = new NullRuntimeConfig();
            }
 
            return s_nullRuntimeConfig;
        }
 
        // Create the single instance of the wrapper for error configuration.
        static internal RuntimeConfig GetErrorRuntimeConfig() {
            if (s_errorRuntimeConfig == null) {
                s_errorRuntimeConfig = new ErrorRuntimeConfig();
            }
 
            return s_errorRuntimeConfig;
        }
 
        // Get the config object for a section
        [ConfigurationPermission(SecurityAction.Assert, Unrestricted=true)]
        protected virtual object GetSectionObject(string sectionName) {
            return _configRecord.GetSection(sectionName);
        }
 
 
        //
        // Return a config implemented by IConfigurationHandler, 
        // and use the runtime cache to store it for quick retreival without
        // having to hit a config record and a demand for ConfigurationPermission.
        //
        private object GetHandlerSection(string sectionName, Type type, ResultsIndex index) {
            // check the results cache
            object result = _results[(int)index];
            if (result != s_unevaluatedResult) {
                return result;
            }
 
            // Get the configuration object.
            //
            // Note that it is legal for an IConfigurationSectionHandler implementation 
            // to return null.
            result = GetSectionObject(sectionName);
 
            // verify the object is of the expected type
            if (result != null && result.GetType() != type) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_unable_to_get_section, sectionName));
            }
 
            // store into results cache
            if (index != ResultsIndex.UNUSED) {
                _results[(int)index] = result;
            }
 
            return result;
        }
 
        //
        // Return a configuration section without checking the runtime cache.
        //
        private object GetSection(string sectionName, Type type) {
            return GetSection(sectionName, type, ResultsIndex.UNUSED);
        }
 
        //
        // Return a configuration section, and use the runtime cache to store it for
        // quick retreival without having to hit a config record and a demand for
        // ConfigurationPermission.
        //
        private object GetSection(string sectionName, Type type, ResultsIndex index) {
            // check the results cache
            object result = _results[(int)index];
            if (result != s_unevaluatedResult) {
                return result;
            }
 
            // get the configuration object
            result = GetSectionObject(sectionName);
            if (result == null) {
                // A section implemented by ConfigurationSection may not return null,
                // but various error handling subclasses of RuntimeConfig may need it.
                // Throw an error if null is not permitted.
                if (!_permitNull) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.Config_unable_to_get_section, sectionName));
                }
            }
            else {
                // verify the object is of the expected type
                if (result.GetType() != type) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.Config_unable_to_get_section, sectionName));
                }
            }
 
            // store into results cache
            if (index != ResultsIndex.UNUSED) {
                _results[(int)index] = result;
            }
 
            return result;
        }
 
        //
        // There are extreme cases where we cannot even retreive the CachedPathData
        // for a path - such as when MapPath deems the path to be suspicious.
        // In these cases, walk the hierarchy upwards until we are able to retreive
        // a CachedPathData and its associated RuntimeConfig.
        //
        static private RuntimeConfig GetLKGRuntimeConfig(VirtualPath path) {
            try {
                // Start with the parent of the path. 
                path = path.Parent;
            }
            catch {
                path = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPathObject;
            }
 
            // Walk the path hierarchy until we can succesfully get a RuntimeConfig.
            while (path != null) {
                try {
                    return GetConfig(path);
                }
                catch {
                    path = path.Parent;
                }
            }
 
            try {
                return GetRootWebConfig();
            }
            catch {
            }
 
            try {
                return GetMachineConfig();
            }
            catch {
            }
 
            return GetNullRuntimeConfig();
        }
    }
}