536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
646 lines
29 KiB
C#
646 lines
29 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="CachedPathData.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
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?
|
|
internal const int FAnonymousAccessChecked = 0x0100;
|
|
internal const int FAnonymousAccessAllowed = 0x0200;
|
|
|
|
static CacheItemRemovedCallback s_callback = new CacheItemRemovedCallback(CachedPathData.OnCacheItemRemoved);
|
|
// initialize the URL metadata cache expiration here, just in case there's an issue with HttpRuntime.HostingInit
|
|
private static TimeSpan s_urlMetadataSlidingExpiration = HostingEnvironmentSection.DefaultUrlMetadataSlidingExpiration;
|
|
private static bool s_doNotCacheUrlMetadata = false;
|
|
private static int s_appConfigPathLength = 0;
|
|
|
|
#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;
|
|
|
|
// VSWhidbey 607683: Config loading for web app has a dependency on CachedPathData.
|
|
// On the other hand, Config also has a dependency on Uri class which has
|
|
// a new static constructor that calls config, and eventually to CachedPathData again.
|
|
// We need a dummy reference to Uri class so the static constructor would be involved
|
|
// first to initialize config.
|
|
string dummy = System.Uri.SchemeDelimiter;
|
|
|
|
}
|
|
|
|
//
|
|
// Called by HttpRuntime.HostingInit to initialize UrlMetadataSlidingExpiration
|
|
//
|
|
static internal void InitializeUrlMetadataSlidingExpiration(HostingEnvironmentSection section) {
|
|
TimeSpan slidingExp = section.UrlMetadataSlidingExpiration;
|
|
if (slidingExp == TimeSpan.Zero) {
|
|
// a value of TimeSpan.Zero means don't cache
|
|
// this "feature" was added for Bing, because they
|
|
// have scenarios where the same URL is never seen twice
|
|
s_doNotCacheUrlMetadata = true;
|
|
}
|
|
else if (slidingExp == TimeSpan.MaxValue) {
|
|
// a value of TimeSpan.MaxValue means use Cache.NoSlidingExpiration,
|
|
// which is how CachedPathData used to be cached, so this effectively
|
|
// reverts to v2.0 behavior for caching CachedPathData
|
|
s_urlMetadataSlidingExpiration = Cache.NoSlidingExpiration;
|
|
s_doNotCacheUrlMetadata = false;
|
|
}
|
|
else {
|
|
// anything in between means cache with that sliding expiration
|
|
s_urlMetadataSlidingExpiration = slidingExp;
|
|
s_doNotCacheUrlMetadata = false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 check that it is valid.
|
|
//
|
|
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);
|
|
}
|
|
|
|
// Dev10 862204: AppDomain does not restart when the application's web.config is touched 2 minutes after the last request
|
|
static private bool IsCachedPathDataRemovable(string configPath) {
|
|
// have we initialized yet?
|
|
if (s_appConfigPathLength == 0) {
|
|
// when hosted use AppConfigPath, otherwise use RootWebConfigPath
|
|
s_appConfigPathLength = (HostingEnvironment.IsHosted) ? HostingEnvironment.AppConfigPath.Length : WebConfigurationHost.RootWebConfigPath.Length;
|
|
}
|
|
// Only config paths beneath the application config path can be removed from the cache.
|
|
return (configPath.Length > s_appConfigPathLength);
|
|
}
|
|
|
|
// 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)");
|
|
bool exists = false;
|
|
bool isDirectory = false;
|
|
bool isRemovable = IsCachedPathDataRemovable(configPath);
|
|
// if the sliding expiration is zero, we won't cache it unless it is a configPath for root web.config or above
|
|
if (isRemovable && DoNotCacheUrlMetadata) {
|
|
string pathSiteID = null;
|
|
VirtualPath virtualFilePath = null;
|
|
string physicalFilePath = null;
|
|
WebConfigurationHost.GetSiteIDAndVPathFromConfigPath(configPath, out pathSiteID, out virtualFilePath);
|
|
physicalFilePath = GetPhysicalPath(virtualFilePath);
|
|
|
|
string parentConfigPath = ConfigPathUtility.GetParent(configPath);
|
|
CachedPathData pathParentData = GetConfigPathData(parentConfigPath);
|
|
if (!String.IsNullOrEmpty(physicalFilePath)) {
|
|
FileUtil.PhysicalPathStatus(physicalFilePath, false, false, out exists, out isDirectory);
|
|
}
|
|
CachedPathData pathData = new CachedPathData(configPath, virtualFilePath, physicalFilePath, exists);
|
|
pathData.Init(pathParentData);
|
|
|
|
return pathData;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
CacheStoreProvider cacheInternal = HttpRuntime.Cache.InternalCache;
|
|
CachedPathData data = (CachedPathData) cacheInternal.Get(key);
|
|
|
|
// if found, return the data
|
|
if (data != null) {
|
|
data.WaitForInit();
|
|
return data;
|
|
}
|
|
|
|
// WOS
|
|
|
|
|
|
bool cacheEntryIsNotRemovable = false;
|
|
|
|
// if not found, try to add it
|
|
string siteID = null;
|
|
VirtualPath virtualPath = null;
|
|
CachedPathData parentData = null;
|
|
CacheDependency dependency = null;
|
|
string physicalPath = null;
|
|
string[] fileDependencies = null;
|
|
string[] cacheItemDependencies = null;
|
|
|
|
if (WebConfigurationHost.IsMachineConfigPath(configPath)) {
|
|
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
|
|
cacheEntryIsNotRemovable = true;
|
|
}
|
|
else {
|
|
cacheEntryIsNotRemovable = !isRemovable;
|
|
WebConfigurationHost.GetSiteIDAndVPathFromConfigPath(configPath, out siteID, out virtualPath);
|
|
physicalPath = GetPhysicalPath(virtualPath);
|
|
|
|
// 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.
|
|
if (!String.IsNullOrEmpty(physicalPath)) {
|
|
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;
|
|
TimeSpan slidingExpiration = cacheEntryIsNotRemovable ? Cache.NoSlidingExpiration : UrlMetadataSlidingExpiration;
|
|
try {
|
|
using (dependency) {
|
|
dataAdd = new CachedPathData(configPath, virtualPath, physicalPath, exists);
|
|
try {
|
|
}
|
|
finally {
|
|
data = (CachedPathData)cacheInternal.Add(key, dataAdd, new CacheInsertOptions() {
|
|
Dependencies = dependency,
|
|
SlidingExpiration = slidingExpiration,
|
|
Priority = priority,
|
|
OnRemovedCallback = 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).
|
|
//
|
|
// NOTE: It is important to reinsert the item into the cache AFTER dropping
|
|
// the lock on dataAdd, in order to prevent the possibility of deadlock.
|
|
//
|
|
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.Insert(key, dataAdd, new CacheInsertOptions() {
|
|
Dependencies = dependency,
|
|
AbsoluteExpiration = DateTime.UtcNow.AddSeconds(5),
|
|
OnRemovedCallback = s_callback
|
|
});
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return dataAdd;
|
|
}
|
|
|
|
// Ensure that the physical path does not look suspicious (MSRC 5556).
|
|
static private string GetPhysicalPath(VirtualPath virtualPath) {
|
|
string physicalPath = null;
|
|
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);
|
|
|
|
return physicalPath;
|
|
}
|
|
|
|
// 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) {
|
|
CacheStoreProvider cacheInternal = HttpRuntime.Cache.InternalCache;
|
|
|
|
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) {
|
|
CacheStoreProvider cacheInternal = HttpRuntime.Cache.InternalCache;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure that Request.PhysicalPath is valid (canonical, not too long, and contains valid characters).
|
|
// The work is done by CheckSuspiciousPhysicalPath, but as a perf optimization, we can compare
|
|
// Request.PhysicalPath with the cached path result. The cached path result is validated before
|
|
// it is cached. As long as the cached path result is identical to Request.PhysicalPath, we don't
|
|
// have to call CheckSuspiciousPhysicalPath again.
|
|
internal void ValidatePath(String physicalPath) {
|
|
if (String.IsNullOrEmpty(_physicalPath) && String.IsNullOrEmpty(physicalPath)) {
|
|
return;
|
|
}
|
|
if (!String.IsNullOrEmpty(_physicalPath) && !String.IsNullOrEmpty(physicalPath)) {
|
|
if (_physicalPath.Length == physicalPath.Length) {
|
|
// if identical, we don't have to call CheckSuspiciousPhysicalPath
|
|
if (0 == String.Compare(_physicalPath, 0, physicalPath, 0, physicalPath.Length, StringComparison.OrdinalIgnoreCase)) {
|
|
return;
|
|
}
|
|
}
|
|
else if (_physicalPath.Length - physicalPath.Length == 1) {
|
|
// if they differ by a trailing slash, we shouldn't call CheckSuspiciousPhysicalPath again
|
|
if (_physicalPath[_physicalPath.Length-1] == System.IO.Path.DirectorySeparatorChar
|
|
&& (0 == String.Compare(_physicalPath, 0, physicalPath, 0, physicalPath.Length, StringComparison.OrdinalIgnoreCase))) {
|
|
return;
|
|
}
|
|
}
|
|
else if (physicalPath.Length - _physicalPath.Length == 1) {
|
|
// if they differ by a trailing slash, we shouldn't call CheckSuspiciousPhysicalPath again
|
|
if (physicalPath[physicalPath.Length-1] == System.IO.Path.DirectorySeparatorChar
|
|
&& (0 == String.Compare(_physicalPath, 0, physicalPath, 0, _physicalPath.Length, StringComparison.OrdinalIgnoreCase))) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we're here, the paths were different, which normally should not happen.
|
|
Debug.Assert(false, "ValidatePath optimization failed: Request.PhysicalPath="
|
|
+ physicalPath + "; _physicalPath=" + _physicalPath);
|
|
FileUtil.CheckSuspiciousPhysicalPath(physicalPath);
|
|
}
|
|
|
|
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 AnonymousAccessChecked {
|
|
get { return _flags[FAnonymousAccessChecked]; }
|
|
set { _flags[FAnonymousAccessChecked] = value; }
|
|
}
|
|
|
|
internal bool AnonymousAccessAllowed {
|
|
get { return _flags[FAnonymousAccessAllowed]; }
|
|
set { _flags[FAnonymousAccessAllowed] = value; }
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Any time we cache metadata for the URL, we should use this
|
|
// sliding expiration, unless DoNotCacheUrlMetadata is true.
|
|
// This is currently used by CachedPathData, MapPathBasedVirtualPathProvider,
|
|
// FileAuthorizationModule, ProcessHostMapPath and MetabaseServerConfig.
|
|
internal static TimeSpan UrlMetadataSlidingExpiration {
|
|
get {
|
|
return s_urlMetadataSlidingExpiration;
|
|
}
|
|
}
|
|
|
|
// if true, do not cache at all.
|
|
internal static bool DoNotCacheUrlMetadata {
|
|
get { return s_doNotCacheUrlMetadata; }
|
|
}
|
|
}
|
|
}
|
|
|