CachedPathData.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FXUpdate3074 / FXUpdate3074 / 1.1 / untmp / whidbey / QFE / ndp / fx / src / xsp / System / Web / CachedPathData.cs / 6 / CachedPathData.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web { 
    using System.Collections; 
    using System.Configuration;
    using System.Configuration.Internal; 
    using System.Globalization;
    using System.Security.Principal;
    using System.Threading;
    using System.Web.Security; 
    using System.Web.SessionState;
    using System.Web.Configuration; 
    using System.Web.Caching; 
    using System.Web.Hosting;
    using System.Web.Util; 
    using System.Web.UI;
    using System.Security.Permissions;

    // Data about a path that is cached across requests 
    class CachedPathData {
        internal const int FInited                  = 0x0001; 
        internal const int FCompletedFirstRequest   = 0x0002; 
        internal const int FExists                  = 0x0004;
        internal const int FOwnsConfigRecord        = 0x0010;   // is this the highest ancestor pointing to the config record? 
        internal const int FClosed                  = 0x0020;   // Has item been closed already?
        internal const int FCloseNeeded             = 0x0040;   // Should we close?

        static CacheItemRemovedCallback s_callback = new CacheItemRemovedCallback(CachedPathData.OnCacheItemRemoved); 

        #pragma warning disable 0649 
        SafeBitVector32         _flags; 
        #pragma warning restore 0649
        string                  _configPath; 
        VirtualPath             _virtualPath;
        string                  _physicalPath;
        RuntimeConfig           _runtimeConfig;
        HandlerMappingMemo      _handlerMemo; 

 
        // 
        // Constructor
        // 
        internal CachedPathData(string configPath, VirtualPath virtualPath, string physicalPath, bool exists) {
            // Guarantee that we return a non-null config record
            // if an error occurs during initialization.
            _runtimeConfig = RuntimeConfig.GetErrorRuntimeConfig(); 
            _configPath = configPath;
            _virtualPath = virtualPath; 
            _physicalPath = physicalPath; 
            _flags[FExists] = exists;
 
            //


 

            string dummy = System.Uri.SchemeDelimiter; 
 
        }
 
        //
        // Get CachedPathData for the machine.config level
        //
        static internal CachedPathData GetMachinePathData() { 
            return GetConfigPathData(WebConfigurationHost.MachineConfigPath);
        } 
 
        //
        // Get CachedPathData for the root web.config path 
        //
        static internal CachedPathData GetRootWebPathData() {
            return GetConfigPathData(WebConfigurationHost.RootWebConfigPath);
        } 

        // 
        // Get CachedPathData for the application. 
        //
        static internal CachedPathData GetApplicationPathData() { 
            if (!HostingEnvironment.IsHosted) {
                return GetRootWebPathData();
            }
 
            return GetConfigPathData(HostingEnvironment.AppConfigPath);
        } 
 
        //
        // Get CachedPathData for a virtual path. 
        // The path may be supplied by user code, so

        static internal CachedPathData GetVirtualPathData(VirtualPath virtualPath, bool permitPathsOutsideApp) {
            if (!HostingEnvironment.IsHosted) { 
                return GetRootWebPathData();
            } 
 
            // Make sure it's not relative
            if (virtualPath != null) { 
                virtualPath.FailIfRelativePath();
            }

            // Check if the path is within the application. 
            if (virtualPath == null || !virtualPath.IsWithinAppRoot) {
                if (permitPathsOutsideApp) { 
                    return GetApplicationPathData(); 
                }
                else { 
                    throw new ArgumentException(SR.GetString(
                        SR.Cross_app_not_allowed,
                        (virtualPath != null) ? virtualPath.VirtualPathString : "null"));
                } 
            }
 
            // Construct a configPath based on the unvalidated virtualPath. 
            string configPath = WebConfigurationHost.GetConfigPathFromSiteIDAndVPath(HostingEnvironment.SiteID, virtualPath);
 
            // Pass the virtualPath to GetConfigPathData to validate in the case where the
            // CachedPathData for the unsafeConfigPath is not found.
            return GetConfigPathData(configPath);
        } 

        // Example of configPath = "machine/webroot/1/fxtest/sub/foo.aspx" 
    // The configPath parameter must be lower case. 
        static private CachedPathData GetConfigPathData(string configPath) {
 
            Debug.Assert(ConfigPathUtility.IsValid(configPath), "ConfigPathUtility.IsValid(configPath)");
            Debug.Assert(configPath == configPath.ToLower(CultureInfo.InvariantCulture), "configPath == configPath.ToLower(CultureInfo.InvariantCulture)");

            // 
            // First, see if the CachedPathData is in the cache.
            // we don't use Add for this lookup, as doing so requires 
            // creating a CacheDependency, which can be slow as it may hit 
            // the filesystem.
            // 
            string key = CreateKey(configPath);
            CacheInternal cacheInternal = HttpRuntime.CacheInternal;
            CachedPathData data = (CachedPathData) cacheInternal.Get(key);
 
            // if found, return the data
            if (data != null) { 
                data.WaitForInit(); 
                return data;
            } 

            // if not found, try to add it
            CachedPathData parentData = null;
            CacheDependency dependency = null; 
            VirtualPath virtualPath = null;
            string physicalPath = null; 
            bool exists = false; 
            string[] fileDependencies = null;
            string[] cacheItemDependencies = null; 
            string siteID = null;

            // WOS
 

            bool cacheEntryIsNotRemovable = false; 
 
            if (WebConfigurationHost.IsMachineConfigPath(configPath)) {
                exists = true; 
                cacheEntryIsNotRemovable = true;
            }
            else {
                // Make sure we have the parent data so we can create a dependency on the parent. 
                // The parent dependency will ensure that configuration data in the parent
                // will be referenced by a cache hit on the child. (see UtcUpdateUsageRecursive in Cache.cs) 
                string parentConfigPath = ConfigPathUtility.GetParent(configPath); 
                parentData = GetConfigPathData(parentConfigPath);
                string parentKey = CreateKey(parentConfigPath); 
                cacheItemDependencies = new string[1] {parentKey};

                if (!WebConfigurationHost.IsVirtualPathConfigPath(configPath)) {
                    // assume hardcoded levels above the path, such as root web.config, exist 
                    exists = true;
                    cacheEntryIsNotRemovable = true; 
                } 
                else {
 
                    // Ensure that the physical path does not look suspicious (MSRC 5556).
                    WebConfigurationHost.GetSiteIDAndVPathFromConfigPath(configPath, out siteID, out virtualPath);
                    try {
                        physicalPath = virtualPath.MapPathInternal(true); 
                    }
                    catch (HttpException e) { 
                        // 
                        // Treat exceptions that are thrown because the path is suspicious
                        // as "404 Not Found" exceptions. Implementations of MapPath 
                        // will throw HttpException with no error code if the path is
                        // suspicious.
                        //
                        if (e.GetHttpCode() == 500) { 
                            throw new HttpException(404, String.Empty);
                        } 
                        else { 
                            throw;
                        } 
                    }

                    //
                    // Throw "404 Not Found" if the path is suspicious and 
                    // the implementation of MapPath has not already done so.
                    // 
                    FileUtil.CheckSuspiciousPhysicalPath(physicalPath); 

                    // Add a dependency on the path itself, if it is a file, 
                    // to handle the case where a file is deleted and replaced
                    // with a directory of the same name.
                    bool isDirectory = false;
                    if (String.IsNullOrEmpty(physicalPath)) { 
                        exists = false;
                    } 
                    else { 
                        FileUtil.PhysicalPathStatus(physicalPath, false, false, out exists, out isDirectory);
                    } 

                    if (exists && !isDirectory) {
                        fileDependencies = new string[1] {physicalPath};
                    } 
                }
 
                try { 
                    dependency = new CacheDependency(0, fileDependencies, cacheItemDependencies);
                } 
                catch {
                    // CacheDependency ctor could fail because of bogus file path
                    // and it is ok not to watch those
                } 
            }
 
            // Try to add the CachedPathData to the cache. 
            CachedPathData  dataAdd = null;
            bool            isDataCreator = false; 
            bool            initCompleted = false;
            CacheItemPriority priority = cacheEntryIsNotRemovable ? CacheItemPriority.NotRemovable : CacheItemPriority.Normal;

            try { 
                using (dependency) {
                    dataAdd = new CachedPathData(configPath, virtualPath, physicalPath, exists); 
                    try { 
                    }
                    finally { 
                        data = (CachedPathData) cacheInternal.UtcAdd(key, dataAdd, dependency,
                            Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
                            priority, s_callback);
 
                        if (data == null) {
                            isDataCreator = true; 
                        } 
                    }
                } 

                // If another thread added it first, return the data
                if (!isDataCreator) {
                    data.WaitForInit(); 
                    return data;
                } 
 
                // This thread is the creator of the CachedPathData, initialize it
                lock (dataAdd) { 
                    try {
                        dataAdd.Init(parentData);
                        initCompleted = true;
                    } 
                    finally {
                        // free waiters 
                        dataAdd._flags[FInited] = true; 

                        // Wake up waiters. 
                        Monitor.PulseAll(dataAdd);

                        if (dataAdd._flags[FCloseNeeded]) {
                            // If we have received a call back to close, then lets 
                            // make sure that our config object is cleaned up
                            dataAdd.Close(); 
                        } 
                    }
                } 
            }
            finally {
                // All the work in this finally block is for the case where we're the
                // creator of the CachedPathData. 
                if (isDataCreator) {
 
                    // 

 


                    if (!dataAdd._flags[FInited]) {
                        lock (dataAdd) { 
                            // free waiters
                            dataAdd._flags[FInited] = true; 
 
                            // Wake up waiters.
                            Monitor.PulseAll(dataAdd); 

                            if (dataAdd._flags[FCloseNeeded]) {
                                // If we have received a call back to close, then lets
                                // make sure that our config object is cleaned up 
                                dataAdd.Close();
                            } 
                        } 
                    }
 
                    //
                    // Even though there is a try/catch handler surrounding the call to Init,
                    // a ThreadAbortException can still cause the handler to be bypassed.
                    // 
                    // If there is an error, either a thread abort or an error in the config
                    // file itself, we do want to leave the item cached for a short period 
                    // so that we do not revisit the error and potentially reparse the config file 
                    // on every request.
                    // 
                    // The reason we simply do not leave the item in the cache forever is that the
                    // problem that caused the configuration exception may be fixed without touching
                    // the config file in a way that causes a file change notification (for example, an
                    // acl change in a parent directory, or a change of path mapping in the metabase). 
                    //
                    // 
 

                    Debug.Assert(dataAdd._flags[FInited], "_flags[FInited]"); 
                    if (!initCompleted || (dataAdd.ConfigRecord != null && dataAdd.ConfigRecord.HasInitErrors)) {
                        //
                        // Create a new dependency object as the old one cannot be reused.
                        // Do not include a file dependency if initialization could not be completed, 
                        // as invoking the file system could lead to further errors during a thread abort.
                        // 
                        if (dependency != null) { 
                            if (!initCompleted) {
                                dependency = new CacheDependency(0, null, cacheItemDependencies); 
                            }
                            else {
                                dependency = new CacheDependency(0, fileDependencies, cacheItemDependencies);
                            } 
                        }
 
                        using (dependency) { 
                            cacheInternal.UtcInsert(key, dataAdd, dependency,
                                DateTime.UtcNow.AddSeconds(5), Cache.NoSlidingExpiration, 
                                CacheItemPriority.Normal, s_callback);
                        }
                    }
 
                }
            } 
 
            return dataAdd;
        } 

        // Remove CachedPathData when the first request for the path results in a
        // 400 range error. We need to remove all data up the path to account for
        // virtual files. 
        // An example of a 400 range error is "path not found".
        static internal void RemoveBadPathData(CachedPathData pathData) { 
            CacheInternal cacheInternal = HttpRuntime.CacheInternal; 

            string configPath = pathData._configPath; 
            string key = CreateKey(configPath);
            while (pathData != null && !pathData.CompletedFirstRequest && !pathData.Exists) {

                cacheInternal.Remove(key); 

                configPath = ConfigPathUtility.GetParent(configPath); 
                if (configPath == null) 
                    break;
 
                key = CreateKey(configPath);
                pathData = (CachedPathData) cacheInternal.Get(key);
            }
        } 

        // Mark CachedPathData as completed when the first request for the path results in a 
        // status outside the 400 range. We need to mark all data up the path to account for 
        // virtual files.
        static internal void MarkCompleted(CachedPathData pathData) { 
            CacheInternal cacheInternal = HttpRuntime.CacheInternal;

            string configPath = pathData._configPath;
            do { 
                pathData.CompletedFirstRequest = true;
 
                configPath = ConfigPathUtility.GetParent(configPath); 
                if (configPath == null)
                    break; 

                string key = CreateKey(configPath);
                pathData = (CachedPathData) cacheInternal.Get(key);
            } while (pathData != null && !pathData.CompletedFirstRequest); 
        }
 
        // Close 
        //
        // Close the object.  This does not mean it can not be used anymore, 
        // it just means that the cleanup as been done, so we don't have
        // to worry about closing it anymore
        //
        void Close() { 
            // Only close if we are propertly initialized
            if (_flags[FInited]) { 
 
                // Only close if we haven't already closed
                if (_flags.ChangeValue(FClosed, true)) { 

                    // Remove the config record if we own it
                    // N.B. ConfigRecord.Remove is safe to call more than once.
                    if (_flags[FOwnsConfigRecord]) { 
                        ConfigRecord.Remove();
                    } 
                } 
            }
        } 

        // OnCacheItemRemoved
        //
        // Notification the items has been removed from the cache.  Flag 
        // the item to be cleaned up, and then try cleanup
        // 
        static void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) { 
            CachedPathData data = (CachedPathData) value;
 
            data._flags[FCloseNeeded] = true;
            data.Close();
        }
 
        static string CreateKey(string configPath) {
            Debug.Assert(configPath == configPath.ToLower(CultureInfo.InvariantCulture), "configPath == configPath.ToLower(CultureInfo.InvariantCulture)"); 
            return CacheInternal.PrefixPathData + configPath; 
        }
 
        // Initialize the data
        void Init(CachedPathData parentData) {
            // Note that _runtimeConfig will be set to the singleton instance of ErrorRuntimeConfig
            // if a ThreadAbortException is thrown during this method. 
            Debug.Assert(_runtimeConfig == RuntimeConfig.GetErrorRuntimeConfig(), "_runtimeConfig == RuntimeConfig.GetErrorRuntimeConfig()");
 
            if (!HttpConfigurationSystem.UseHttpConfigurationSystem) { 
                //
                // configRecord may legitimately be null if we are not using the HttpConfigurationSystem. 
                //
                _runtimeConfig = null;
            }
            else { 
                IInternalConfigRecord configRecord = HttpConfigurationSystem.GetUniqueConfigRecord(_configPath);
                Debug.Assert(configRecord != null, "configRecord != null"); 
 
                if (configRecord.ConfigPath.Length == _configPath.Length) {
                    // 
                    // The config is unique to this path, so this make this record the owner of the config.
                    //
                    _flags[FOwnsConfigRecord] = true;
                    _runtimeConfig = new RuntimeConfig(configRecord); 
                }
                else { 
                    // 
                    // The config record is the same as an ancestor's, so use the parent's RuntimeConfig.
                    // 
                    Debug.Assert(parentData != null, "parentData != null");
                    _runtimeConfig = parentData._runtimeConfig;
                }
            } 
        }
 
        void WaitForInit() { 
            // Wait for the data to be initialized.
            if (!_flags[FInited]) { 
                lock (this) {
                    if (!_flags[FInited]) {
                        Monitor.Wait(this);
                    } 
                }
            } 
        } 

        internal bool CompletedFirstRequest { 
            get {return _flags[FCompletedFirstRequest];}
            set {
                _flags[FCompletedFirstRequest] = value;
            } 
        }
 
        internal VirtualPath Path { 
            get {return _virtualPath;}
        } 

        internal string PhysicalPath {
            get {return _physicalPath;}
        } 

        internal bool Exists { 
            get {return _flags[FExists];} 
        }
 
        internal HandlerMappingMemo CachedHandler {
            get {return _handlerMemo;}
            set {_handlerMemo = value;}
        } 

 
        internal IInternalConfigRecord ConfigRecord { 
            get {
                // _runtimeConfig may be null if we are not using the HttpConfigurationSystem. 
                return (_runtimeConfig != null) ? _runtimeConfig.ConfigRecord : null;
            }
        }
 
        internal RuntimeConfig RuntimeConfig {
            get { 
                return _runtimeConfig; 
            }
        } 
    }
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK