//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ /* * The ASP.NET runtime services * * Copyright (c) 1998 Microsoft Corporation */ namespace System.Web { using System; using System.Collections; using System.Configuration; using System.Data; using System.Data.Common; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Net; using System.Reflection; using System.Resources; using System.Runtime; using System.Runtime.InteropServices; using System.Runtime.Remoting.Messaging; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Security.Policy; using System.Security.Principal; using System.Text; using System.Threading; using System.Web; using System.Web.Caching; using System.Web.Compilation; using System.Web.Configuration; using System.Web.Hosting; using System.Web.Management; using System.Web.Security; using System.Web.UI; using System.Web.Util; using System.Xml; using Microsoft.Win32; /// /// Provides a set of ASP.NET runtime services. /// public sealed class HttpRuntime { internal const string codegenDirName = "Temporary ASP.NET Files"; internal const string profileFileName = "profileoptimization.prof"; private static HttpRuntime _theRuntime; // single instance of the class internal static byte[] s_autogenKeys = new byte[1024]; // // Names of special ASP.NET directories // internal const string BinDirectoryName = "bin"; internal const string CodeDirectoryName = "App_Code"; internal const string WebRefDirectoryName = "App_WebReferences"; internal const string ResourcesDirectoryName = "App_GlobalResources"; internal const string LocalResourcesDirectoryName = "App_LocalResources"; internal const string DataDirectoryName = "App_Data"; internal const string ThemesDirectoryName = "App_Themes"; internal const string GlobalThemesDirectoryName = "Themes"; internal const string BrowsersDirectoryName = "App_Browsers"; private static string DirectorySeparatorString = new string(Path.DirectorySeparatorChar, 1); private static string DoubleDirectorySeparatorString = new string(Path.DirectorySeparatorChar, 2); private static char[] s_InvalidPhysicalPathChars = { '/', '?', '*', '<', '>', '|', '"' }; #if OLD // For s_forbiddenDirs and s_forbiddenDirsConstant, see // ndll.h, and RestrictIISFolders in regiis.cxx internal static string[] s_forbiddenDirs = { BinDirectoryName, CodeDirectoryName, DataDirectoryName, ResourcesDirectoryName, WebRefDirectoryName, }; internal static Int32[] s_forbiddenDirsConstant = { UnsafeNativeMethods.RESTRICT_BIN, UnsafeNativeMethods.RESTRICT_CODE, UnsafeNativeMethods.RESTRICT_DATA, UnsafeNativeMethods.RESTRICT_RESOURCES, UnsafeNativeMethods.RESTRICT_WEBREFERENCES, }; #endif static HttpRuntime() { AddAppDomainTraceMessage("*HttpRuntime::cctor"); StaticInit(); _theRuntime = new HttpRuntime(); _theRuntime.Init(); AddAppDomainTraceMessage("HttpRuntime::cctor*"); } [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] public HttpRuntime() { } // // static initialization to get hooked up to the unmanaged code // get installation directory, etc. // private static bool s_initialized = false; private static String s_installDirectory; private static bool s_isEngineLoaded = false; // Force the static initialization of this class. internal static void ForceStaticInit() { } private static void StaticInit() { if (s_initialized) { // already initialized return; } bool isEngineLoaded = false; bool wasEngineLoadedHere = false; String installDir = null; // load webengine.dll if it is not loaded already #if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features installDir = RuntimeEnvironment.GetRuntimeDirectory(); if (UnsafeNativeMethods.GetModuleHandle(ModName.ENGINE_FULL_NAME) != IntPtr.Zero) { isEngineLoaded = true; } // Load webengine.dll if not loaded already if (!isEngineLoaded) { String fullPath = installDir + Path.DirectorySeparatorChar + ModName.ENGINE_FULL_NAME; if (UnsafeNativeMethods.LoadLibrary(fullPath) != IntPtr.Zero) { isEngineLoaded = true; wasEngineLoadedHere = true; } } if (isEngineLoaded) { UnsafeNativeMethods.InitializeLibrary(false); if (wasEngineLoadedHere) { UnsafeNativeMethods.PerfCounterInitialize(); } } #else // !FEATURE_PAL string p = typeof(object).Module.FullyQualifiedName; installDir = Path.GetDirectoryName(p); #endif // !FEATURE_PAL s_installDirectory = installDir; s_isEngineLoaded = isEngineLoaded; s_initialized = true; PopulateIISVersionInformation(); AddAppDomainTraceMessage("Initialize"); } // // Runtime services // private NamedPermissionSet _namedPermissionSet; private PolicyLevel _policyLevel; private string _hostSecurityPolicyResolverType = null; private FileChangesMonitor _fcm; private Cache _cachePublic; private bool _isOnUNCShare; private Profiler _profiler; private RequestTimeoutManager _timeoutManager; private RequestQueue _requestQueue; private bool _apartmentThreading; private bool _processRequestInApplicationTrust; private bool _disableProcessRequestInApplicationTrust; private bool _isLegacyCas; // // Counters // private bool _beforeFirstRequest = true; private DateTime _firstRequestStartTime; private bool _firstRequestCompleted; private bool _userForcedShutdown; private bool _configInited; private bool _fusionInited; private int _activeRequestCount; private volatile bool _disposingHttpRuntime; private DateTime _lastShutdownAttemptTime; private bool _shutdownInProgress; private String _shutDownStack; private String _shutDownMessage; private ApplicationShutdownReason _shutdownReason = ApplicationShutdownReason.None; private string _trustLevel; private string _wpUserId; private bool _shutdownWebEventRaised; // // Header Newlines // private bool _enableHeaderChecking; // // Callbacks // private AsyncCallback _requestNotificationCompletionCallback; private AsyncCallback _handlerCompletionCallback; private HttpWorkerRequest.EndOfSendNotification _asyncEndOfSendCallback; private WaitCallback _appDomainUnloadallback; // // Initialization error (to be reported on subsequent requests) // private Exception _initializationError; private bool _hostingInitFailed; // make such errors non-sticky private Timer _appDomainShutdownTimer = null; // // App domain related // private String _tempDir; private String _codegenDir; private String _appDomainAppId; private String _appDomainAppPath; private VirtualPath _appDomainAppVPath; private String _appDomainId; // // Debugging support // private bool _debuggingEnabled = false; // // App_Offline.htm support // private const string AppOfflineFileName = "App_Offline.htm"; private const long MaxAppOfflineFileLength = 1024 * 1024; private byte[] _appOfflineMessage; // // Client script support // private const string AspNetClientFilesSubDirectory = "asp.netclientfiles"; private const string AspNetClientFilesParentVirtualPath = "/aspnet_client/system_web/"; private string _clientScriptVirtualPath; private string _clientScriptPhysicalPath; // // IIS version and whether we're using the integrated pipeline // private static Version _iisVersion; private static bool _useIntegratedPipeline; // // Prefetch // private static bool _enablePrefetchOptimization; ///////////////////////////////////////////////////////////////////////// // 3 steps of initialization: // Init() is called from HttpRuntime cctor // HostingInit() is called by the Hosting Environment // FirstRequestInit() is called on first HTTP request // /* * Context-less initialization (on app domain creation) */ private void Init() { try { #if !FEATURE_PAL if (Environment.OSVersion.Platform != PlatformID.Win32NT) throw new PlatformNotSupportedException(SR.GetString(SR.RequiresNT)); #else // !FEATURE_PAL // ROTORTODO // Do nothing: FEATURE_PAL environment will always support ASP.NET hosting #endif // !FEATURE_PAL _profiler = new Profiler(); _timeoutManager = new RequestTimeoutManager(); _wpUserId = GetCurrentUserName(); _requestNotificationCompletionCallback = new AsyncCallback(this.OnRequestNotificationCompletion); _handlerCompletionCallback = new AsyncCallback(this.OnHandlerCompletion); _asyncEndOfSendCallback = new HttpWorkerRequest.EndOfSendNotification(this.EndOfSendCallback); _appDomainUnloadallback = new WaitCallback(this.ReleaseResourcesAndUnloadAppDomain); // appdomain values if (GetAppDomainString(".appDomain") != null) { Debug.Assert(HostingEnvironment.IsHosted); _appDomainAppId = GetAppDomainString(".appId"); _appDomainAppPath = GetAppDomainString(".appPath"); _appDomainAppVPath = VirtualPath.CreateNonRelativeTrailingSlash(GetAppDomainString(".appVPath")); _appDomainId = GetAppDomainString(".domainId"); _isOnUNCShare = StringUtil.StringStartsWith(_appDomainAppPath, "\\\\"); // init perf counters for this appdomain PerfCounters.Open(_appDomainAppId); } else { Debug.Assert(!HostingEnvironment.IsHosted); } // _appDomainAppPath should be set before file change notifications are initialized // DevDiv 248126: Check httpRuntime fcnMode first before we use the registry key _fcm = new FileChangesMonitor(HostingEnvironment.FcnMode); } catch (Exception e) { // remember static initalization error InitializationException = e; } } private void SetUpDataDirectory() { // Set the DataDirectory (see VSWhidbey 226834) with permission (DevDiv 29614) string dataDirectory = Path.Combine(_appDomainAppPath, DataDirectoryName); AppDomain.CurrentDomain.SetData("DataDirectory", dataDirectory, new FileIOPermission(FileIOPermissionAccess.PathDiscovery, dataDirectory)); } private void DisposeAppDomainShutdownTimer() { Timer timer = _appDomainShutdownTimer; if (timer != null && Interlocked.CompareExchange(ref _appDomainShutdownTimer, null, timer) == timer) { timer.Dispose(); } } private void AppDomainShutdownTimerCallback(Object state) { try { DisposeAppDomainShutdownTimer(); ShutdownAppDomain(ApplicationShutdownReason.InitializationError, "Initialization Error"); } catch { } // ignore exceptions } /* * Restart the AppDomain in 10 seconds */ private void StartAppDomainShutdownTimer() { if (_appDomainShutdownTimer == null && !_shutdownInProgress) { lock (this) { if (_appDomainShutdownTimer == null && !_shutdownInProgress) { _appDomainShutdownTimer = new Timer( new TimerCallback(this.AppDomainShutdownTimerCallback), null, 10 * 1000, 0); } } } } /* * Initialization from HostingEnvironment of HTTP independent features */ private void HostingInit(HostingEnvironmentFlags hostingFlags, PolicyLevel policyLevel, Exception appDomainCreationException) { using (new ApplicationImpersonationContext()) { try { // To ignore FCN during initialization _firstRequestStartTime = DateTime.UtcNow; SetUpDataDirectory(); // Throw an exception about lack of access to app directory early on EnsureAccessToApplicationDirectory(); // Monitor renames to directories we are watching, and notifications on the bin directory // // Note that this must be the first monitoring that we do of the application directory. // There is a bug in Windows 2000 Server where notifications on UNC shares do not // happen correctly if: // 1. the directory is monitored for regular notifications // 2. the directory is then monitored for directory renames // 3. the directory is monitored again for regular notifications StartMonitoringDirectoryRenamesAndBinDirectory(); // Initialize ObjectCacheHost before config is read, since config relies on the cache if (InitializationException == null) { HostingEnvironment.InitializeObjectCacheHost(); } // // Get the configuration needed to minimally initialize // the components required for a complete configuration system, // especially SetTrustLevel. // // We want to do this before calling SetUpCodegenDirectory(), // to remove the risk of the config system loading // codegen assemblies in full trust (VSWhidbey 460506) // CacheSection cacheSection; TrustSection trustSection; SecurityPolicySection securityPolicySection; CompilationSection compilationSection; HostingEnvironmentSection hostingEnvironmentSection; Exception configInitException; GetInitConfigSections( out cacheSection, out trustSection, out securityPolicySection, out compilationSection, out hostingEnvironmentSection, out configInitException); // Once the configuration system is initialized, we can read // the cache configuration settings. // // Note that we must do this after we start monitoring directory renames, // as reading config will cause file monitoring on the application directory // to occur. // Set up the codegen directory for the app. This needs to be done before we process // the policy file, because it needs to replace the $CodeGen$ token. SetUpCodegenDirectory(compilationSection); if(compilationSection != null) { _enablePrefetchOptimization = compilationSection.EnablePrefetchOptimization; if(_enablePrefetchOptimization) { UnsafeNativeMethods.StartPrefetchActivity((uint)StringUtil.GetStringHashCode(_appDomainAppId)); } } // NOTE: after calling SetUpCodegenDirectory(), and until we call SetTrustLevel(), we are at // risk of codegen assemblies being loaded in full trust. No code that might cause // assembly loading should be added here! This is only valid if the legacyCasModel is set // to true in section. // Throw the original configuration exception from ApplicationManager if configuration is broken. if (appDomainCreationException != null) { throw appDomainCreationException; } if (trustSection == null || String.IsNullOrEmpty(trustSection.Level)) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_section_not_present, "trust")); } if (trustSection.LegacyCasModel) { try { _disableProcessRequestInApplicationTrust = false; _isLegacyCas = true; // Set code access policy on the app domain SetTrustLevel(trustSection, securityPolicySection); } catch { // throw the original config exception if it exists if (configInitException != null) throw configInitException; throw; } } else if ((hostingFlags & HostingEnvironmentFlags.ClientBuildManager) != 0) { _trustLevel = "Full"; } else { _disableProcessRequestInApplicationTrust = true; // Set code access policy properties of the runtime object SetTrustParameters(trustSection, securityPolicySection, policyLevel); } // Configure fusion to use directories set in the app config InitFusion(hostingEnvironmentSection); // set the sliding expiration for URL metadata CachedPathData.InitializeUrlMetadataSlidingExpiration(hostingEnvironmentSection); // Complete initialization of configuration. // Note that this needs to be called after SetTrustLevel, // as it indicates that we have the permission set needed // to correctly run configuration section handlers. // As little config should be read before CompleteInit() as possible. // No section that runs before CompleteInit() should demand permissions, // as the permissions set has not yet determined until SetTrustLevel() // is called. HttpConfigurationSystem.CompleteInit(); // // If an exception occurred loading configuration, // we are now ready to handle exception processing // with the correct trust level set. // if (configInitException != null) { throw configInitException; } SetThreadPoolLimits(); SetAutogenKeys(); // Initialize the build manager BuildManager.InitializeBuildManager(); if(compilationSection != null && compilationSection.ProfileGuidedOptimizations == ProfileGuidedOptimizationsFlags.All) { ProfileOptimization.SetProfileRoot(_codegenDir); ProfileOptimization.StartProfile(profileFileName); } // Determine apartment threading setting InitApartmentThreading(); // Init debugging InitDebuggingSupport(); _processRequestInApplicationTrust = trustSection.ProcessRequestInApplicationTrust; // Init AppDomain Resource Perf Counters AppDomainResourcePerfCounters.Init(); RelaxMapPathIfRequired(); } catch (Exception e) { _hostingInitFailed = true; InitializationException = e; Debug.Trace("AppDomainFactory", "HostingInit failed. " + e.ToString()); if ((hostingFlags & HostingEnvironmentFlags.ThrowHostingInitErrors) != 0) throw; } } } internal static Exception InitializationException { get { return _theRuntime._initializationError; } // The exception is "cached" for 10 seconds, then the AppDomain is restarted. set { _theRuntime._initializationError = value; // In v2.0, we shutdown immediately if hostingInitFailed...so we don't need the timer if (!HostingInitFailed) { _theRuntime.StartAppDomainShutdownTimer(); } } } internal static bool HostingInitFailed { get { return _theRuntime._hostingInitFailed; } } internal static void InitializeHostingFeatures(HostingEnvironmentFlags hostingFlags, PolicyLevel policyLevel, Exception appDomainCreationException) { _theRuntime.HostingInit(hostingFlags, policyLevel, appDomainCreationException); } internal static bool EnableHeaderChecking { get { return _theRuntime._enableHeaderChecking; } } internal static bool ProcessRequestInApplicationTrust { get { return _theRuntime._processRequestInApplicationTrust; } } internal static bool DisableProcessRequestInApplicationTrust { get { return _theRuntime._disableProcessRequestInApplicationTrust; } } internal static bool IsLegacyCas { get { return _theRuntime._isLegacyCas; } } internal static byte[] AppOfflineMessage { get { return _theRuntime._appOfflineMessage; } } /* * Initialization on first request (context available) */ private void FirstRequestInit(HttpContext context) { Exception error = null; if (InitializationException == null && _appDomainId != null) { #if DBG HttpContext.SetDebugAssertOnAccessToCurrent(true); #endif try { using (new ApplicationImpersonationContext()) { // Is this necessary? See InitHttpConfiguration CultureInfo savedCulture = Thread.CurrentThread.CurrentCulture; CultureInfo savedUICulture = Thread.CurrentThread.CurrentUICulture; try { // Ensure config system is initialized InitHttpConfiguration(); // be sure config system is set // Check if applicaton is enabled CheckApplicationEnabled(); // Check access to temp compilation directory (under hosting identity) CheckAccessToTempDirectory(); // Initialize health monitoring InitializeHealthMonitoring(); // Init request queue (after reading config) InitRequestQueue(); // configure the profiler according to config InitTrace(context); // Start heatbeat for Web Event Health Monitoring HealthMonitoringManager.StartHealthMonitoringHeartbeat(); // Remove read and browse access of the bin directory RestrictIISFolders(context); // Preload all assemblies from bin (only if required). ASURT 114486 PreloadAssembliesFromBin(); // Decide whether or not to encode headers. VsWhidbey 257154 InitHeaderEncoding(); // Force the current encoder + validator to load so that there's a deterministic // place (here) for an exception to occur if there's a load error HttpEncoder.InitializeOnFirstRequest(); RequestValidator.InitializeOnFirstRequest(); if (context.WorkerRequest is ISAPIWorkerRequestOutOfProc) { // Make sure that the section has no errors ProcessModelSection processModel = RuntimeConfig.GetMachineConfig().ProcessModel; } } finally { Thread.CurrentThread.CurrentUICulture = savedUICulture; SetCurrentThreadCultureWithAssert(savedCulture); } } } catch (ConfigurationException e) { error = e; } catch (Exception e) { // remember second-phase initialization error error = new HttpException(SR.GetString(SR.XSP_init_error, e.Message), e); } finally { #if DBG HttpContext.SetDebugAssertOnAccessToCurrent(false); #endif } } if (InitializationException != null) { // throw cached exception. We need to wrap it in a new exception, otherwise // we lose the original stack. throw new HttpException(InitializationException.Message, InitializationException); } else if (error != null) { InitializationException = error; // throw new exception throw error; } AddAppDomainTraceMessage("FirstRequestInit"); } [SecurityPermission(SecurityAction.Assert, ControlThread = true)] internal static void SetCurrentThreadCultureWithAssert(CultureInfo cultureInfo) { Thread.CurrentThread.CurrentCulture = cultureInfo; } private void EnsureFirstRequestInit(HttpContext context) { if (_beforeFirstRequest) { lock (this) { if (_beforeFirstRequest) { _firstRequestStartTime = DateTime.UtcNow; FirstRequestInit(context); _beforeFirstRequest = false; context.FirstRequest = true; } } } } private void EnsureAccessToApplicationDirectory() { if (!FileUtil.DirectoryAccessible(_appDomainAppPath)) { // if (_appDomainAppPath.IndexOf('?') >= 0) { // Possible Unicode when not supported throw new HttpException(SR.GetString(SR.Access_denied_to_unicode_app_dir, _appDomainAppPath)); } else { throw new HttpException(SR.GetString(SR.Access_denied_to_app_dir, _appDomainAppPath)); } } } private void StartMonitoringDirectoryRenamesAndBinDirectory() { _fcm.StartMonitoringDirectoryRenamesAndBinDirectory(AppDomainAppPathInternal, new FileChangeEventHandler(this.OnCriticalDirectoryChange)); } // // Monitor a local resources subdirectory and unload appdomain when it changes // internal static void StartListeningToLocalResourcesDirectory(VirtualPath virtualDir) { #if !FEATURE_PAL // FEATURE_PAL does not enable file change notification _theRuntime._fcm.StartListeningToLocalResourcesDirectory(virtualDir); #endif // !FEATURE_PAL } // // Get the configuration needed to minimally initialize // the components required for a complete configuration system, // // Note that if the application configuration file has an error, // AppLKGConfig will still retreive any valid configuration from // that file, or from location directives that apply to the // application path. This implies that an administrator can // lock down an application's trust level in root web.config, // and it will still take effect if the application's web.config // has errors. // private void GetInitConfigSections( out CacheSection cacheSection, out TrustSection trustSection, out SecurityPolicySection securityPolicySection, out CompilationSection compilationSection, out HostingEnvironmentSection hostingEnvironmentSection, out Exception initException) { cacheSection = null; trustSection = null; securityPolicySection = null; compilationSection = null; hostingEnvironmentSection = null; initException = null; // AppLKGConfig is guaranteed to not throw an exception. RuntimeConfig appLKGConfig = RuntimeConfig.GetAppLKGConfig(); // AppConfig may throw an exception. RuntimeConfig appConfig = null; try { appConfig = RuntimeConfig.GetAppConfig(); } catch (Exception e) { initException = e; } // Cache section if (appConfig != null) { try { cacheSection = appConfig.Cache; } catch (Exception e) { if (initException == null) { initException = e; } } } if (cacheSection == null) { cacheSection = appLKGConfig.Cache; } // Trust section if (appConfig != null) { try { trustSection = appConfig.Trust; } catch (Exception e) { if (initException == null) { initException = e; } } } if (trustSection == null) { trustSection = appLKGConfig.Trust; } // SecurityPolicy section if (appConfig != null) { try { securityPolicySection = appConfig.SecurityPolicy; } catch (Exception e) { if (initException == null) { initException = e; } } } if (securityPolicySection == null) { securityPolicySection = appLKGConfig.SecurityPolicy; } // Compilation section if (appConfig != null) { try { compilationSection = appConfig.Compilation; } catch (Exception e) { if (initException == null) { initException = e; } } } if (compilationSection == null) { compilationSection = appLKGConfig.Compilation; } // HostingEnvironment section if (appConfig != null) { try { hostingEnvironmentSection = appConfig.HostingEnvironment; } catch (Exception e) { if (initException == null) { initException = e; } } } if (hostingEnvironmentSection == null) { hostingEnvironmentSection = appLKGConfig.HostingEnvironment; } } // Set up the codegen directory for the app [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This call site is trusted.")] private void SetUpCodegenDirectory(CompilationSection compilationSection) { AppDomain appDomain = Thread.GetDomain(); string codegenBase; // devdiv 1038337. Passing the corresponding IsDevelopmentEnvironment flag to ConstructSimpleAppName string simpleAppName = System.Web.Hosting.AppManagerAppDomainFactory.ConstructSimpleAppName( AppDomainAppVirtualPath, HostingEnvironment.IsDevelopmentEnvironment); string tempDirectory = null; // These variables are used for error handling string tempDirAttribName = null; string configFileName = null; int configLineNumber = 0; if (compilationSection != null && !String.IsNullOrEmpty(compilationSection.TempDirectory)) { tempDirectory = compilationSection.TempDirectory; compilationSection.GetTempDirectoryErrorInfo(out tempDirAttribName, out configFileName, out configLineNumber); } if (tempDirectory != null) { tempDirectory = tempDirectory.Trim(); if (!Path.IsPathRooted(tempDirectory)) { // Make sure the path is not relative (VSWhidbey 260075) tempDirectory = null; } else { try { // Canonicalize it to avoid problems with spaces (VSWhidbey 229873) tempDirectory = new DirectoryInfo(tempDirectory).FullName; } catch { tempDirectory = null; } } if (tempDirectory == null) { throw new ConfigurationErrorsException( SR.GetString(SR.Invalid_temp_directory, tempDirAttribName), configFileName, configLineNumber); } #if FEATURE_PAL } else { System.UInt32 length = 0; StringBuilder sb = null; bool bRet; // Get the required length bRet = UnsafeNativeMethods.GetUserTempDirectory( UnsafeNativeMethods.DeploymentDirectoryType.ddtInstallationDependentDirectory, null, ref length); if (true == bRet) { // now, allocate the string sb = new StringBuilder ((int)length); // call again to get the value bRet = UnsafeNativeMethods.GetUserTempDirectory( UnsafeNativeMethods.DeploymentDirectoryType.ddtInstallationDependentDirectory, sb, ref length); } if (false == bRet) { throw new ConfigurationException( HttpRuntime.FormatResourceString(SR.Invalid_temp_directory, tempDirAttribName)); } tempDirectory = Path.Combine(sb.ToString(), codegenDirName); } // Always try to create the ASP.Net temp directory for FEATURE_PAL #endif // FEATURE_PAL // Create the config-specified directory if needed try { Directory.CreateDirectory(tempDirectory); } catch (Exception e) { throw new ConfigurationErrorsException( SR.GetString(SR.Invalid_temp_directory, tempDirAttribName), e, configFileName, configLineNumber); } #if !FEATURE_PAL } else { tempDirectory = Path.Combine(s_installDirectory, codegenDirName); } #endif // !FEATURE_PAL // If we don't have write access to the codegen dir, use the TEMP dir instead. // This will allow non-admin users to work in hosting scenarios (e.g. Venus, aspnet_compiler) if (!System.Web.UI.Util.HasWriteAccessToDirectory(tempDirectory)) { // Don't do this if we are not in a CBM scenario and we're in a service (!UserInteractive), // as TEMP could point to unwanted places. #if !FEATURE_PAL // always fail here if ((!BuildManagerHost.InClientBuildManager) && (!Environment.UserInteractive)) #endif // !FEATURE_PAL { throw new HttpException(SR.GetString(SR.No_codegen_access, System.Web.UI.Util.GetCurrentAccountName(), tempDirectory)); } tempDirectory = Path.GetTempPath(); Debug.Assert(System.Web.UI.Util.HasWriteAccessToDirectory(tempDirectory)); tempDirectory = Path.Combine(tempDirectory, codegenDirName); } _tempDir = tempDirectory; codegenBase = Path.Combine(tempDirectory, simpleAppName); #pragma warning disable 0618 // To avoid deprecation warning appDomain.SetDynamicBase(codegenBase); #pragma warning restore 0618 _codegenDir = Thread.GetDomain().DynamicDirectory; // Create the codegen directory if needed Directory.CreateDirectory(_codegenDir); } private void InitFusion(HostingEnvironmentSection hostingEnvironmentSection) { AppDomain appDomain = Thread.GetDomain(); // If there is a double backslash in the string, get rid of it (ASURT 122191) // Make sure to skip the first char, to avoid breaking the UNC case string appDomainAppPath = _appDomainAppPath; if (appDomainAppPath.IndexOf(DoubleDirectorySeparatorString, 1, StringComparison.Ordinal) >= 1) { appDomainAppPath = appDomainAppPath[0] + appDomainAppPath.Substring(1).Replace(DoubleDirectorySeparatorString, DirectorySeparatorString); } #pragma warning disable 0618 // To avoid deprecation warning // Allow assemblies from 'bin' to be loaded appDomain.AppendPrivatePath(appDomainAppPath + BinDirectoryName); #pragma warning restore 0618 // If shadow copying was disabled via config, turn it off (DevDiv 30864) if (hostingEnvironmentSection != null && !hostingEnvironmentSection.ShadowCopyBinAssemblies) { #pragma warning disable 0618 // To avoid deprecation warning appDomain.ClearShadowCopyPath(); #pragma warning restore 0618 } else { // enable shadow-copying from bin #pragma warning disable 0618 // To avoid deprecation warning appDomain.SetShadowCopyPath(appDomainAppPath + BinDirectoryName); #pragma warning restore 0618 } // Get rid of the last part of the directory (the app name), since it will // be re-appended. string parentDir = Directory.GetParent(_codegenDir).FullName; #pragma warning disable 0618 // To avoid deprecation warning appDomain.SetCachePath(parentDir); #pragma warning restore 0618 _fusionInited = true; } private void InitRequestQueue() { RuntimeConfig config = RuntimeConfig.GetAppConfig(); HttpRuntimeSection runtimeConfig = config.HttpRuntime; ProcessModelSection processConfig = config.ProcessModel; if (processConfig.AutoConfig) { _requestQueue = new RequestQueue( 88 * processConfig.CpuCount, 76 * processConfig.CpuCount, runtimeConfig.AppRequestQueueLimit, processConfig.ClientConnectedCheck); } else { // Configuration section handlers cannot validate values based on values // in other configuration sections, so we validate minFreeThreads and // minLocalRequestFreeThreads here. int maxThreads = (processConfig.MaxWorkerThreadsTimesCpuCount < processConfig.MaxIoThreadsTimesCpuCount) ? processConfig.MaxWorkerThreadsTimesCpuCount : processConfig.MaxIoThreadsTimesCpuCount; // validate minFreeThreads if (runtimeConfig.MinFreeThreads >= maxThreads) { if (runtimeConfig.ElementInformation.Properties["minFreeThreads"].LineNumber == 0) { if (processConfig.ElementInformation.Properties["maxWorkerThreads"].LineNumber != 0) { throw new ConfigurationErrorsException(SR.GetString(SR.Thread_pool_limit_must_be_greater_than_minFreeThreads, runtimeConfig.MinFreeThreads.ToString(CultureInfo.InvariantCulture)), processConfig.ElementInformation.Properties["maxWorkerThreads"].Source, processConfig.ElementInformation.Properties["maxWorkerThreads"].LineNumber); } else { throw new ConfigurationErrorsException(SR.GetString(SR.Thread_pool_limit_must_be_greater_than_minFreeThreads, runtimeConfig.MinFreeThreads.ToString(CultureInfo.InvariantCulture)), processConfig.ElementInformation.Properties["maxIoThreads"].Source, processConfig.ElementInformation.Properties["maxIoThreads"].LineNumber); } } else { throw new ConfigurationErrorsException(SR.GetString(SR.Min_free_threads_must_be_under_thread_pool_limits, maxThreads.ToString(CultureInfo.InvariantCulture)), runtimeConfig.ElementInformation.Properties["minFreeThreads"].Source, runtimeConfig.ElementInformation.Properties["minFreeThreads"].LineNumber); } } // validate minLocalRequestFreeThreads if (runtimeConfig.MinLocalRequestFreeThreads > runtimeConfig.MinFreeThreads) { if (runtimeConfig.ElementInformation.Properties["minLocalRequestFreeThreads"].LineNumber == 0) { throw new ConfigurationErrorsException(SR.GetString(SR.Local_free_threads_cannot_exceed_free_threads), processConfig.ElementInformation.Properties["minFreeThreads"].Source, processConfig.ElementInformation.Properties["minFreeThreads"].LineNumber); } else { throw new ConfigurationErrorsException(SR.GetString(SR.Local_free_threads_cannot_exceed_free_threads), runtimeConfig.ElementInformation.Properties["minLocalRequestFreeThreads"].Source, runtimeConfig.ElementInformation.Properties["minLocalRequestFreeThreads"].LineNumber); } } _requestQueue = new RequestQueue( runtimeConfig.MinFreeThreads, runtimeConfig.MinLocalRequestFreeThreads, runtimeConfig.AppRequestQueueLimit, processConfig.ClientConnectedCheck); } } private void InitApartmentThreading() { HttpRuntimeSection runtimeConfig = RuntimeConfig.GetAppConfig().HttpRuntime; if (runtimeConfig != null) { _apartmentThreading = runtimeConfig.ApartmentThreading; } else { _apartmentThreading = false; } } private void InitTrace(HttpContext context) { TraceSection traceConfig = RuntimeConfig.GetAppConfig().Trace; Profile.RequestsToProfile = traceConfig.RequestLimit; Profile.PageOutput = traceConfig.PageOutput; Profile.OutputMode = TraceMode.SortByTime; if (traceConfig.TraceMode == TraceDisplayMode.SortByCategory) Profile.OutputMode = TraceMode.SortByCategory; Profile.LocalOnly = traceConfig.LocalOnly; Profile.IsEnabled = traceConfig.Enabled; Profile.MostRecent = traceConfig.MostRecent; Profile.Reset(); // the first request's context is created before InitTrace, so // we need to set this manually. (ASURT 93730) context.TraceIsEnabled = traceConfig.Enabled; TraceContext.SetWriteToDiagnosticsTrace(traceConfig.WriteToDiagnosticsTrace); } private void InitDebuggingSupport() { CompilationSection compConfig = RuntimeConfig.GetAppConfig().Compilation; _debuggingEnabled = compConfig.Debug; } /* * Pre-load all the bin assemblies if we're impersonated. This way, if user code * calls Assembly.Load while impersonated, the assembly will already be loaded, and * we won't fail due to lack of permissions on the codegen dir (see ASURT 114486) */ [PermissionSet(SecurityAction.Assert, Unrestricted = true)] private void PreloadAssembliesFromBin() { bool appClientImpersonationEnabled = false; if (!_isOnUNCShare) { // if not on UNC share check if config has impersonation enabled (without userName) IdentitySection c = RuntimeConfig.GetAppConfig().Identity; if (c.Impersonate && c.ImpersonateToken == IntPtr.Zero) appClientImpersonationEnabled = true; } if (!appClientImpersonationEnabled) return; // Get the path to the bin directory string binPath = HttpRuntime.BinDirectoryInternal; DirectoryInfo binPathDirectory = new DirectoryInfo(binPath); if (!binPathDirectory.Exists) return; PreloadAssembliesFromBinRecursive(binPathDirectory); } private void PreloadAssembliesFromBinRecursive(DirectoryInfo dirInfo) { FileInfo[] binDlls = dirInfo.GetFiles("*.dll"); // Pre-load all the assemblies, ignoring all exceptions foreach (FileInfo fi in binDlls) { try { Assembly.Load(System.Web.UI.Util.GetAssemblyNameFromFileName(fi.Name)); } catch (FileNotFoundException) { // If Load failed, try LoadFrom (VSWhidbey 493725) try { Assembly.LoadFrom(fi.FullName); } catch { } } catch { } } // Recurse on the subdirectories DirectoryInfo[] subDirs = dirInfo.GetDirectories(); foreach (DirectoryInfo di in subDirs) { PreloadAssembliesFromBinRecursive(di); } } private void SetAutoConfigLimits(ProcessModelSection pmConfig) { // check if the current limits are ok int workerMax, ioMax; ThreadPool.GetMaxThreads(out workerMax, out ioMax); // only set if different if (pmConfig.DefaultMaxWorkerThreadsForAutoConfig != workerMax || pmConfig.DefaultMaxIoThreadsForAutoConfig != ioMax) { Debug.Trace("ThreadPool", "SetThreadLimit: from " + workerMax + "," + ioMax + " to " + pmConfig.DefaultMaxWorkerThreadsForAutoConfig + "," + pmConfig.DefaultMaxIoThreadsForAutoConfig); UnsafeNativeMethods.SetClrThreadPoolLimits(pmConfig.DefaultMaxWorkerThreadsForAutoConfig, pmConfig.DefaultMaxIoThreadsForAutoConfig, true); } // this is the code equivalent of setting maxconnection // Dev11 141729: Make autoConfig scale by default // Dev11 144842: PERF: Consider removing Max connection limit or changing the default value System.Net.ServicePointManager.DefaultConnectionLimit = Int32.MaxValue; // we call InitRequestQueue later, from FirstRequestInit, and set minFreeThreads and minLocalRequestFreeThreads } private void SetThreadPoolLimits() { try { ProcessModelSection pmConfig = RuntimeConfig.GetMachineConfig().ProcessModel; if (pmConfig.AutoConfig) { // use recommendation in http://support.microsoft.com/?id=821268 SetAutoConfigLimits(pmConfig); } else if (pmConfig.MaxWorkerThreadsTimesCpuCount > 0 && pmConfig.MaxIoThreadsTimesCpuCount > 0) { // check if the current limits are ok int workerMax, ioMax; ThreadPool.GetMaxThreads(out workerMax, out ioMax); // only set if different if (pmConfig.MaxWorkerThreadsTimesCpuCount != workerMax || pmConfig.MaxIoThreadsTimesCpuCount != ioMax) { Debug.Trace("ThreadPool", "SetThreadLimit: from " + workerMax + "," + ioMax + " to " + pmConfig.MaxWorkerThreadsTimesCpuCount + "," + pmConfig.MaxIoThreadsTimesCpuCount); UnsafeNativeMethods.SetClrThreadPoolLimits(pmConfig.MaxWorkerThreadsTimesCpuCount, pmConfig.MaxIoThreadsTimesCpuCount, false); } } if (pmConfig.MinWorkerThreadsTimesCpuCount > 0 || pmConfig.MinIoThreadsTimesCpuCount > 0) { int currentMinWorkerThreads, currentMinIoThreads; ThreadPool.GetMinThreads(out currentMinWorkerThreads, out currentMinIoThreads); int newMinWorkerThreads = pmConfig.MinWorkerThreadsTimesCpuCount > 0 ? pmConfig.MinWorkerThreadsTimesCpuCount : currentMinWorkerThreads; int newMinIoThreads = pmConfig.MinIoThreadsTimesCpuCount > 0 ? pmConfig.MinIoThreadsTimesCpuCount : currentMinIoThreads; if (newMinWorkerThreads > 0 && newMinIoThreads > 0 && (newMinWorkerThreads != currentMinWorkerThreads || newMinIoThreads != currentMinIoThreads)) ThreadPool.SetMinThreads(newMinWorkerThreads, newMinIoThreads); } } catch { } } internal static void CheckApplicationEnabled() { // process App_Offline.htm file string appOfflineFile = Path.Combine(_theRuntime._appDomainAppPath, AppOfflineFileName); bool appOfflineFileFound = false; // monitor even if doesn't exist _theRuntime._fcm.StartMonitoringFile(appOfflineFile, new FileChangeEventHandler(_theRuntime.OnAppOfflineFileChange)); // read the file into memory try { if (File.Exists(appOfflineFile)) { Debug.Trace("AppOffline", "File " + appOfflineFile + " exists. Using it."); using (FileStream fs = new FileStream(appOfflineFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (fs.Length <= MaxAppOfflineFileLength) { int length = (int)fs.Length; if (length > 0) { byte[] message = new byte[length]; if (fs.Read(message, 0, length) == length) { // remember the message _theRuntime._appOfflineMessage = message; appOfflineFileFound = true; } } else { // empty file appOfflineFileFound = true; _theRuntime._appOfflineMessage = new byte[0]; } } } } } catch { // ignore any IO errors reading the file } // throw if there is a valid App_Offline file if (appOfflineFileFound) { throw new HttpException(503, String.Empty); } // process the config setting HttpRuntimeSection runtimeConfig = RuntimeConfig.GetAppConfig().HttpRuntime; if (!runtimeConfig.Enable) { // throw 404 on first request init -- this will get cached until config changes throw new HttpException(404, String.Empty); } } [FileIOPermission(SecurityAction.Assert, Unrestricted = true)] private void CheckAccessToTempDirectory() { // The original check (in HostingInit) was done under process identity // this time we do it under hosting identity if (HostingEnvironment.HasHostingIdentity) { using (new ApplicationImpersonationContext()) { if (!System.Web.UI.Util.HasWriteAccessToDirectory(_tempDir)) { throw new HttpException(SR.GetString(SR.No_codegen_access, System.Web.UI.Util.GetCurrentAccountName(), _tempDir)); } } } } private void InitializeHealthMonitoring() { #if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features ProcessModelSection pmConfig = RuntimeConfig.GetMachineConfig().ProcessModel; int deadLockInterval = (int)pmConfig.ResponseDeadlockInterval.TotalSeconds; int requestQueueLimit = pmConfig.RequestQueueLimit; Debug.Trace("HealthMonitor", "Initalizing: ResponseDeadlockInterval=" + deadLockInterval); UnsafeNativeMethods.InitializeHealthMonitor(deadLockInterval, requestQueueLimit); #endif // !FEATURE_PAL } private static void InitHttpConfiguration() { if (!_theRuntime._configInited) { _theRuntime._configInited = true; HttpConfigurationSystem.EnsureInit(null, true, true); // whenever possible report errors in the user's culture (from machine.config) // Note: this thread's culture is saved/restored during FirstRequestInit, so this is safe // see ASURT 81655 GlobalizationSection globConfig = RuntimeConfig.GetAppLKGConfig().Globalization; if (globConfig != null) { if (!String.IsNullOrEmpty(globConfig.Culture) && !StringUtil.StringStartsWithIgnoreCase(globConfig.Culture, "auto")) SetCurrentThreadCultureWithAssert(HttpServerUtility.CreateReadOnlyCultureInfo(globConfig.Culture)); if (!String.IsNullOrEmpty(globConfig.UICulture) && !StringUtil.StringStartsWithIgnoreCase(globConfig.UICulture, "auto")) Thread.CurrentThread.CurrentUICulture = HttpServerUtility.CreateReadOnlyCultureInfo(globConfig.UICulture); } // check for errors in section RuntimeConfig appConfig = RuntimeConfig.GetAppConfig(); object section = appConfig.ProcessModel; // check for errors in section section = appConfig.HostingEnvironment; } } private void InitHeaderEncoding() { HttpRuntimeSection runtimeConfig = RuntimeConfig.GetAppConfig().HttpRuntime; _enableHeaderChecking = runtimeConfig.EnableHeaderChecking; } private static void SetAutogenKeys() { #if !FEATURE_PAL // FEATURE_PAL does not enable cryptography byte[] bKeysRandom = new byte[s_autogenKeys.Length]; byte[] bKeysStored = new byte[s_autogenKeys.Length]; bool fGetStoredKeys = false; RNGCryptoServiceProvider randgen = new RNGCryptoServiceProvider(); // Gernerate random keys randgen.GetBytes(bKeysRandom); // If getting stored keys via WorkerRequest object failed, get it directly if (!fGetStoredKeys) fGetStoredKeys = (UnsafeNativeMethods.EcbCallISAPI(IntPtr.Zero, UnsafeNativeMethods.CallISAPIFunc.GetAutogenKeys, bKeysRandom, bKeysRandom.Length, bKeysStored, bKeysStored.Length) == 1); // If we managed to get stored keys, copy them in; else use random keys if (fGetStoredKeys) Buffer.BlockCopy(bKeysStored, 0, s_autogenKeys, 0, s_autogenKeys.Length); else Buffer.BlockCopy(bKeysRandom, 0, s_autogenKeys, 0, s_autogenKeys.Length); #endif // !FEATURE_PAL } internal static void IncrementActivePipelineCount() { Interlocked.Increment(ref _theRuntime._activeRequestCount); HostingEnvironment.IncrementBusyCount(); } internal static void DecrementActivePipelineCount() { HostingEnvironment.DecrementBusyCount(); Interlocked.Decrement(ref _theRuntime._activeRequestCount); } internal static void PopulateIISVersionInformation() { if (IsEngineLoaded) { uint dwVersion; bool fIsIntegratedMode; UnsafeIISMethods.MgdGetIISVersionInformation(out dwVersion, out fIsIntegratedMode); if (dwVersion != 0) { // High word is the major version; low word is the minor version (this is MAKELONG format) _iisVersion = new Version((int)(dwVersion >> 16), (int)(dwVersion & 0xffff)); _useIntegratedPipeline = fIsIntegratedMode; } } } // Gets the version of IIS (7.0, 7.5, 8.0, etc.) that is hosting this application, or null if this application isn't IIS-hosted. // Should also return the correct version for IIS Express. public static Version IISVersion { get { return _iisVersion; } } // DevDivBugs 190952: public method for querying runtime pipeline mode public static bool UsingIntegratedPipeline { get { return UseIntegratedPipeline; } } internal static bool UseIntegratedPipeline { get { return _useIntegratedPipeline; } } internal static bool EnablePrefetchOptimization { get { return _enablePrefetchOptimization; } } /* * Process one step of the integrated pipeline * */ internal static RequestNotificationStatus ProcessRequestNotification(IIS7WorkerRequest wr, HttpContext context) { return _theRuntime.ProcessRequestNotificationPrivate(wr, context); } private RequestNotificationStatus ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) { RequestNotificationStatus status = RequestNotificationStatus.Pending; try { int currentModuleIndex; bool isPostNotification; int currentNotification; // setup the HttpContext for this event/module combo UnsafeIISMethods.MgdGetCurrentNotificationInfo(wr.RequestContext, out currentModuleIndex, out isPostNotification, out currentNotification); context.CurrentModuleIndex = currentModuleIndex; context.IsPostNotification = isPostNotification; context.CurrentNotification = (RequestNotification) currentNotification; #if DBG Debug.Trace("PipelineRuntime", "HttpRuntime::ProcessRequestNotificationPrivate: notification=" + context.CurrentNotification.ToString() + ", isPost=" + context.IsPostNotification + ", moduleIndex=" + context.CurrentModuleIndex); #endif IHttpHandler handler = null; if (context.NeedToInitializeApp()) { #if DBG Debug.Trace("FileChangesMonitorIgnoreSubdirChange", "*** FirstNotification " + DateTime.Now.ToString("hh:mm:ss.fff", CultureInfo.InvariantCulture) + ": _appDomainAppId=" + _appDomainAppId); #endif // First request initialization try { EnsureFirstRequestInit(context); } catch { // If we are handling a DEBUG request, ignore the FirstRequestInit exception. // This allows the HttpDebugHandler to execute, and lets the debugger attach to // the process (VSWhidbey 358135) if (!context.Request.IsDebuggingRequest) { throw; } } context.Response.InitResponseWriter(); handler = HttpApplicationFactory.GetApplicationInstance(context); if (handler == null) throw new HttpException(SR.GetString(SR.Unable_create_app_object)); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, handler.GetType().FullName, "Start"); HttpApplication app = handler as HttpApplication; if (app != null) { // associate the context with an application instance app.AssignContext(context); } } // this may throw, and should be called after app initialization wr.SynchronizeVariables(context); if (context.ApplicationInstance != null) { // process request IAsyncResult ar = context.ApplicationInstance.BeginProcessRequestNotification(context, _requestNotificationCompletionCallback); if (ar.CompletedSynchronously) { status = RequestNotificationStatus.Continue; } } else if (handler != null) { // HttpDebugHandler is processed here handler.ProcessRequest(context); status = RequestNotificationStatus.FinishRequest; } else { status = RequestNotificationStatus.Continue; } } catch (Exception e) { status = RequestNotificationStatus.FinishRequest; context.Response.InitResponseWriter(); // errors are handled in HttpRuntime::FinishRequestNotification context.AddError(e); } if (status != RequestNotificationStatus.Pending) { // we completed synchronously FinishRequestNotification(wr, context, ref status); } #if DBG Debug.Trace("PipelineRuntime", "HttpRuntime::ProcessRequestNotificationPrivate: status=" + status.ToString()); #endif return status; } private void FinishRequestNotification(IIS7WorkerRequest wr, HttpContext context, ref RequestNotificationStatus status) { Debug.Assert(status != RequestNotificationStatus.Pending, "status != RequestNotificationStatus.Pending"); HttpApplication app = context.ApplicationInstance; if (context.NotificationContext.RequestCompleted) { status = RequestNotificationStatus.FinishRequest; } // check if the app offline or whether an error has occurred, and report the condition context.ReportRuntimeErrorIfExists(ref status); // we do not return FinishRequest for LogRequest or EndRequest if (status == RequestNotificationStatus.FinishRequest && (context.CurrentNotification == RequestNotification.LogRequest || context.CurrentNotification == RequestNotification.EndRequest)) { status = RequestNotificationStatus.Continue; } IntPtr requestContext = wr.RequestContext; bool sendHeaders = UnsafeIISMethods.MgdIsLastNotification(requestContext, status); try { context.Response.UpdateNativeResponse(sendHeaders); } catch(Exception e) { // if we catch an exception here then // i) clear cached response body bytes on the worker request // ii) clear the managed headers, the IIS native headers, the mangaged httpwriter response buffers, and the native IIS response buffers // iii) attempt to format the exception and write it to the response wr.UnlockCachedResponseBytes(); context.AddError(e); context.ReportRuntimeErrorIfExists(ref status); try { context.Response.UpdateNativeResponse(sendHeaders); } catch { } } if (sendHeaders) { context.FinishPipelineRequest(); } // Perf optimization: dispose managed context if possible (no need to try if status is pending) if (status != RequestNotificationStatus.Pending) { PipelineRuntime.DisposeHandler(context, requestContext, status); } } internal static void FinishPipelineRequest(HttpContext context) { // Remember that first request is done _theRuntime._firstRequestCompleted = true; // need to raise OnRequestCompleted while within the ThreadContext so that things like User, CurrentCulture, etc. are available context.RaiseOnRequestCompleted(); context.Request.Dispose(); context.Response.Dispose(); HttpApplication app = context.ApplicationInstance; if(null != app) { ThreadContext threadContext = context.IndicateCompletionContext; if (threadContext != null) { if (!threadContext.HasBeenDisassociatedFromThread) { lock (threadContext) { if (!threadContext.HasBeenDisassociatedFromThread) { threadContext.DisassociateFromCurrentThread(); context.IndicateCompletionContext = null; context.InIndicateCompletion = false; } } } } app.ReleaseAppInstance(); } SetExecutionTimePerformanceCounter(context); UpdatePerfCounters(context.Response.StatusCode); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_END_HANDLER, context.WorkerRequest); // In case of a HostingInit() error, app domain should not stick around if (HostingInitFailed) { Debug.Trace("AppDomainFactory", "Shutting down appdomain because of HostingInit error"); ShutdownAppDomain(ApplicationShutdownReason.HostingEnvironment, "HostingInit error"); } } /* * Process one request */ private void ProcessRequestInternal(HttpWorkerRequest wr) { // Count active requests Interlocked.Increment(ref _activeRequestCount); if (_disposingHttpRuntime) { // Dev11 333176: An appdomain is unloaded before all requests are served, resulting in System.AppDomainUnloadedException during isapi completion callback // // HttpRuntim.Dispose could have already finished on a different thread when we had no active requests // In this case we are about to start or already started unloading the appdomain so we will reject the request the safest way possible try { wr.SendStatus(503, "Server Too Busy"); wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8"); byte[] body = Encoding.ASCII.GetBytes("Server Too Busy"); wr.SendResponseFromMemory(body, body.Length); // this will flush synchronously because of HttpRuntime.ShutdownInProgress wr.FlushResponse(true); wr.EndOfRequest(); } finally { Interlocked.Decrement(ref _activeRequestCount); } return; } // Construct the Context on HttpWorkerRequest, hook everything together HttpContext context; try { context = new HttpContext(wr, false /* initResponseWriter */); } catch { try { // If we fail to create the context for any reason, send back a 400 to make sure // the request is correctly closed (relates to VSUQFE3962) wr.SendStatus(400, "Bad Request"); wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8"); byte[] body = Encoding.ASCII.GetBytes("Bad Request"); wr.SendResponseFromMemory(body, body.Length); wr.FlushResponse(true); wr.EndOfRequest(); return; } finally { Interlocked.Decrement(ref _activeRequestCount); } } wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context); HostingEnvironment.IncrementBusyCount(); try { // First request initialization try { EnsureFirstRequestInit(context); } catch { // If we are handling a DEBUG request, ignore the FirstRequestInit exception. // This allows the HttpDebugHandler to execute, and lets the debugger attach to // the process (VSWhidbey 358135) if (!context.Request.IsDebuggingRequest) { throw; } } // Init response writer (after we have config in first request init) // no need for impersonation as it is handled in config system context.Response.InitResponseWriter(); // Get application instance IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context); if (app == null) throw new HttpException(SR.GetString(SR.Unable_create_app_object)); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start"); if (app is IHttpAsyncHandler) { // asynchronous handler IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app; context.AsyncAppHandler = asyncHandler; asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context); } else { // synchronous handler app.ProcessRequest(context); FinishRequest(context.WorkerRequest, context, null); } } catch (Exception e) { context.Response.InitResponseWriter(); FinishRequest(wr, context, e); } } private void RejectRequestInternal(HttpWorkerRequest wr, bool silent) { // Construct the Context on HttpWorkerRequest, hook everything together HttpContext context = new HttpContext(wr, false /* initResponseWriter */); wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context); // Count active requests Interlocked.Increment(ref _activeRequestCount); HostingEnvironment.IncrementBusyCount(); if (silent) { context.Response.InitResponseWriter(); FinishRequest(wr, context, null); } else { PerfCounters.IncrementGlobalCounter(GlobalPerfCounter.REQUESTS_REJECTED); PerfCounters.IncrementCounter(AppPerfCounter.APP_REQUESTS_REJECTED); try { throw new HttpException(503, SR.GetString(SR.Server_too_busy)); } catch (Exception e) { context.Response.InitResponseWriter(); FinishRequest(wr, context, e); } } } internal static void ReportAppOfflineErrorMessage(HttpResponse response, byte[] appOfflineMessage) { response.StatusCode = 503; response.ContentType = "text/html"; response.AddHeader("Retry-After", "3600"); response.OutputStream.Write(appOfflineMessage, 0, appOfflineMessage.Length); } /* * Finish processing request, sync or async */ private void FinishRequest(HttpWorkerRequest wr, HttpContext context, Exception e) { HttpResponse response = context.Response; if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_END_HANDLER, context.WorkerRequest); SetExecutionTimePerformanceCounter(context); // Flush in case of no error if (e == null) { // impersonate around PreSendHeaders / PreSendContent using (new ClientImpersonationContext(context, false)) { try { // this sends the actual content in most cases response.FinalFlushAtTheEndOfRequestProcessing(); } catch (Exception eFlush) { e = eFlush; } } } // Report error if any if (e != null) { using (new DisposableHttpContextWrapper(context)) { // if the custom encoder throws, it might interfere with returning error information // to the client, so we force use of the default encoder context.DisableCustomHttpEncoder = true; if (_appOfflineMessage != null) { try { ReportAppOfflineErrorMessage(response, _appOfflineMessage); response.FinalFlushAtTheEndOfRequestProcessing(); } catch { } } else { // when application is on UNC share the code below must // be run while impersonating the token given by IIS using (new ApplicationImpersonationContext()) { try { try { // try to report error in a way that could possibly throw (a config exception) response.ReportRuntimeError(e, true /*canThrow*/, false); } catch (Exception eReport) { // report the config error in a way that would not throw response.ReportRuntimeError(eReport, false /*canThrow*/, false); } response.FinalFlushAtTheEndOfRequestProcessing(); } catch { } } } } } // Remember that first request is done _firstRequestCompleted = true; // In case we reporting HostingInit() error, app domain should not stick around if (_hostingInitFailed) { Debug.Trace("AppDomainFactory", "Shutting down appdomain because of HostingInit error"); ShutdownAppDomain(ApplicationShutdownReason.HostingEnvironment, "HostingInit error"); } // Check status code and increment proper counter // If it's an error status code (i.e. 400 or higher), increment the proper perf counters int statusCode = response.StatusCode; UpdatePerfCounters(statusCode); context.FinishRequestForCachedPathData(statusCode); // ---- exceptions from EndOfRequest as they will prevent proper request cleanup // Since the exceptions are not expected here we want to log them try { wr.EndOfRequest(); } catch (Exception ex) { WebBaseEvent.RaiseRuntimeError(ex, this); } // Count active requests HostingEnvironment.DecrementBusyCount(); Interlocked.Decrement(ref _activeRequestCount); // Schedule more work if some requests are queued if (_requestQueue != null) _requestQueue.ScheduleMoreWorkIfNeeded(); } // // Make sure shutdown happens only once // private bool InitiateShutdownOnce() { if (_shutdownInProgress) return false; lock (this) { if (_shutdownInProgress) return false; _shutdownInProgress = true; } return true; } // // Shutdown this and restart new app domain // [PermissionSet(SecurityAction.Assert, Unrestricted = true)] private void ReleaseResourcesAndUnloadAppDomain(Object state /*not used*/) { #if DBG Debug.Trace("FileChangesMonitorIgnoreSubdirChange", "*** ReleaseResourcesAndUnloadAppDomain " + DateTime.Now.ToString("hh:mm:ss.fff", CultureInfo.InvariantCulture) + ": _appDomainAppId=" + _appDomainAppId); #endif Debug.Trace("AppDomainFactory", "ReleaseResourcesAndUnloadAppDomain, Id=" + _appDomainAppId + " DomainId = " + _appDomainId + " Stack = " + Environment.StackTrace ); try { PerfCounters.IncrementGlobalCounter(GlobalPerfCounter.APPLICATION_RESTARTS); } catch { } // Release all resources try { Dispose(); } catch { } Thread.Sleep(250); AddAppDomainTraceMessage("before Unload"); for (; ; ) { try { AppDomain.Unload(Thread.GetDomain()); } catch (CannotUnloadAppDomainException) { Debug.Assert(false); } catch (Exception e) { Debug.Trace("AppDomainFactory", "AppDomain.Unload exception: " + e + "; Id=" + _appDomainAppId); if (!BuildManagerHost.InClientBuildManager) { // Avoid calling Exception.ToString if we are in the ClientBuildManager (Dev10 bug 824659) AddAppDomainTraceMessage("Unload Exception: " + e); } throw; } } } private static void SetExecutionTimePerformanceCounter(HttpContext context) { // Set the Request Execution time perf counter TimeSpan elapsed = DateTime.UtcNow.Subtract(context.WorkerRequest.GetStartTime()); long milli = elapsed.Ticks / TimeSpan.TicksPerMillisecond; if (milli > Int32.MaxValue) milli = Int32.MaxValue; PerfCounters.SetGlobalCounter(GlobalPerfCounter.REQUEST_EXECUTION_TIME, (int)milli); PerfCounters.SetCounter(AppPerfCounter.APP_REQUEST_EXEC_TIME, (int)milli); } private static void UpdatePerfCounters(int statusCode) { if (400 <= statusCode) { PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED); switch (statusCode) { case 401: // Not authorized PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_AUTHORIZED); break; case 404: // Not found case 414: // Not found PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND); break; } } else { // If status code is not in the 400-599 range (i.e. 200-299 success or 300-399 redirection), // count it as a successful request. PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_SUCCEDED); } } private void WaitForRequestsToFinish(int waitTimeoutMs) { DateTime waitLimit = DateTime.UtcNow.AddMilliseconds(waitTimeoutMs); for (; ; ) { if (_activeRequestCount == 0 && (_requestQueue == null || _requestQueue.IsEmpty)) break; Thread.Sleep(250); // only apply timeout if a managed debugger is not attached if (!System.Diagnostics.Debugger.IsAttached && DateTime.UtcNow > waitLimit) { break; // give it up } } } /* * Cleanup of all unmananged state */ private void Dispose() { // get shutdown timeout from config int drainTimeoutSec = HttpRuntimeSection.DefaultShutdownTimeout; try { HttpRuntimeSection runtimeConfig = RuntimeConfig.GetAppLKGConfig().HttpRuntime; if (runtimeConfig != null) { drainTimeoutSec = (int)runtimeConfig.ShutdownTimeout.TotalSeconds; } // before aborting compilation give time to drain (new requests are no longer coming at this point) WaitForRequestsToFinish(drainTimeoutSec * 1000); // reject remaining queued requests if (_requestQueue != null) _requestQueue.Drain(); } finally { // By this time all new requests should be directed to a newly created app domain // But there might be requests that got dispatched to this old app domain but have not reached ProcessRequestInternal yet // Signal ProcessRequestInternal to reject them immediately without initiating async operations _disposingHttpRuntime = true; } // give it a little more time to drain WaitForRequestsToFinish((drainTimeoutSec * 1000) / 6); // wait for pending async io to complete, prior to aborting requests // this isn't necessary for IIS 7, where the async sends are always done // from native code with native buffers System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.WaitForPendingAsyncIo(); // For IIS7 integrated pipeline, wait until GL_APPLICATION_STOP fires and // there are no active calls to IndicateCompletion before unloading the AppDomain if (HttpRuntime.UseIntegratedPipeline) { PipelineRuntime.WaitForRequestsToDrain(); } else { // wait for all active requests to complete while (_activeRequestCount != 0) { Thread.Sleep(250); } } // Dispose AppDomainShutdownTimer DisposeAppDomainShutdownTimer(); // kill all remaining requests (and the timeout timer) _timeoutManager.Stop(); AppDomainResourcePerfCounters.Stop(); #if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features // double check for pending async io System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6.WaitForPendingAsyncIo(); // stop sqlcachedependency polling SqlCacheDependencyManager.Dispose((drainTimeoutSec * 1000) / 2); #endif // !FEATURE_PAL // cleanup cache (this ends all sessions) HealthMonitoringManager.IsCacheDisposed = true; // HMM is the only place internally where we care if the Cache is disposed or not. if (_cachePublic != null) { var oCache = HttpRuntime.Cache.GetObjectCache(createIfDoesNotExist: false); var iCache = HttpRuntime.Cache.GetInternalCache(createIfDoesNotExist: false); if (oCache != null) { oCache.Dispose(); } if (iCache != null) { iCache.Dispose(); } } // app on end, cleanup app instances HttpApplicationFactory.EndApplication(); // call app_onEnd // stop file changes monitor _fcm.Stop(); // stop health monitoring timer HealthMonitoringManager.Shutdown(); } /* * Async completion of IIS7 pipeline (unlike OnHandlerCompletion, this may fire more than once). */ private void OnRequestNotificationCompletion(IAsyncResult ar) { try { OnRequestNotificationCompletionHelper(ar); } catch(Exception e) { ApplicationManager.RecordFatalException(e); throw; } } private void OnRequestNotificationCompletionHelper(IAsyncResult ar) { if (ar.CompletedSynchronously) { Debug.Trace("PipelineRuntime", "OnRequestNotificationCompletion: completed synchronously"); return; } Debug.Trace("PipelineRuntime", "OnRequestNotificationCompletion: completed asynchronously"); RequestNotificationStatus status = RequestNotificationStatus.Continue; HttpContext context = (HttpContext) ar.AsyncState; IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest; try { context.ApplicationInstance.EndProcessRequestNotification(ar); } catch (Exception e) { status = RequestNotificationStatus.FinishRequest; context.AddError(e); } // RequestContext is set to null if this is the last notification, so we need to save it // for the call to PostCompletion IntPtr requestContext = wr.RequestContext; FinishRequestNotification(wr, context, ref status); // set the notification context to null since we are exiting this notification context.NotificationContext = null; // Indicate completion to IIS, so that it can resume // request processing on an IIS thread Debug.Trace("PipelineRuntime", "OnRequestNotificationCompletion(" + status + ")"); int result = UnsafeIISMethods.MgdPostCompletion(requestContext, status); Misc.ThrowIfFailedHr(result); } /* * Async completion of managed pipeline (called at most one time). */ private void OnHandlerCompletion(IAsyncResult ar) { HttpContext context = (HttpContext)ar.AsyncState; try { context.AsyncAppHandler.EndProcessRequest(ar); } catch (Exception e) { context.AddError(e); } finally { // no longer keep AsyncAppHandler poiting to the application // is only needed to call EndProcessRequest context.AsyncAppHandler = null; } FinishRequest(context.WorkerRequest, context, context.Error); } /* * Notification from worker request that it is done writing from buffer * so that the buffers can be recycled */ private void EndOfSendCallback(HttpWorkerRequest wr, Object arg) { Debug.Trace("PipelineRuntime", "HttpRuntime.EndOfSendCallback"); HttpContext context = (HttpContext)arg; context.Request.Dispose(); context.Response.Dispose(); } /* * Notification when something in the bin directory changed */ private void OnCriticalDirectoryChange(Object sender, FileChangeEvent e) { // shutdown the app domain Debug.Trace("AppDomainFactory", "Shutting down appdomain because of bin dir change or directory rename." + " FileName=" + e.FileName + " Action=" + e.Action); ApplicationShutdownReason reason = ApplicationShutdownReason.None; string directoryName = new DirectoryInfo(e.FileName).Name; string message = FileChangesMonitor.GenerateErrorMessage(e.Action); message = (message != null) ? message + directoryName : directoryName + " dir change or directory rename"; if (StringUtil.EqualsIgnoreCase(directoryName, CodeDirectoryName)) { reason = ApplicationShutdownReason.CodeDirChangeOrDirectoryRename; } else if (StringUtil.EqualsIgnoreCase(directoryName, ResourcesDirectoryName)) { reason = ApplicationShutdownReason.ResourcesDirChangeOrDirectoryRename; } else if (StringUtil.EqualsIgnoreCase(directoryName, BrowsersDirectoryName)) { reason = ApplicationShutdownReason.BrowsersDirChangeOrDirectoryRename; } else if (StringUtil.EqualsIgnoreCase(directoryName, BinDirectoryName)) { reason = ApplicationShutdownReason.BinDirChangeOrDirectoryRename; } if (e.Action == FileAction.Added) { // Make sure HttpRuntime does not ignore the appdomain shutdown if a file is added (VSWhidbey 363481) HttpRuntime.SetUserForcedShutdown(); Debug.Trace("AppDomainFactorySpecial", "Call SetUserForcedShutdown: FileName=" + e.FileName + "; now=" + DateTime.Now); } ShutdownAppDomain(reason, message); } /** * Coalesce file change notifications to minimize sharing violations and AppDomain restarts (ASURT 147492) */ internal static void CoalesceNotifications() { int waitChangeNotification = HttpRuntimeSection.DefaultWaitChangeNotification; int maxWaitChangeNotification = HttpRuntimeSection.DefaultMaxWaitChangeNotification; try { HttpRuntimeSection config = RuntimeConfig.GetAppLKGConfig().HttpRuntime; if (config != null) { waitChangeNotification = config.WaitChangeNotification; maxWaitChangeNotification = config.MaxWaitChangeNotification; } } catch { } if (waitChangeNotification == 0 || maxWaitChangeNotification == 0) return; DateTime maxWait = DateTime.UtcNow.AddSeconds(maxWaitChangeNotification); // Coalesce file change notifications try { while (DateTime.UtcNow < maxWait) { if (DateTime.UtcNow > _theRuntime.LastShutdownAttemptTime.AddSeconds(waitChangeNotification)) break; Thread.Sleep(250); } } catch { } } // appdomain shutdown eventhandler internal static event BuildManagerHostUnloadEventHandler AppDomainShutdown; internal static void OnAppDomainShutdown(BuildManagerHostUnloadEventArgs e) { if (AppDomainShutdown != null) { AppDomainShutdown(_theRuntime, e); } } internal static void SetUserForcedShutdown() { _theRuntime._userForcedShutdown = true; } /* * Shutdown the current app domain */ internal static bool ShutdownAppDomain(ApplicationShutdownReason reason, string message) { return ShutdownAppDomainWithStackTrace(reason, message, null /*stackTrace*/); } /* * Shutdown the current app domain with a stack trace. This is useful for callers that are running * on a QUWI callback, and wouldn't provide a meaningful stack trace by default. */ internal static bool ShutdownAppDomainWithStackTrace(ApplicationShutdownReason reason, string message, string stackTrace) { SetShutdownReason(reason, message); return ShutdownAppDomain(stackTrace); } private static bool ShutdownAppDomain(string stackTrace) { #if DBG Debug.Trace("FileChangesMonitorIgnoreSubdirChange", "*** ShutdownAppDomain " + DateTime.Now.ToString("hh:mm:ss.fff", CultureInfo.InvariantCulture) + ": _appDomainAppId=" + HttpRuntime.AppDomainAppId); #endif // Ignore notifications during the processing of the first request (ASURT 100335) // skip this if LastShutdownAttemptTime has been set if (_theRuntime.LastShutdownAttemptTime == DateTime.MinValue && !_theRuntime._firstRequestCompleted && !_theRuntime._userForcedShutdown) { // check the timeout (don't disable notifications forever int delayTimeoutSec = HttpRuntimeSection.DefaultDelayNotificationTimeout; try { RuntimeConfig runtimeConfig = RuntimeConfig.GetAppLKGConfig(); if (runtimeConfig != null) { HttpRuntimeSection runtimeSection = runtimeConfig.HttpRuntime; if (runtimeSection != null) { delayTimeoutSec = (int)runtimeSection.DelayNotificationTimeout.TotalSeconds; if (DateTime.UtcNow < _theRuntime._firstRequestStartTime.AddSeconds(delayTimeoutSec)) { Debug.Trace("AppDomainFactory", "ShutdownAppDomain IGNORED (1st request is not done yet), Id = " + AppDomainAppId); return false; } } } } catch { } } try { _theRuntime.RaiseShutdownWebEventOnce(); } catch { // VSWhidbey 444472: if an exception is thrown, we consume it and continue executing the following code. } // Update last time ShutdownAppDomain was called _theRuntime.LastShutdownAttemptTime = DateTime.UtcNow; if (!HostingEnvironment.ShutdownInitiated) { // This shutdown is not triggered by hosting environment - let it do the job HostingEnvironment.InitiateShutdownWithoutDemand(); return true; } //WOS 1400290: CantUnloadAppDomainException in ISAPI mode, wait until HostingEnvironment.ShutdownThisAppDomainOnce completes if (HostingEnvironment.ShutdownInProgress) { return false; } // Make sure we don't go through shutdown logic many times if (!_theRuntime.InitiateShutdownOnce()) return false; Debug.Trace("AppDomainFactory", "ShutdownAppDomain, Id = " + AppDomainAppId + ", ShutdownInProgress=" + ShutdownInProgress + ", ShutdownMessage=" + _theRuntime._shutDownMessage); if (String.IsNullOrEmpty(stackTrace) && !BuildManagerHost.InClientBuildManager) { // Avoid calling Environment.StackTrace if we are in the ClientBuildManager (Dev10 bug 824659) // Instrument to be able to see what's causing a shutdown new EnvironmentPermission(PermissionState.Unrestricted).Assert(); try { _theRuntime._shutDownStack = Environment.StackTrace; } finally { CodeAccessPermission.RevertAssert(); } } else { _theRuntime._shutDownStack = stackTrace; } // Notify when appdomain is about to shutdown. OnAppDomainShutdown(new BuildManagerHostUnloadEventArgs(_theRuntime._shutdownReason)); // unload app domain from another CLR thread ThreadPool.QueueUserWorkItem(_theRuntime._appDomainUnloadallback); return true; } internal static void RecoverFromUnexceptedAppDomainUnload() { if (_theRuntime._shutdownInProgress) return; // someone unloaded app domain directly - tell unmanaged code Debug.Trace("AppDomainFactory", "Unexpected AppDomainUnload"); _theRuntime._shutdownInProgress = true; // tell unmanaged code not to dispatch requests to this app domain try { ISAPIRuntime.RemoveThisAppDomainFromUnmanagedTable(); PipelineRuntime.RemoveThisAppDomainFromUnmanagedTable(); AddAppDomainTraceMessage("AppDomainRestart"); } finally { // release all resources _theRuntime.Dispose(); } } /* * Notification when app-level Config changed */ internal static void OnConfigChange(String message) { Debug.Trace("AppDomainFactory", "Shutting down appdomain because of config change"); ShutdownAppDomain(ApplicationShutdownReason.ConfigurationChange, (message != null) ? message : "CONFIG change"); } // Intrumentation to remember the overwhelming file change internal static void SetShutdownReason(ApplicationShutdownReason reason, String message) { if (_theRuntime._shutdownReason == ApplicationShutdownReason.None) { _theRuntime._shutdownReason = reason; } SetShutdownMessage(message); } internal static void SetShutdownMessage(String message) { if (message != null) { if (_theRuntime._shutDownMessage == null) _theRuntime._shutDownMessage = message; else _theRuntime._shutDownMessage += "\r\n" + message; } } // public method is on HostingEnvironment internal static ApplicationShutdownReason ShutdownReason { get { return _theRuntime._shutdownReason; } } // // public static APIs // /* * Process one request */ /// /// The method that drives /// all ASP.NET web processing execution. /// [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] public static void ProcessRequest(HttpWorkerRequest wr) { if (wr == null) throw new ArgumentNullException("wr"); if (HttpRuntime.UseIntegratedPipeline) { throw new PlatformNotSupportedException(SR.GetString(SR.Method_Not_Supported_By_Iis_Integrated_Mode, "HttpRuntime.ProcessRequest")); } ProcessRequestNoDemand(wr); } internal static void ProcessRequestNoDemand(HttpWorkerRequest wr) { RequestQueue rq = _theRuntime._requestQueue; wr.UpdateInitialCounters(); if (rq != null) // could be null before first request wr = rq.GetRequestToExecute(wr); if (wr != null) { CalculateWaitTimeAndUpdatePerfCounter(wr); wr.ResetStartTime(); ProcessRequestNow(wr); } } private static void CalculateWaitTimeAndUpdatePerfCounter(HttpWorkerRequest wr) { DateTime begin = wr.GetStartTime(); TimeSpan elapsed = DateTime.UtcNow.Subtract(begin); long milli = elapsed.Ticks / TimeSpan.TicksPerMillisecond; if (milli > Int32.MaxValue) milli = Int32.MaxValue; PerfCounters.SetGlobalCounter(GlobalPerfCounter.REQUEST_WAIT_TIME, (int)milli); PerfCounters.SetCounter(AppPerfCounter.APP_REQUEST_WAIT_TIME, (int)milli); } internal static void ProcessRequestNow(HttpWorkerRequest wr) { _theRuntime.ProcessRequestInternal(wr); } internal static void RejectRequestNow(HttpWorkerRequest wr, bool silent) { _theRuntime.RejectRequestInternal(wr, silent); } /// /// Removes all items from the cache and shuts down the runtime. /// [SecurityPermission(SecurityAction.Demand, Unrestricted = true)] public static void Close() { Debug.Trace("AppDomainFactory", "HttpRuntime.Close, ShutdownInProgress=" + ShutdownInProgress); if (_theRuntime.InitiateShutdownOnce()) { SetShutdownReason(ApplicationShutdownReason.HttpRuntimeClose, "HttpRuntime.Close is called"); if (HostingEnvironment.IsHosted) { // go throw initiate shutdown for hosted scenarios HostingEnvironment.InitiateShutdownWithoutDemand(); } else { _theRuntime.Dispose(); } } } /// /// Unloads the current app domain. /// public static void UnloadAppDomain() { _theRuntime._userForcedShutdown = true; ShutdownAppDomain(ApplicationShutdownReason.UnloadAppDomainCalled, "User code called UnloadAppDomain"); } private DateTime LastShutdownAttemptTime { get { DateTime dt; lock (this) { dt = _lastShutdownAttemptTime; } return dt; } set { lock (this) { _lastShutdownAttemptTime = value; } } } internal static Profiler Profile { get { return _theRuntime._profiler; } } internal static bool IsTrustLevelInitialized { get { return !HostingEnvironment.IsHosted || TrustLevel != null; } } internal static NamedPermissionSet NamedPermissionSet { get { // Make sure we have already initialized the trust level // return _theRuntime._namedPermissionSet; } } internal static PolicyLevel PolicyLevel { get { return _theRuntime._policyLevel; } } internal static string HostSecurityPolicyResolverType { get { return _theRuntime._hostSecurityPolicyResolverType; } } [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Unrestricted)] public static NamedPermissionSet GetNamedPermissionSet() { NamedPermissionSet namedPermissionSet = _theRuntime._namedPermissionSet; if (namedPermissionSet == null) { return null; } else { return new NamedPermissionSet(namedPermissionSet); } } internal static bool IsFullTrust { get { // Make sure we have already initialized the trust level Debug.Assert(IsTrustLevelInitialized); return (_theRuntime._namedPermissionSet == null); } } /* * Check that the current trust level allows access to a virtual path. Throw if it doesn't, */ internal static void CheckVirtualFilePermission(string virtualPath) { string physicalPath = HostingEnvironment.MapPath(virtualPath); CheckFilePermission(physicalPath); } /* * Check that the current trust level allows access to a path. Throw if it doesn't, */ internal static void CheckFilePermission(string path) { CheckFilePermission(path, false); } internal static void CheckFilePermission(string path, bool writePermissions) { if (!HasFilePermission(path, writePermissions)) { throw new HttpException(SR.GetString(SR.Access_denied_to_path, GetSafePath(path))); } } internal static bool HasFilePermission(string path) { return HasFilePermission(path, false); } internal static bool HasFilePermission(string path, bool writePermissions) { // WOS #1523618: need to skip this check for HttpResponse.ReportRuntimeError when reporting an // InitializationException (e.g., necessary to display line info for ConfigurationException). if (TrustLevel == null && InitializationException != null) { return true; } // Make sure we have already initialized the trust level Debug.Assert(TrustLevel != null || !HostingEnvironment.IsHosted, "TrustLevel != null || !HostingEnvironment.IsHosted"); // If we don't have a NamedPermissionSet, we're in full trust if (NamedPermissionSet == null) return true; bool fAccess = false; // Check that the user has permission to the path IPermission allowedPermission = NamedPermissionSet.GetPermission(typeof(FileIOPermission)); if (allowedPermission != null) { IPermission askedPermission = null; try { if (!writePermissions) askedPermission = new FileIOPermission(FileIOPermissionAccess.Read, path); else askedPermission = new FileIOPermission(FileIOPermissionAccess.AllAccess, path); } catch { // This could happen if the path is not absolute return false; } fAccess = askedPermission.IsSubsetOf(allowedPermission); } return fAccess; } internal static bool HasWebPermission(Uri uri) { // Make sure we have already initialized the trust level Debug.Assert(TrustLevel != null || !HostingEnvironment.IsHosted); // If we don't have a NamedPermissionSet, we're in full trust if (NamedPermissionSet == null) return true; bool fAccess = false; // Check that the user has permission to the URI IPermission allowedPermission = NamedPermissionSet.GetPermission(typeof(WebPermission)); if (allowedPermission != null) { IPermission askedPermission = null; try { askedPermission = new WebPermission(NetworkAccess.Connect, uri.ToString()); } catch { return false; } fAccess = askedPermission.IsSubsetOf(allowedPermission); } return fAccess; } internal static bool HasDbPermission(DbProviderFactory factory) { // Make sure we have already initialized the trust level Debug.Assert(TrustLevel != null || !HostingEnvironment.IsHosted); // If we don't have a NamedPermissionSet, we're in full trust if (NamedPermissionSet == null) return true; bool fAccess = false; // Check that the user has permission to the provider CodeAccessPermission askedPermission = factory.CreatePermission(PermissionState.Unrestricted); if (askedPermission != null) { IPermission allowedPermission = NamedPermissionSet.GetPermission(askedPermission.GetType()); if (allowedPermission != null) { fAccess = askedPermission.IsSubsetOf(allowedPermission); } } return fAccess; } internal static bool HasPathDiscoveryPermission(string path) { // WOS #1523618: need to skip this check for HttpResponse.ReportRuntimeError when reporting an // InitializationException (e.g., necessary to display line info for ConfigurationException). if (TrustLevel == null && InitializationException != null) { return true; } // Make sure we have already initialized the trust level Debug.Assert(TrustLevel != null || !HostingEnvironment.IsHosted); // If we don't have a NamedPermissionSet, we're in full trust if (NamedPermissionSet == null) return true; bool fAccess = false; // Check that the user has permission to the path IPermission allowedPermission = NamedPermissionSet.GetPermission(typeof(FileIOPermission)); if (allowedPermission != null) { IPermission askedPermission = new FileIOPermission(FileIOPermissionAccess.PathDiscovery, path); fAccess = askedPermission.IsSubsetOf(allowedPermission); } return fAccess; } internal static bool HasAppPathDiscoveryPermission() { return HasPathDiscoveryPermission(HttpRuntime.AppDomainAppPathInternal); } internal static string GetSafePath(string path) { if (String.IsNullOrEmpty(path)) return path; try { if (HasPathDiscoveryPermission(path)) // could throw on bad filenames return path; } catch { } return Path.GetFileName(path); } /* * Check that the current trust level allows Unmanaged access */ internal static bool HasUnmanagedPermission() { // Make sure we have already initialized the trust level Debug.Assert(TrustLevel != null || !HostingEnvironment.IsHosted); // If we don't have a NamedPermissionSet, we're in full trust if (NamedPermissionSet == null) return true; SecurityPermission securityPermission = (SecurityPermission)NamedPermissionSet.GetPermission( typeof(SecurityPermission)); if (securityPermission == null) return false; return (securityPermission.Flags & SecurityPermissionFlag.UnmanagedCode) != 0; } internal static bool HasAspNetHostingPermission(AspNetHostingPermissionLevel level) { // Make sure we have already initialized the trust level // // If we don't have a NamedPermissionSet, we're in full trust if (NamedPermissionSet == null) return true; AspNetHostingPermission permission = (AspNetHostingPermission)NamedPermissionSet.GetPermission( typeof(AspNetHostingPermission)); if (permission == null) return false; return (permission.Level >= level); } internal static void CheckAspNetHostingPermission(AspNetHostingPermissionLevel level, String errorMessageId) { if (!HasAspNetHostingPermission(level)) { throw new HttpException(SR.GetString(errorMessageId)); } } // If we're not in full trust, fail if the passed in type doesn't have the APTCA bit internal static void FailIfNoAPTCABit(Type t, ElementInformation elemInfo, string propertyName) { if (!IsTypeAllowedInConfig(t)) { if (null != elemInfo) { PropertyInformation propInfo = elemInfo.Properties[propertyName]; throw new ConfigurationErrorsException(SR.GetString(SR.Type_from_untrusted_assembly, t.FullName), propInfo.Source, propInfo.LineNumber); } else { throw new ConfigurationErrorsException(SR.GetString(SR.Type_from_untrusted_assembly, t.FullName)); } } } // If we're not in full trust, fail if the passed in type doesn't have the APTCA bit internal static void FailIfNoAPTCABit(Type t, XmlNode node) { if (!IsTypeAllowedInConfig(t)) { throw new ConfigurationErrorsException(SR.GetString(SR.Type_from_untrusted_assembly, t.FullName), node); } } private static bool HasAPTCABit(Assembly assembly) { return assembly.IsDefined(typeof(AllowPartiallyTrustedCallersAttribute), inherit: false); } // Check if the type is allowed to be used in config by checking the APTCA bit internal static bool IsTypeAllowedInConfig(Type t) { // Allow everything in full trust if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted)) return true; return IsTypeAccessibleFromPartialTrust(t); } internal static bool IsTypeAccessibleFromPartialTrust(Type t) { Assembly assembly = t.Assembly; if (assembly.SecurityRuleSet == SecurityRuleSet.Level1) { // Level 1 CAS uses transparency as an auditing mechanism rather than an enforcement mechanism, so we can't // perform a transparency check. Instead, allow the call to go through if: // (a) the referenced assembly is partially trusted, hence it cannot do anything dangerous; or // (b) the assembly is fully trusted and has APTCA. return (!assembly.IsFullyTrusted || HasAPTCABit(assembly)); } else { // ** TEMPORARY ** // Some GACed assemblies register critical modules / handlers. We can't break these scenarios for .NET 4.5, but we should // remove this APTCA check when we fix DevDiv #85358 and use only the transparency check defined below. if (HasAPTCABit(assembly)) { return true; } // ** END TEMPORARY ** // Level 2 CAS uses transparency as an enforcement mechanism, so we can perform a transparency check. // Transparent and SafeCritical types are safe to use from partial trust code. return (t.IsSecurityTransparent || t.IsSecuritySafeCritical); } } internal static FileChangesMonitor FileChangesMonitor { get { return _theRuntime._fcm; } } internal static RequestTimeoutManager RequestTimeoutManager { get { return _theRuntime._timeoutManager; } } /// /// Provides access to the cache. /// public static Cache Cache { get { if (HttpRuntime.AspInstallDirectoryInternal == null) { throw new HttpException(SR.GetString(SR.Aspnet_not_installed, VersionInfo.SystemWebVersion)); } Cache cachePublic = _theRuntime._cachePublic; if (cachePublic == null) { lock (_theRuntime) { cachePublic = _theRuntime._cachePublic; if (cachePublic == null) { // Create the CACHE object cachePublic = new Caching.Cache(0); _theRuntime._cachePublic = cachePublic; } } } return cachePublic; } } /// /// [To be supplied.] /// public static string AspInstallDirectory { get { String path = AspInstallDirectoryInternal; if (path == null) { throw new HttpException(SR.GetString(SR.Aspnet_not_installed, VersionInfo.SystemWebVersion)); } InternalSecurityPermissions.PathDiscovery(path).Demand(); return path; } } internal static string AspInstallDirectoryInternal { get { return s_installDirectory; } } // // Return the client script virtual path, e.g. "/aspnet_client/system_web/2_0_50217" // public static string AspClientScriptVirtualPath { get { if (_theRuntime._clientScriptVirtualPath == null) { string aspNetVersion = VersionInfo.SystemWebVersion; string clientScriptVirtualPath = AspNetClientFilesParentVirtualPath + aspNetVersion.Substring(0, aspNetVersion.LastIndexOf('.')).Replace('.', '_'); _theRuntime._clientScriptVirtualPath = clientScriptVirtualPath; } return _theRuntime._clientScriptVirtualPath; } } public static string AspClientScriptPhysicalPath { get { String path = AspClientScriptPhysicalPathInternal; if (path == null) { throw new HttpException(SR.GetString(SR.Aspnet_not_installed, VersionInfo.SystemWebVersion)); } return path; } } // // Return the client script physical path, e.g. @"c:\windows\microsoft.net\framework\v2.0.50217.0\asp.netclientfiles" // internal static string AspClientScriptPhysicalPathInternal { get { if (_theRuntime._clientScriptPhysicalPath == null) { string clientScriptPhysicalPath = System.IO.Path.Combine(AspInstallDirectoryInternal, AspNetClientFilesSubDirectory); _theRuntime._clientScriptPhysicalPath = clientScriptPhysicalPath; } return _theRuntime._clientScriptPhysicalPath; } } /// /// [To be supplied.] /// public static string ClrInstallDirectory { get { String path = ClrInstallDirectoryInternal; InternalSecurityPermissions.PathDiscovery(path).Demand(); return path; } } internal static string ClrInstallDirectoryInternal { get { return HttpConfigurationSystem.MsCorLibDirectory; } } /// /// [To be supplied.] /// public static string MachineConfigurationDirectory { get { String path = MachineConfigurationDirectoryInternal; InternalSecurityPermissions.PathDiscovery(path).Demand(); return path; } } internal static string MachineConfigurationDirectoryInternal { get { return HttpConfigurationSystem.MachineConfigurationDirectory; } } internal static bool IsEngineLoaded { get { return s_isEngineLoaded; } } // // Static app domain related properties // /// /// [To be supplied.] /// public static String CodegenDir { get { String path = CodegenDirInternal; InternalSecurityPermissions.PathDiscovery(path).Demand(); return path; } } internal static string CodegenDirInternal { get { return _theRuntime._codegenDir; } } internal static string TempDirInternal { get { return _theRuntime._tempDir; } } /// /// [To be supplied.] /// public static String AppDomainAppId { get { return _theRuntime._appDomainAppId; } } internal static bool IsAspNetAppDomain { get { return AppDomainAppId != null; } } /// /// [To be supplied.] /// public static String AppDomainAppPath { get { InternalSecurityPermissions.AppPathDiscovery.Demand(); return AppDomainAppPathInternal; } } internal static string AppDomainAppPathInternal { get { return _theRuntime._appDomainAppPath; } } /// /// [To be supplied.] /// public static String AppDomainAppVirtualPath { get { return VirtualPath.GetVirtualPathStringNoTrailingSlash(_theRuntime._appDomainAppVPath); } } // Save as AppDomainAppVirtualPath, but includes the trailng slash. We can't change // AppDomainAppVirtualPath since it's public. internal static String AppDomainAppVirtualPathString { get { return VirtualPath.GetVirtualPathString(_theRuntime._appDomainAppVPath); } } internal static VirtualPath AppDomainAppVirtualPathObject { get { return _theRuntime._appDomainAppVPath; } } internal static bool IsPathWithinAppRoot(String path) { if (AppDomainIdInternal == null) return true; // app domain not initialized return UrlPath.IsEqualOrSubpath(AppDomainAppVirtualPathString, path); } /// /// [To be supplied.] /// public static String AppDomainId { [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)] get { return AppDomainIdInternal; } } internal static string AppDomainIdInternal { get { return _theRuntime._appDomainId; } } /// /// [To be supplied.] /// public static String BinDirectory { get { String path = BinDirectoryInternal; InternalSecurityPermissions.PathDiscovery(path).Demand(); return path; } } internal static string BinDirectoryInternal { get { return Path.Combine(_theRuntime._appDomainAppPath, BinDirectoryName) + Path.DirectorySeparatorChar; } } internal static VirtualPath CodeDirectoryVirtualPath { get { return _theRuntime._appDomainAppVPath.SimpleCombineWithDir(CodeDirectoryName); } } internal static VirtualPath ResourcesDirectoryVirtualPath { get { return _theRuntime._appDomainAppVPath.SimpleCombineWithDir(ResourcesDirectoryName); } } internal static VirtualPath WebRefDirectoryVirtualPath { get { return _theRuntime._appDomainAppVPath.SimpleCombineWithDir(WebRefDirectoryName); } } /// /// [To be supplied.] /// public static bool IsOnUNCShare { [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)] get { return IsOnUNCShareInternal; } } internal static bool IsOnUNCShareInternal { get { return _theRuntime._isOnUNCShare; } } // // Static helper to retrieve app domain values // private static String GetAppDomainString(String key) { Object x = Thread.GetDomain().GetData(key); return x as String; } internal static void AddAppDomainTraceMessage(String message) { const String appDomainTraceKey = "ASP.NET Domain Trace"; AppDomain d = Thread.GetDomain(); String m = d.GetData(appDomainTraceKey) as String; d.SetData(appDomainTraceKey, (m != null) ? m + " ... " + message : message); } // Gets the version of the ASP.NET framework the current web applications is targeting. // This property is normally set via the element's "targetFramework" // attribute. The property is not guaranteed to return a correct value if the current // AppDomain is not an ASP.NET web application AppDomain. public static Version TargetFramework { get { return BinaryCompatibility.Current.TargetFramework; } } // // Flags // internal static bool DebuggingEnabled { get { return _theRuntime._debuggingEnabled; } } internal static bool ConfigInited { get { return _theRuntime._configInited; } } internal static bool FusionInited { get { return _theRuntime._fusionInited; } } internal static bool ApartmentThreading { get { return _theRuntime._apartmentThreading; } } internal static bool ShutdownInProgress { get { return _theRuntime._shutdownInProgress; } } internal static string TrustLevel { get { return _theRuntime._trustLevel; } } internal static string WpUserId { get { return _theRuntime._wpUserId; } } private void SetTrustLevel(TrustSection trustSection, SecurityPolicySection securityPolicySection) { // Use a temporary variable, since we use the field as a signal that the trust has really // been set, which is not the case until later in this method. string trustLevel = trustSection.Level; if (trustSection.Level == "Full") { _trustLevel = trustLevel; return; } if (securityPolicySection == null || securityPolicySection.TrustLevels[trustSection.Level] == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level), String.Empty, 0); // Do not give out configuration information since we don't know what trust level we are // supposed to be running at. If the information below is added to the error it might expose // part of the config file that the users does not have permissions to see. VS261145 // ,trustSection.ElementInformation.Properties["level"].Source, // trustSection.ElementInformation.Properties["level"].LineNumber); } String file = null; if (trustSection.Level == "Minimal" || trustSection.Level == "Low" || trustSection.Level == "Medium" || trustSection.Level == "High") { file = (String)securityPolicySection.TrustLevels[trustSection.Level].LegacyPolicyFileExpanded; } else { file = (String)securityPolicySection.TrustLevels[trustSection.Level].PolicyFileExpanded; } if (file == null || !FileUtil.FileExists(file)) { //if HttpContext.Current.IsCustomErrorEnabled throw new HttpException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level)); //else // throw new ConfigurationErrorsException(SR.GetString(SR.Unable_to_get_policy_file, trustSection.Level), // trustSection.Filename, trustSection.LineNumber); } bool foundGacToken = false; #pragma warning disable 618 PolicyLevel policyLevel = CreatePolicyLevel(file, AppDomainAppPathInternal, CodegenDirInternal, trustSection.OriginUrl, out foundGacToken); // see if the policy file contained a v1.x UrlMembershipCondition containing // a GAC token. If so, let's upgrade it by adding a code group granting // full trust to code from the GAC if (foundGacToken) { // walk the code groups at the app domain level and look for one that grants // access to the GAC with an UrlMembershipCondition. CodeGroup rootGroup = policyLevel.RootCodeGroup; bool foundGacCondition = false; foreach (CodeGroup childGroup in rootGroup.Children) { if (childGroup.MembershipCondition is GacMembershipCondition) { foundGacCondition = true; // if we found the GAC token and also have the GacMembershipCondition // the policy file needs to be upgraded to just include the GacMembershipCondition Debug.Assert(!foundGacCondition); break; } } // add one as a child of the toplevel group after // some sanity checking to make sure it's an ASP.NET policy file // which always begins with a FirstMatchCodeGroup granting nothing // this might not upgrade some custom policy files if (!foundGacCondition) { if (rootGroup is FirstMatchCodeGroup) { FirstMatchCodeGroup firstMatch = (FirstMatchCodeGroup)rootGroup; if (firstMatch.MembershipCondition is AllMembershipCondition && firstMatch.PermissionSetName == "Nothing") { PermissionSet fullTrust = new PermissionSet(PermissionState.Unrestricted); CodeGroup gacGroup = new UnionCodeGroup(new GacMembershipCondition(), new PolicyStatement(fullTrust)); // now, walk the current groups and insert our new group // immediately before the old Gac group // we'll need to use heuristics for this: // it will be an UrlMembershipCondition group with full trust CodeGroup newRoot = new FirstMatchCodeGroup(rootGroup.MembershipCondition, rootGroup.PolicyStatement); foreach (CodeGroup childGroup in rootGroup.Children) { // is this the target old $Gac$ group? // insert our new GacMembershipCondition group ahead of it if ((childGroup is UnionCodeGroup) && (childGroup.MembershipCondition is UrlMembershipCondition) && childGroup.PolicyStatement.PermissionSet.IsUnrestricted()) { if (null != gacGroup) { newRoot.AddChild(gacGroup); gacGroup = null; } } // append this group to the root group // AddChild itself does a deep Copy to get any // child groups so we don't need one here newRoot.AddChild(childGroup); } policyLevel.RootCodeGroup = newRoot; //Debug.Trace("internal", "PolicyLevel: " + policyLevel.ToXml()); } } } #pragma warning restore 618 } #pragma warning disable 618 AppDomain.CurrentDomain.SetAppDomainPolicy(policyLevel); _namedPermissionSet = policyLevel.GetNamedPermissionSet(trustSection.PermissionSetName); #pragma warning restore 618 _trustLevel = trustLevel; _fcm.StartMonitoringFile(file, new FileChangeEventHandler(this.OnSecurityPolicyFileChange)); } #pragma warning disable 618 private static PolicyLevel CreatePolicyLevel(String configFile, String appDir, String binDir, String strOriginUrl, out bool foundGacToken) { // Read in the config file to a string. FileStream file = new FileStream(configFile, FileMode.Open, FileAccess.Read); StreamReader reader = new StreamReader(file, Encoding.UTF8); String strFileData = reader.ReadToEnd(); reader.Close(); appDir = FileUtil.RemoveTrailingDirectoryBackSlash(appDir); binDir = FileUtil.RemoveTrailingDirectoryBackSlash(binDir); strFileData = strFileData.Replace("$AppDir$", appDir); strFileData = strFileData.Replace("$AppDirUrl$", MakeFileUrl(appDir)); strFileData = strFileData.Replace("$CodeGen$", MakeFileUrl(binDir)); if (strOriginUrl == null) strOriginUrl = String.Empty; strFileData = strFileData.Replace("$OriginHost$", strOriginUrl); // see if the file contains a GAC token // if so, do the replacement and record the // fact so that we later add a GacMembershipCondition // codegroup to the PolicyLevel int ndx = strFileData.IndexOf("$Gac$", StringComparison.Ordinal); if (ndx != -1) { string gacLocation = GetGacLocation(); if (gacLocation != null) gacLocation = MakeFileUrl(gacLocation); if (gacLocation == null) gacLocation = String.Empty; strFileData = strFileData.Replace("$Gac$", gacLocation); foundGacToken = true; } else { foundGacToken = false; } return SecurityManager.LoadPolicyLevelFromString(strFileData, PolicyLevelType.AppDomain); } #pragma warning restore 618 private void SetTrustParameters(TrustSection trustSection, SecurityPolicySection securityPolicySection, PolicyLevel policyLevel) { _trustLevel = trustSection.Level; if (_trustLevel != "Full") { // if we are in partial trust, HostingEnvironment should init HttpRuntime with a non-null PolicyLevel object Debug.Assert(policyLevel != null); _namedPermissionSet = policyLevel.GetNamedPermissionSet(trustSection.PermissionSetName); _policyLevel = policyLevel; _hostSecurityPolicyResolverType = trustSection.HostSecurityPolicyResolverType; String file = (String)securityPolicySection.TrustLevels[trustSection.Level].PolicyFileExpanded; _fcm.StartMonitoringFile(file, new FileChangeEventHandler(this.OnSecurityPolicyFileChange)); } } /* * Notification when something in the code-access security policy file changed */ private void OnSecurityPolicyFileChange(Object sender, FileChangeEvent e) { // shutdown the app domain Debug.Trace("AppDomainFactory", "Shutting down appdomain because code-access security policy file changed"); string message = FileChangesMonitor.GenerateErrorMessage(e.Action, e.FileName); if (message == null) { message = "Change in code-access security policy file"; } ShutdownAppDomain(ApplicationShutdownReason.ChangeInSecurityPolicyFile, message); } // notification when app_offline.htm file changed or created private void OnAppOfflineFileChange(Object sender, FileChangeEvent e) { // shutdown the app domain Debug.Trace("AppOffline", AppOfflineFileName + " changed - shutting down the app domain"); Debug.Trace("AppDomainFactory", "Shutting down appdomain because " + AppOfflineFileName + " file changed"); // WOS 1948399: set _userForcedShutdown to avoid DelayNotificationTimeout, since first request has not completed yet in integrated mode; SetUserForcedShutdown(); string message = FileChangesMonitor.GenerateErrorMessage(e.Action, AppOfflineFileName); if (message == null) { message = "Change in " + AppOfflineFileName; } ShutdownAppDomain(ApplicationShutdownReason.ConfigurationChange, message); } internal static String MakeFileUrl(String path) { Uri uri = new Uri(path); return uri.ToString(); } internal static String GetGacLocation() { StringBuilder buf = new StringBuilder(262); int iSize = 260; // if (UnsafeNativeMethods.GetCachePath(2, buf, ref iSize) >= 0) return buf.ToString(); throw new HttpException(SR.GetString(SR.GetGacLocaltion_failed)); } /* * Remove from metabase all read/write/browse permission from certain subdirs * */ internal static void RestrictIISFolders(HttpContext context) { int ret; HttpWorkerRequest wr = context.WorkerRequest; Debug.Assert(AppDomainAppId != null); // Don't do it if we are not running on IIS if (wr == null || !(wr is System.Web.Hosting.ISAPIWorkerRequest)) { return; } // Do it only for IIS 5 #if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features if (!(wr is System.Web.Hosting.ISAPIWorkerRequestInProcForIIS6)) #endif // !FEATURE_PAL { byte[] bufin; byte[] bufout = new byte[1]; // Just to keep EcbCallISAPI happy bufin = BitConverter.GetBytes(UnsafeNativeMethods.RESTRICT_BIN); ret = context.CallISAPI(UnsafeNativeMethods.CallISAPIFunc.RestrictIISFolders, bufin, bufout); if (ret != 1) { // Cannot pass back any HR from inetinfo.exe because CSyncPipeManager::GetDataFromIIS // does not support passing back any value when there is an error. Debug.Trace("RestrictIISFolders", "Cannot restrict folder access for '" + AppDomainAppId + "'."); } } } // // Helper to create instances (public vs. internal/private ctors, see 89781) // internal static Object CreateNonPublicInstance(Type type) { return CreateNonPublicInstance(type, null); } [PermissionSet(SecurityAction.Assert, Unrestricted = true)] internal static Object CreateNonPublicInstance(Type type, Object[] args) { return Activator.CreateInstance( type, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance, null, args, null); } internal static Object CreatePublicInstance(Type type) { return Activator.CreateInstance(type); } #if !DONTUSEFACTORYGENERATOR // Cache instances of IWebObjectFactory for each Type, which allow us // to instantiate the objects very efficiently, compared to calling // Activator.CreateInstance on every call. private static FactoryGenerator s_factoryGenerator; private static Hashtable s_factoryCache; private static bool s_initializedFactory; private static object s_factoryLock = new Object(); #endif // DONTUSEFACTORYGENERATOR /* * Faster implementation of CreatePublicInstance. It generates bits of IL * on the fly to achieve the improve performance. this should only be used * in cases where the number of different types to be created is well bounded. * Otherwise, we would create too much IL, which can bloat the process. */ internal static Object FastCreatePublicInstance(Type type) { #if DONTUSEFACTORYGENERATOR return CreatePublicInstance(type); #else // Only use the factory logic if the assembly is in the GAC, to avoid getting // assembly conflicts (VSWhidbey 405086) if (!type.Assembly.GlobalAssemblyCache) { return CreatePublicInstance(type); } // Create the factory generator on demand if (!s_initializedFactory) { // Devdiv 90810 - Synchronize to avoid race condition lock (s_factoryLock) { if (!s_initializedFactory) { s_factoryGenerator = new FactoryGenerator(); // Create the factory cache s_factoryCache = Hashtable.Synchronized(new Hashtable()); s_initializedFactory = true; } } } // First, check if it's cached IWebObjectFactory factory = (IWebObjectFactory)s_factoryCache[type]; if (factory == null) { Debug.Trace("FastCreatePublicInstance", "Creating generator for type " + type.FullName); // Create the object factory factory = s_factoryGenerator.CreateFactory(type); // Cache the factory s_factoryCache[type] = factory; } return factory.CreateInstance(); #endif // DONTUSEFACTORYGENERATOR } internal static Object CreatePublicInstance(Type type, Object[] args) { if (args == null) return Activator.CreateInstance(type); return Activator.CreateInstance(type, args); } static string GetCurrentUserName() { try { return WindowsIdentity.GetCurrent().Name; } catch { return null; } } void RaiseShutdownWebEventOnce() { if (!_shutdownWebEventRaised) { lock (this) { if (!_shutdownWebEventRaised) { // Raise Web Event WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationShutdown, WebApplicationLifetimeEvent.DetailCodeFromShutdownReason(ShutdownReason)); _shutdownWebEventRaised = true; } } } } private static string _DefaultPhysicalPathOnMapPathFailure; private void RelaxMapPathIfRequired() { try { RuntimeConfig config = RuntimeConfig.GetAppConfig(); if (config != null && config.HttpRuntime != null && config.HttpRuntime.RelaxedUrlToFileSystemMapping) { _DefaultPhysicalPathOnMapPathFailure = Path.Combine(_appDomainAppPath, "NOT_A_VALID_FILESYSTEM_PATH"); } } catch {} } internal static bool IsMapPathRelaxed { get { return _DefaultPhysicalPathOnMapPathFailure != null; } } internal static string GetRelaxedMapPathResult(string originalResult) { if (!IsMapPathRelaxed) // Feature not enabled? return originalResult; if (originalResult == null) // null is never valid: Return the hard coded default physical path return _DefaultPhysicalPathOnMapPathFailure; // Does it contain an invalid file-path char? if (originalResult.IndexOfAny(s_InvalidPhysicalPathChars) >= 0) return _DefaultPhysicalPathOnMapPathFailure; // Final check: do the full check to ensure it is valid try { bool pathTooLong; if (FileUtil.IsSuspiciousPhysicalPath(originalResult, out pathTooLong) || pathTooLong) return _DefaultPhysicalPathOnMapPathFailure; } catch { return _DefaultPhysicalPathOnMapPathFailure; } // it is valid return originalResult; } } public enum ApplicationShutdownReason { None = 0, HostingEnvironment = 1, ChangeInGlobalAsax = 2, ConfigurationChange = 3, UnloadAppDomainCalled = 4, ChangeInSecurityPolicyFile = 5, BinDirChangeOrDirectoryRename = 6, BrowsersDirChangeOrDirectoryRename = 7, CodeDirChangeOrDirectoryRename = 8, ResourcesDirChangeOrDirectoryRename = 9, IdleTimeout = 10, PhysicalApplicationPathChanged = 11, HttpRuntimeClose = 12, InitializationError = 13, MaxRecompilationsReached = 14, BuildManagerChange = 15, }; }