Moving BSTR conv to native code in SecureStringToBSTR.
[mono.git] / mcs / class / referencesource / System.Web / Hosting / HostingEnvironment.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="HostingEnvironment.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.Hosting {
8     using System;
9     using System.Collections;
10     using System.Configuration;
11     using System.Diagnostics.CodeAnalysis;
12     using System.Globalization;
13     using System.IO;
14     using System.Runtime.Caching;
15     using System.Runtime.CompilerServices;
16     using System.Runtime.InteropServices;
17     using System.Runtime.Remoting;
18     using System.Runtime.Remoting.Messaging;
19     using System.Security;
20     using System.Security.Permissions;
21     using System.Security.Policy;
22     using System.Security.Principal;
23     using System.Text;
24     using System.Threading;
25     using System.Threading.Tasks;
26     using System.Web;
27     using System.Web.Caching;
28     using System.Web.Compilation;
29     using System.Web.Configuration;
30     using System.Web.Management;
31     using System.Web.Util;
32     using System.Web.WebSockets;
33     using Microsoft.Win32;
34     using System.Collections.Generic;
35
36     [Flags]
37     internal enum HostingEnvironmentFlags {
38         Default = 0,
39         HideFromAppManager = 1,
40         ThrowHostingInitErrors = 2,
41         DontCallAppInitialize = 4,
42         ClientBuildManager = 8,
43         SupportsMultiTargeting = 16,
44     }
45
46     [Serializable]
47     internal class HostingEnvironmentParameters {
48         private HostingEnvironmentFlags _hostingFlags;
49         private ClientBuildManagerParameter _clientBuildManagerParameter;
50         private string _precompTargetPhysicalDir;
51         private string _iisExpressVersion;
52
53         public HostingEnvironmentFlags HostingFlags {
54             get { return _hostingFlags; }
55             set { _hostingFlags = value; }
56         }
57
58         // Directory where the precompiled site is placed
59         public string PrecompilationTargetPhysicalDirectory {
60             get { return _precompTargetPhysicalDir; }
61             set {
62                 _precompTargetPhysicalDir = FileUtil.FixUpPhysicalDirectory(value);
63             }
64         }
65
66         // Determines the behavior of the precompilation
67         public ClientBuildManagerParameter ClientBuildManagerParameter {
68             get { return _clientBuildManagerParameter; }
69             set { _clientBuildManagerParameter = value; }
70         }
71
72         // Determines which config system to load
73         public string IISExpressVersion {
74             get { return _iisExpressVersion; }
75             set { _iisExpressVersion = value; }
76         }
77
78         // Determines what FileChangeMonitor mode to use
79         public FcnMode FcnMode {
80             get;
81             set;
82         }
83
84         // Should FileChangesMonitor skip reading and caching DACLs?
85         public bool FcnSkipReadAndCacheDacls {
86             get;
87             set;
88         }
89
90         public KeyValuePair<string, bool>[] ClrQuirksSwitches {
91             get;
92             set;
93         }
94     }
95
96     public sealed class HostingEnvironment : MarshalByRefObject {
97
98         private static HostingEnvironment _theHostingEnvironment;
99         private EventHandler _onAppDomainUnload;
100         private ApplicationManager _appManager;
101         private HostingEnvironmentParameters _hostingParameters;
102         private IApplicationHost _appHost;
103         private bool _externalAppHost;
104         private IConfigMapPath _configMapPath;
105         private IConfigMapPath2 _configMapPath2;
106         private IntPtr _configToken;
107
108         private IdentitySection _appIdentity;
109         private IntPtr _appIdentityToken;
110         private bool _appIdentityTokenSet;
111
112         private String _appId;
113         private VirtualPath _appVirtualPath;
114         private String _appPhysicalPath;
115         private String _siteName;
116         private String _siteID;
117         private String _appConfigPath;
118
119         private bool   _isBusy;
120         private int    _busyCount;
121
122         private volatile static bool _stopListeningWasCalled; // static since it's process-wide
123         private bool _removedFromAppManager;
124         private bool _appDomainShutdownStarted;
125         private bool _shutdownInitiated;
126         private bool _shutdownInProgress;
127         private String _shutDownStack;
128
129         private int _inTrimCache;
130         private ObjectCacheHost _objectCacheHost;
131
132         // table of well know objects keyed by type
133         private Hashtable _wellKnownObjects = new Hashtable();
134
135         // list of registered IRegisteredObject instances, suspend listeners, and background work items
136         private Hashtable _registeredObjects = new Hashtable();
137         private SuspendManager _suspendManager = new SuspendManager();
138         private BackgroundWorkScheduler _backgroundWorkScheduler = null; // created on demand
139         private static readonly Task<object> _completedTask = Task.FromResult<object>(null);
140
141         // callback to make InitiateShutdown non-blocking
142         private WaitCallback _initiateShutdownWorkItemCallback;
143
144         // inside app domain idle shutdown logic
145         private IdleTimeoutMonitor _idleTimeoutMonitor;
146
147         private static IProcessHostSupportFunctions _functions;
148         private static bool _hasBeenRemovedFromAppManangerTable;
149
150         private const string TemporaryVirtualPathProviderKey = "__TemporaryVirtualPathProvider__";
151
152         // Determines what FileChangeMonitor mode to use
153         internal static FcnMode FcnMode {
154             get {
155                 if (_theHostingEnvironment != null && _theHostingEnvironment._hostingParameters != null) {
156                     return _theHostingEnvironment._hostingParameters.FcnMode;
157                 }
158                 return FcnMode.NotSet;
159             }
160         }
161
162         internal static bool FcnSkipReadAndCacheDacls {
163             get {
164                 if (_theHostingEnvironment != null && _theHostingEnvironment._hostingParameters != null) {
165                     return _theHostingEnvironment._hostingParameters.FcnSkipReadAndCacheDacls;
166                 }
167                 return false;
168             }
169         }
170
171         public override Object InitializeLifetimeService() {
172             return null; // never expire lease
173         }
174
175         /// <internalonly/>
176         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
177         public HostingEnvironment() {
178             if (_theHostingEnvironment != null)
179                 throw new InvalidOperationException(SR.GetString(SR.Only_1_HostEnv));
180
181             // remember singleton HostingEnvironment in a static
182             _theHostingEnvironment = this;
183
184             // start watching for app domain unloading
185             _onAppDomainUnload = new EventHandler(OnAppDomainUnload);
186             Thread.GetDomain().DomainUnload += _onAppDomainUnload;
187         }
188
189         internal long TrimCache(int percent) {
190             if (Interlocked.Exchange(ref _inTrimCache, 1) != 0)
191                 return 0;
192             try {
193                 long trimmedOrExpired = 0;
194                 // do nothing if we're shutting down
195                 if (!_shutdownInitiated) {
196                     trimmedOrExpired = HttpRuntime.CacheInternal.TrimCache(percent);
197                     if (_objectCacheHost != null && !_shutdownInitiated) {
198                         trimmedOrExpired += _objectCacheHost.TrimCache(percent);
199                     }
200                 }
201                 return trimmedOrExpired;
202             }
203             finally {
204                 Interlocked.Exchange(ref _inTrimCache, 0);
205             }
206         }
207
208         private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) {
209             Debug.Trace("PipelineRuntime", "HE.OnAppDomainUnload");
210
211             Thread.GetDomain().DomainUnload -= _onAppDomainUnload;
212
213             // check for unexpected shutdown
214             if (!_removedFromAppManager) {
215                 RemoveThisAppDomainFromAppManagerTableOnce();
216             }
217
218             HttpRuntime.RecoverFromUnexceptedAppDomainUnload();
219
220             // call Stop on all registered objects with immediate = true
221             StopRegisteredObjects(true);
222
223             // notify app manager
224             if (_appManager != null) {
225                 // disconnect the real app host and substitute it with a bogus one
226                 // to avoid exceptions later when app host is called (it normally wouldn't)
227                 IApplicationHost originalAppHost = null;
228
229                 if (_externalAppHost) {
230                     originalAppHost = _appHost;
231                     _appHost = new SimpleApplicationHost(_appVirtualPath, _appPhysicalPath);
232                     _externalAppHost = false;
233                 }
234
235                 IDisposable configSystem = _configMapPath2 as IDisposable;
236                 if (configSystem != null) {
237                     configSystem.Dispose();
238                 }
239
240                 _appManager.HostingEnvironmentShutdownComplete(_appId, originalAppHost);
241             }
242
243             // free the config access token
244             if (_configToken != IntPtr.Zero) {
245                 UnsafeNativeMethods.CloseHandle(_configToken);
246                 _configToken = IntPtr.Zero;
247             }
248         }
249
250         //
251         // Initialization
252         //
253
254         // called from app manager right after app domain (and hosting env) is created
255         internal void Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel) {
256             Initialize(appManager, appHost, configMapPathFactory, hostingParameters, policyLevel, null);
257         }
258
259         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
260         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control this method's callers.")]
261         internal void Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory,
262             HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel,
263             Exception appDomainCreationException) {
264
265             _hostingParameters = hostingParameters;
266
267             HostingEnvironmentFlags hostingFlags = HostingEnvironmentFlags.Default;
268             if (_hostingParameters != null) {
269                 hostingFlags = _hostingParameters.HostingFlags;
270                 if (_hostingParameters.IISExpressVersion != null) {
271                     ServerConfig.IISExpressVersion = _hostingParameters.IISExpressVersion;
272                 }
273             }
274
275             // Keep track of the app manager, unless HideFromAppManager flag was passed
276             if ((hostingFlags & HostingEnvironmentFlags.HideFromAppManager) == 0)
277                 _appManager = appManager;
278
279             if ((hostingFlags & HostingEnvironmentFlags.ClientBuildManager) != 0) {
280                 BuildManagerHost.InClientBuildManager = true;
281             }
282
283             if ((hostingFlags & HostingEnvironmentFlags.SupportsMultiTargeting) != 0) {
284                 BuildManagerHost.SupportsMultiTargeting = true;
285             }
286
287             // Set CLR quirks switches before the config system is initialized since config might depend on them
288             if (_hostingParameters != null && _hostingParameters.ClrQuirksSwitches != null && _hostingParameters.ClrQuirksSwitches.Length > 0) {
289                 SetClrQuirksSwitches(_hostingParameters.ClrQuirksSwitches);
290             }
291
292             //
293             // init config system using private config if applicable
294             //
295             if (appHost is ISAPIApplicationHost && !ServerConfig.UseMetabase) {
296                 string rootWebConfigPath = ((ISAPIApplicationHost)appHost).ResolveRootWebConfigPath();
297                 if (!String.IsNullOrEmpty(rootWebConfigPath)) {
298                     Debug.Assert(File.Exists(rootWebConfigPath), "File.Exists(rootWebConfigPath)");
299                     HttpConfigurationSystem.RootWebConfigurationFilePath = rootWebConfigPath;
300                 }
301
302                 // we need to explicit create a COM proxy in this app domain
303                 // so we don't go back to the default domain or have lifetime issues
304                 // remember support functions
305                 IProcessHostSupportFunctions proxyFunctions = ((ISAPIApplicationHost)appHost).SupportFunctions;
306                 if (null != proxyFunctions) {
307                     _functions = Misc.CreateLocalSupportFunctions(proxyFunctions);
308                 }
309             }
310
311             _appId = HttpRuntime.AppDomainAppId;
312             _appVirtualPath = HttpRuntime.AppDomainAppVirtualPathObject;
313             _appPhysicalPath = HttpRuntime.AppDomainAppPathInternal;
314             _appHost = appHost;
315
316             _configMapPath = configMapPathFactory.Create(_appVirtualPath.VirtualPathString, _appPhysicalPath);
317             HttpConfigurationSystem.EnsureInit(_configMapPath, true, false);
318
319             // attempt to cache and use IConfigMapPath2 provider
320             // which supports VirtualPath's to save on conversions
321             _configMapPath2 = _configMapPath as IConfigMapPath2;
322
323
324             _initiateShutdownWorkItemCallback = new WaitCallback(this.InitiateShutdownWorkItemCallback);
325
326             // notify app manager
327             if (_appManager != null) {
328                 _appManager.HostingEnvironmentActivated(CacheMemorySizePressure.EffectiveProcessMemoryLimit);
329             }
330
331             // make sure there is always app host
332             if (_appHost == null) {
333                 _appHost = new SimpleApplicationHost(_appVirtualPath, _appPhysicalPath);
334             }
335             else {
336                 _externalAppHost = true;
337             }
338
339             // remember the token to access config
340             _configToken = _appHost.GetConfigToken();
341
342             // Start with a MapPath based virtual path provider
343             _mapPathBasedVirtualPathProvider = new MapPathBasedVirtualPathProvider();
344             _virtualPathProvider = _mapPathBasedVirtualPathProvider;
345
346             // initiaze HTTP-independent features
347             HttpRuntime.InitializeHostingFeatures(hostingFlags, policyLevel, appDomainCreationException);
348
349             // VSWhidbey 393259. Do not monitor idle timeout for CBM since Venus
350             // will always restart a new appdomain if old one is shutdown.
351             if (!BuildManagerHost.InClientBuildManager) {
352                 // start monitoring for idle inside app domain
353                 StartMonitoringForIdleTimeout();
354             }
355
356             // notify app manager if the app domain limit is violated
357             EnforceAppDomainLimit();
358
359             // get application identity (for explicit impersonation mode)
360             GetApplicationIdentity();
361
362             // call AppInitialize, unless the flag says not to do it (e.g. CBM scenario).
363             // Also, don't call it if HostingInit failed (VSWhidbey 210495)
364             if(!HttpRuntime.HostingInitFailed) {
365                 try {
366                     BuildManager.ExecutePreAppStart();
367                     if ((hostingFlags & HostingEnvironmentFlags.DontCallAppInitialize) == 0) {
368                         BuildManager.CallAppInitializeMethod();
369                     }
370                 }
371                 catch (Exception e) {
372                     // could throw compilation errors in 'code' - report them with first http request
373                     HttpRuntime.InitializationException = e;
374
375                     if ((hostingFlags & HostingEnvironmentFlags.ThrowHostingInitErrors) != 0) {
376                         throw;
377                     }
378                 }
379             }
380         }
381
382         private void InitializeObjectCacheHostPrivate() {
383             // set ObjectCacheHost if the Host is not already set
384             if (ObjectCache.Host == null) {
385                 ObjectCacheHost objectCacheHost = new ObjectCacheHost();
386                 ObjectCache.Host = objectCacheHost;
387                 _objectCacheHost = objectCacheHost;
388             }
389         }
390
391         internal static void InitializeObjectCacheHost() {
392             if (_theHostingEnvironment != null) {
393                 _theHostingEnvironment.InitializeObjectCacheHostPrivate();
394             }
395         }
396
397         private void StartMonitoringForIdleTimeout() {
398             HostingEnvironmentSection hostEnvConfig = RuntimeConfig.GetAppLKGConfig().HostingEnvironment;
399
400             TimeSpan idleTimeout = (hostEnvConfig != null) ? hostEnvConfig.IdleTimeout : HostingEnvironmentSection.DefaultIdleTimeout;
401
402             // always create IdleTimeoutMonitor (even if config value is TimeSpan.MaxValue (infinite)
403             // IdleTimeoutMonitor is also needed to keep the last event for app domain set trimming
404             // and the timer is used to trim the application instances
405             _idleTimeoutMonitor = new IdleTimeoutMonitor(idleTimeout);
406         }
407
408         // enforce app domain limit
409         private void EnforceAppDomainLimit() {
410             if (_appManager == null)  /// detached app domain
411                 return;
412
413             int limit = 0;
414
415             try {
416                 ProcessModelSection pmConfig = RuntimeConfig.GetMachineConfig().ProcessModel;
417                 limit = pmConfig.MaxAppDomains;
418             }
419             catch {
420             }
421
422             if (limit > 0 && _appManager.AppDomainsCount >= limit) {
423                 // current app domain doesn't count yet (not in the table)
424                 // that's why '>=' above
425                 _appManager.ReduceAppDomainsCount(limit);
426             }
427         }
428
429         private void GetApplicationIdentity() {
430             // if the explicit impersonation is set, use it instead of UNC identity
431             try {
432                 IdentitySection c = RuntimeConfig.GetAppConfig().Identity;
433                 if (c.Impersonate && c.ImpersonateToken != IntPtr.Zero) {
434                     _appIdentity = c;
435                     _appIdentityToken = c.ImpersonateToken;
436                 }
437                 else {
438                     _appIdentityToken = _configToken;
439                 }
440                 _appIdentityTokenSet = true;
441             }
442             catch {
443             }
444         }
445
446         private static void SetClrQuirksSwitches(KeyValuePair<string, bool>[] switches) {
447             // First, see if the static API AppContext.SetSwitch even exists.
448             // Type.GetType will return null if the type doesn't exist; it will throw on catastrophic failure.
449
450             Type appContextType = Type.GetType("System.AppContext, " + AssemblyRef.Mscorlib);
451             if (appContextType == null) {
452                 return; // wrong version of mscorlib - do nothing
453             }
454
455             Action<string, bool> setter = (Action<string, bool>)Delegate.CreateDelegate(
456                 typeof(Action<string, bool>),
457                 appContextType,
458                 "SetSwitch",
459                 ignoreCase: false,
460                 throwOnBindFailure: false);
461             if (setter == null) {
462                 return; // wrong version of mscorlib - do nothing
463             }
464
465             // Finally, set each switch individually.
466
467             foreach (var sw in switches) {
468                 setter(sw.Key, sw.Value);
469             }
470         }
471
472
473         // If an exception was thrown during initialization, return it.
474         public static Exception InitializationException {
475             get {
476                 return HttpRuntime.InitializationException;
477             }
478         }
479
480         // called from app manager (from management APIs)
481         internal ApplicationInfo GetApplicationInfo() {
482             return new ApplicationInfo(_appId, _appVirtualPath, _appPhysicalPath);
483         }
484
485         //
486         // Shutdown logic
487         //
488         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
489         private void StopRegisteredObjects(bool immediate) {
490             if (_registeredObjects.Count > 0) {
491                 ArrayList list = new ArrayList();
492
493                 lock (this) {
494                     foreach (DictionaryEntry e in _registeredObjects) {
495                         Object x = e.Key;
496
497                         // well-known objects first
498                         if (IsWellKnownObject(x)) {
499                             list.Insert(0, x);
500                         }
501                         else {
502                             list.Add(x);
503                         }
504                     }
505                 }
506
507                 foreach (IRegisteredObject obj in list) {
508                     try {
509                         obj.Stop(immediate);
510                     }
511                     catch {
512                     }
513                 }
514             }
515         }
516
517         private void InitiateShutdownWorkItemCallback(Object state /*not used*/) {
518             Debug.Trace("HostingEnvironmentShutdown", "Shutting down: appId=" + _appId);
519
520             // no registered objects -- shutdown
521             if (_registeredObjects.Count == 0) {
522                 Debug.Trace("HostingEnvironmentShutdown", "No registered objects");
523                 ShutdownThisAppDomainOnce();
524                 return;
525             }
526
527             // call Stop on all registered objects with immediate = false
528             StopRegisteredObjects(false);
529
530             // no registered objects -- shutdown now
531             if (_registeredObjects.Count == 0) {
532                 Debug.Trace("HostingEnvironmentShutdown", "All registered objects gone after Stop(false)");
533                 ShutdownThisAppDomainOnce();
534                 return;
535             }
536
537             // if not everything shutdown synchronously give it some time.
538             int shutdownTimeoutSeconds = HostingEnvironmentSection.DefaultShutdownTimeout;
539             HostingEnvironmentSection hostEnvConfig = RuntimeConfig.GetAppLKGConfig().HostingEnvironment;
540             if (hostEnvConfig != null) {
541                 shutdownTimeoutSeconds = (int) hostEnvConfig.ShutdownTimeout.TotalSeconds;
542             }
543             Debug.Trace("HostingEnvironmentShutdown", "Waiting for " + shutdownTimeoutSeconds + " sec...");
544
545             DateTime waitUntil = DateTime.UtcNow.AddSeconds(shutdownTimeoutSeconds);
546             while (_registeredObjects.Count > 0 && DateTime.UtcNow < waitUntil) {
547                 Thread.Sleep(100);
548             }
549
550             Debug.Trace("HostingEnvironmentShutdown", "Shutdown timeout (" + shutdownTimeoutSeconds + " sec) expired");
551
552             // call Stop on all registered objects with immediate = true
553             StopRegisteredObjects(true);
554
555             // no registered objects -- shutdown now
556             if (_registeredObjects.Count == 0) {
557                 Debug.Trace("HostingEnvironmentShutdown", "All registered objects gone after Stop(true)");
558                 ShutdownThisAppDomainOnce();
559                 return;
560             }
561
562             // shutdown regardless
563             Debug.Trace("HostingEnvironmentShutdown", "Forced shutdown: " + _registeredObjects.Count + " registered objects left");
564             _registeredObjects = new Hashtable();
565             ShutdownThisAppDomainOnce();
566         }
567
568         // app domain shutdown logic
569         internal void InitiateShutdownInternal() {
570 #if DBG
571             try {
572 #endif
573             Debug.Trace("AppManager", "HostingEnvironment.InitiateShutdownInternal appId=" + _appId);
574
575             bool proceed = false;
576
577             if (!_shutdownInitiated) {
578                 lock (this) {
579                     if (!_shutdownInitiated) {
580                         _shutdownInProgress = true;
581                         proceed = true;
582                         _shutdownInitiated = true;
583                     }
584                 }
585             }
586
587             if (!proceed) {
588                 return;
589             }
590
591             HttpRuntime.SetShutdownReason(ApplicationShutdownReason.HostingEnvironment, "HostingEnvironment initiated shutdown");
592
593             // Avoid calling Environment.StackTrace if we are in the ClientBuildManager (Dev10 bug 824659)
594             if (!BuildManagerHost.InClientBuildManager) {
595                 new EnvironmentPermission(PermissionState.Unrestricted).Assert();
596                 try {
597                     _shutDownStack = Environment.StackTrace;
598                 }
599                 finally {
600                     CodeAccessPermission.RevertAssert();
601                 }
602             }
603
604             // waitChangeNotification need not be honored in ClientBuildManager (Dev11 bug 264894)
605             if (!BuildManagerHost.InClientBuildManager) {
606                 // this should only be called once, before the cache is disposed, and
607                 // the config records are released.
608                 HttpRuntime.CoalesceNotifications();
609             }
610
611             RemoveThisAppDomainFromAppManagerTableOnce();
612
613             // stop all registered objects without blocking
614             ThreadPool.QueueUserWorkItem(this._initiateShutdownWorkItemCallback);
615 #if DBG
616             } catch (Exception ex) {
617                 HandleExceptionFromInitiateShutdownInternal(ex);
618                 throw;
619             }
620 #endif
621         }
622
623 #if DBG
624         // InitiateShutdownInternal should never throw an exception, but we have seen cases where
625         // CLR bugs can cause it to fail without running to completion. This could cause an ASP.NET
626         // AppDomain never to unload. If we detect that an exception is thrown, we should DebugBreak
627         // so that the fundamentals team can investigate. Taking the Exception object as a parameter
628         // makes it easy to locate when looking at a stack dump.
629         [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
630         private static void HandleExceptionFromInitiateShutdownInternal(Exception ex) {
631             Debug.Break();
632         }
633 #endif
634
635         internal bool HasBeenRemovedFromAppManagerTable {
636             get {
637                 return _hasBeenRemovedFromAppManangerTable;
638             }
639             set {
640                 _hasBeenRemovedFromAppManangerTable = value;
641             }
642         }
643
644         private void RemoveThisAppDomainFromAppManagerTableOnce() {
645             bool proceed = false;
646             if (!_removedFromAppManager) {
647                 lock (this) {
648                     if (!_removedFromAppManager) {
649                         proceed = true;
650                         _removedFromAppManager = true;
651                     }
652                 }
653             }
654
655             if (!proceed)
656                 return;
657
658             if (_appManager != null) {
659                 Debug.Trace("AppManager", "Removing HostingEnvironment from AppManager table, appId=" + _appId);
660                 _appManager.HostingEnvironmentShutdownInitiated(_appId, this);
661             }
662 #if DBG
663             Debug.Trace("FileChangesMonitorIgnoreSubdirChange", 
664                         "*** REMOVE APPMANAGER TABLE" + DateTime.Now.ToString("hh:mm:ss.fff", CultureInfo.InvariantCulture) 
665                         + ": _appId=" + _appId);
666 #endif
667         }
668
669         private void ShutdownThisAppDomainOnce() {
670             bool proceed = false;
671
672             if (!_appDomainShutdownStarted) {
673                 lock (this) {
674                     if (!_appDomainShutdownStarted) {
675                         proceed = true;
676                         _appDomainShutdownStarted = true;
677                     }
678                 }
679             }
680
681             if (!proceed)
682                 return;
683
684             Debug.Trace("AppManager", "HostingEnvironment - shutting down AppDomain, appId=" + _appId);
685
686             // stop the timer used for idle timeout
687             if (_idleTimeoutMonitor != null) {
688                 _idleTimeoutMonitor.Stop();
689                 _idleTimeoutMonitor = null;
690             }
691
692             while (_inTrimCache == 1) {
693                 Thread.Sleep(100);
694             }
695
696             // close all outstanding WebSocket connections and begin winding down code that consumes them
697             AspNetWebSocketManager.Current.AbortAllAndWait();
698
699             // 
700             HttpRuntime.SetUserForcedShutdown();
701
702             //WOS 1400290: CantUnloadAppDomainException in ISAPI mode, wait until HostingEnvironment.ShutdownThisAppDomainOnce completes
703             _shutdownInProgress = false;
704
705             HttpRuntime.ShutdownAppDomainWithStackTrace(ApplicationShutdownReason.HostingEnvironment,
706                                                         SR.GetString(SR.Hosting_Env_Restart),
707                                                         _shutDownStack);
708         }
709
710         //
711         // internal methods called by app manager
712         //
713
714         // helper for app manager to implement AppHost.CreateAppHost
715         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
716         internal ObjectHandle CreateInstance(String assemblyQualifiedName) {
717             Type type = Type.GetType(assemblyQualifiedName, true);
718             return new ObjectHandle(Activator.CreateInstance(type));
719         }
720
721         // start well known object
722         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
723         internal ObjectHandle CreateWellKnownObjectInstance(String assemblyQualifiedName, bool failIfExists) {
724             Type type = Type.GetType(assemblyQualifiedName, true);
725             IRegisteredObject obj = null;
726             String key = type.FullName;
727             bool exists = false;
728
729             lock (this) {
730                 obj = _wellKnownObjects[key] as IRegisteredObject;
731
732                 if (obj == null) {
733                     obj = (IRegisteredObject)Activator.CreateInstance(type);
734                     _wellKnownObjects[key] = obj;
735                 }
736                 else {
737                     exists = true;
738                 }
739             }
740
741             if (exists && failIfExists) {
742                 throw new InvalidOperationException(SR.GetString(SR.Wellknown_object_already_exists, key));
743             }
744
745             return new ObjectHandle(obj);
746         }
747
748         // check if well known object
749         private bool IsWellKnownObject(Object obj) {
750             bool found = false;
751             String key = obj.GetType().FullName;
752
753             lock (this) {
754                 if (_wellKnownObjects[key] == obj) {
755                     found = true;
756                 }
757             }
758
759             return found;
760         }
761
762         // find well known object by type
763         internal ObjectHandle FindWellKnownObject(String assemblyQualifiedName) {
764             Type type = Type.GetType(assemblyQualifiedName, true);
765             IRegisteredObject obj = null;
766             String key = type.FullName;
767
768             lock (this) {
769                 obj = _wellKnownObjects[key] as IRegisteredObject;
770             }
771
772             return (obj != null) ? new ObjectHandle(obj) : null;
773         }
774
775         // stop well known object by type
776         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
777         internal void StopWellKnownObject(String assemblyQualifiedName) {
778             Type type = Type.GetType(assemblyQualifiedName, true);
779             IRegisteredObject obj = null;
780             String key = type.FullName;
781
782             lock (this) {
783                 obj = _wellKnownObjects[key] as IRegisteredObject;
784                 if (obj != null) {
785                     _wellKnownObjects.Remove(key);
786                     obj.Stop(false);
787                 }
788             }
789         }
790
791         internal bool IsIdle() {
792             bool isBusy = _isBusy;
793             _isBusy = false;
794             return (!isBusy && _busyCount == 0);
795         }
796
797         internal bool GetIdleValue() {
798             return (!_isBusy && _busyCount == 0);
799         }
800
801         internal void IncrementBusyCountInternal() {
802             _isBusy = true;
803             Interlocked.Increment(ref _busyCount);
804         }
805
806         internal void DecrementBusyCountInternal() {
807             _isBusy = true;
808             Interlocked.Decrement(ref _busyCount);
809
810             // Notify idle timeout monitor
811             IdleTimeoutMonitor itm = _idleTimeoutMonitor;
812             if (itm != null) {
813                 itm.LastEvent = DateTime.UtcNow;
814             }
815         }
816         internal void IsUnloaded()
817         {
818             return;
819         }
820
821         private void MessageReceivedInternal() {
822             _isBusy = true;
823
824             IdleTimeoutMonitor itm = _idleTimeoutMonitor;
825             if (itm != null) {
826                 itm.LastEvent = DateTime.UtcNow;
827             }
828         }
829
830         // the busier the app domain the higher the score
831         internal int LruScore {
832             get {
833                 if (_busyCount > 0)
834                     return _busyCount;
835
836                 IdleTimeoutMonitor itm = _idleTimeoutMonitor;
837
838                 if (itm == null)
839                     return 0;
840
841                 // return negative number of seconds since last activity
842                 return -(int)(DateTime.UtcNow - itm.LastEvent).TotalSeconds;
843             }
844         }
845
846         internal static ApplicationManager GetApplicationManager() {
847             if (_theHostingEnvironment == null)
848                 return null;
849
850             return _theHostingEnvironment._appManager;
851         }
852
853         //
854         // private helpers
855         //
856
857         // register protocol handler with hosting environment
858         private void RegisterRunningObjectInternal(IRegisteredObject obj) {
859             lock (this) {
860                 _registeredObjects[obj] = obj;
861
862                 ISuspendibleRegisteredObject suspendibleObject = obj as ISuspendibleRegisteredObject;
863                 if (suspendibleObject != null) {
864                     _suspendManager.RegisterObject(suspendibleObject);
865                 }
866             }
867         }
868
869         // unregister protocol handler from hosting environment
870         private void UnregisterRunningObjectInternal(IRegisteredObject obj) {
871             bool lastOne = false;
872
873             lock (this) {
874                 // if it is a well known object, remove it from that table as well
875                 String key = obj.GetType().FullName;
876                 if (_wellKnownObjects[key] == obj) {
877                     _wellKnownObjects.Remove(key);
878                 }
879
880                 // remove from running objects list
881                 _registeredObjects.Remove(obj);
882
883                 ISuspendibleRegisteredObject suspendibleObject = obj as ISuspendibleRegisteredObject;
884                 if (suspendibleObject != null) {
885                     _suspendManager.UnregisterObject(suspendibleObject);
886                 }
887
888                 if (_registeredObjects.Count == 0)
889                     lastOne = true;
890             }
891
892             if (!lastOne)
893                 return;
894
895             // shutdown app domain after last protocol handler is gone
896
897             InitiateShutdownInternal();
898         }
899
900         // site name
901         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This method is not dangerous.")]
902         private String GetSiteName() {
903             if (_siteName == null) {
904                 lock (this) {
905                     if (_siteName == null) {
906                         String s = null;
907
908                         if (_appHost != null) {
909                             // 
910                             InternalSecurityPermissions.Unrestricted.Assert();
911                             try {
912                                 s = _appHost.GetSiteName();
913                             }
914                             finally {
915                                 CodeAccessPermission.RevertAssert();
916                             }
917                         }
918
919                         if (s == null)
920                             s = WebConfigurationHost.DefaultSiteName;
921
922                         _siteName = s;
923                     }
924                 }
925             }
926
927             return _siteName;
928         }
929
930         // site ID
931         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This method is not dangerous.")]
932         private String GetSiteID() {
933             if (_siteID == null) {
934                 lock (this) {
935                     if (_siteID == null) {
936                         String s = null;
937
938                         if (_appHost != null) {
939                             // 
940                             InternalSecurityPermissions.Unrestricted.Assert();
941                             try {
942                                 s = _appHost.GetSiteID();
943                             }
944                             finally {
945                                 CodeAccessPermission.RevertAssert();
946                             }
947                         }
948
949                         if (s == null)
950                             s = WebConfigurationHost.DefaultSiteID;
951
952                         _siteID = s.ToLower(CultureInfo.InvariantCulture);
953                     }
954                 }
955             }
956
957             return _siteID;
958         }
959
960         // Return the configPath for the app, e.g. "machine/webroot/1/myapp"
961         private String GetAppConfigPath() {
962             if (_appConfigPath == null) {
963                 _appConfigPath = WebConfigurationHost.GetConfigPathFromSiteIDAndVPath(SiteID, ApplicationVirtualPathObject);
964             }
965
966             return _appConfigPath;
967         }
968
969         // Return the call context slot name to use for a virtual path
970         private static string GetFixedMappingSlotName(VirtualPath virtualPath) {
971             return "MapPath_" + virtualPath.VirtualPathString.ToLowerInvariant().GetHashCode().ToString(CultureInfo.InvariantCulture);
972         }
973
974         /*
975          * Map a virtual path to a physical path.  i.e. the physicalPath will be returned
976          * when MapPath is called on the virtual path, bypassing the IApplicationHost
977          */
978         private static string GetVirtualPathToFileMapping(VirtualPath virtualPath) {
979             return CallContext.GetData(GetFixedMappingSlotName(virtualPath)) as string;
980         }
981
982         /*
983          * Map a virtual path to a physical path.  i.e. the physicalPath will be returned
984          * when MapPath is called on the virtual path, bypassing the IApplicationHost
985          */
986         internal static object AddVirtualPathToFileMapping(
987             VirtualPath virtualPath, string physicalPath) {
988
989             // Save the mapping in the call context, using a key derived from the
990             // virtual path.  The mapping is only valid for the duration of the request.
991             CallContext.SetData(GetFixedMappingSlotName(virtualPath), physicalPath);
992
993             // Return a mapping object to keep track of the virtual path, and of the current
994             // virtualPathProvider.
995             VirtualPathToFileMappingState state = new VirtualPathToFileMappingState();
996             state.VirtualPath = virtualPath;
997             state.VirtualPathProvider = _theHostingEnvironment._virtualPathProvider;
998
999             // Always use the MapPathBasedVirtualPathProvider, otherwise the mapping mechanism
1000             // doesn't work (VSWhidbey 420702)
1001             // Set/Get the VPP on the call context so as not to affect other concurrent requests  (Dev10 852255)
1002             CallContext.SetData(TemporaryVirtualPathProviderKey, _theHostingEnvironment._mapPathBasedVirtualPathProvider);
1003
1004             return state;
1005         }
1006
1007         internal static void ClearVirtualPathToFileMapping(object state) {
1008
1009             VirtualPathToFileMappingState mapping = (VirtualPathToFileMappingState)state;
1010
1011             // Clear the mapping from the call context
1012             CallContext.SetData(GetFixedMappingSlotName(mapping.VirtualPath), null);
1013
1014             // Restore the previous VirtualPathProvider
1015             // Set/Get the VPP on the call context so as not to affect other concurrent requests  (Dev10 852255)
1016             CallContext.SetData(TemporaryVirtualPathProviderKey, null);
1017         }
1018
1019         private string MapPathActual(VirtualPath virtualPath, bool permitNull)
1020         {
1021             string result = null;
1022
1023             Debug.Assert(virtualPath != null);
1024
1025             virtualPath.FailIfRelativePath();
1026
1027             VirtualPath reqpath = virtualPath;
1028
1029             if (String.CompareOrdinal(reqpath.VirtualPathString, _appVirtualPath.VirtualPathString) == 0) {
1030                 // for application path don't need to call app host
1031                 Debug.Trace("MapPath", reqpath  +" is the app path");
1032                 result = _appPhysicalPath;
1033             }
1034             else {
1035                 using (new ProcessImpersonationContext()) {
1036                     // If there is a mapping for this virtual path in the call context, use it
1037                     result = GetVirtualPathToFileMapping(reqpath);
1038
1039                     if (result == null) {
1040                         // call host's mappath
1041                         if (_configMapPath == null) {
1042                             Debug.Trace("MapPath", "Missing _configMapPath");
1043                             throw new InvalidOperationException(SR.GetString(SR.Cannot_map_path, reqpath));
1044                         }
1045                         Debug.Trace("MapPath", "call ConfigMapPath (" + reqpath + ")");
1046
1047                         // see if the IConfigMapPath provider implements the interface
1048                         // with VirtualPath
1049                         try {
1050                             if (null != _configMapPath2) {
1051                                 result = _configMapPath2.MapPath(GetSiteID(), reqpath);
1052                             }
1053                             else {
1054                                 result = _configMapPath.MapPath(GetSiteID(), reqpath.VirtualPathString);
1055                             }
1056                             if (HttpRuntime.IsMapPathRelaxed)
1057                                 result = HttpRuntime.GetRelaxedMapPathResult(result);
1058                         } catch {
1059                             if (HttpRuntime.IsMapPathRelaxed)
1060                                 result = HttpRuntime.GetRelaxedMapPathResult(null);
1061                             else
1062                                 throw;
1063                         }
1064                     }
1065                 }
1066             }
1067
1068             if (String.IsNullOrEmpty(result)) {
1069                 Debug.Trace("MapPath", "null Result");
1070                 if (!permitNull) {
1071                     if (HttpRuntime.IsMapPathRelaxed)
1072                         result = HttpRuntime.GetRelaxedMapPathResult(null);
1073                     else
1074                         throw new InvalidOperationException(SR.GetString(SR.Cannot_map_path, reqpath));
1075                 }
1076             }
1077             else {
1078                 // ensure extra '\\' in the physical path if the virtual path had extra '/'
1079                 // and the other way -- no extra '\\' in physical if virtual didn't have it.
1080                 if (virtualPath.HasTrailingSlash) {
1081                     if (!UrlPath.PathEndsWithExtraSlash(result) && !UrlPath.PathIsDriveRoot(result))
1082                         result = result + "\\";
1083                 }
1084                 else {
1085                     if (UrlPath.PathEndsWithExtraSlash(result) && !UrlPath.PathIsDriveRoot(result))
1086                         result = result.Substring(0, result.Length - 1);
1087                 }
1088
1089                 Debug.Trace("MapPath", "    result=" + result);
1090             }
1091
1092             return result;
1093         }
1094
1095         //
1096         // public static methods
1097         //
1098
1099
1100         // register protocol handler with hosting environment
1101         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1102         public static void RegisterObject(IRegisteredObject obj) {
1103             if (_theHostingEnvironment != null)
1104                 _theHostingEnvironment.RegisterRunningObjectInternal(obj);
1105         }
1106
1107
1108         // unregister protocol handler from hosting environment
1109         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1110         public static void UnregisterObject(IRegisteredObject obj) {
1111             if (_theHostingEnvironment != null)
1112                 _theHostingEnvironment.UnregisterRunningObjectInternal(obj);
1113         }
1114
1115         // Schedules a task which can run in the background, independent of any request.
1116         // This differs from a normal ThreadPool work item in that ASP.NET can keep track
1117         // of how many work items registered through this API are currently running, and
1118         // the ASP.NET runtime will try not to delay AppDomain shutdown until these work
1119         // items have finished executing.
1120         //
1121         // Usage notes:
1122         // - This API cannot be called outside of an ASP.NET-managed AppDomain.
1123         // - The caller's ExecutionContext is not flowed to the work item.
1124         // - Scheduled work items are not guaranteed to ever execute, e.g., when AppDomain
1125         //   shutdown has already started by the time this API was called.
1126         // - The provided CancellationToken will be signaled when the application is
1127         //   shutting down. The work item should make every effort to honor this token.
1128         //   If a work item does not honor this token and continues executing it will
1129         //   eventually be considered rogue, and the ASP.NET runtime will rudely unload
1130         //   the AppDomain without waiting for the work item to finish.
1131         //
1132         // This overload of QueueBackgroundWorkItem takes a void-returning callback; the
1133         // work item will be considered finished when the callback returns.
1134         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
1135         public static void QueueBackgroundWorkItem(Action<CancellationToken> workItem) {
1136             if (workItem == null) {
1137                 throw new ArgumentNullException("workItem");
1138             }
1139
1140             QueueBackgroundWorkItem(ct => { workItem(ct); return _completedTask; });
1141         }
1142
1143         // See documentation on the other overload for a general API overview.
1144         //
1145         // This overload of QueueBackgroundWorkItem takes a Task-returning callback; the
1146         // work item will be considered finished when the returned Task transitions to a
1147         // terminal state.
1148         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
1149         public static void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem) {
1150             if (workItem == null) {
1151                 throw new ArgumentNullException("workItem");
1152             }
1153             if (_theHostingEnvironment == null) {
1154                 throw new InvalidOperationException(); // can only be called within an ASP.NET AppDomain
1155             }
1156
1157             _theHostingEnvironment.QueueBackgroundWorkItemInternal(workItem);
1158         }
1159
1160         private void QueueBackgroundWorkItemInternal(Func<CancellationToken, Task> workItem) {
1161             Debug.Assert(workItem != null);
1162
1163             BackgroundWorkScheduler scheduler = Volatile.Read(ref _backgroundWorkScheduler);
1164
1165             // If the scheduler doesn't exist, lazily create it, but only allow one instance to ever be published to the backing field
1166             if (scheduler == null) {
1167                 BackgroundWorkScheduler newlyCreatedScheduler = new BackgroundWorkScheduler(UnregisterObject, Misc.WriteUnhandledExceptionToEventLog);
1168                 scheduler = Interlocked.CompareExchange(ref _backgroundWorkScheduler, newlyCreatedScheduler, null) ?? newlyCreatedScheduler;
1169                 if (scheduler == newlyCreatedScheduler) {
1170                     RegisterObject(scheduler); // Only call RegisterObject if we just created the "winning" one
1171                 }
1172             }
1173
1174             scheduler.ScheduleWorkItem(workItem);
1175         }
1176
1177         // This event is a simple way to hook IStopListeningRegisteredObject.StopListening
1178         // without needing to call RegisterObject. The same restrictions which apply to
1179         // that method apply to this event.
1180         public static event EventHandler StopListening;
1181
1182         //
1183         // public static methods for the user code to call
1184         //
1185
1186
1187         public static void IncrementBusyCount() {
1188             if (_theHostingEnvironment != null)
1189                 _theHostingEnvironment.IncrementBusyCountInternal();
1190         }
1191
1192
1193         public static void DecrementBusyCount() {
1194             if (_theHostingEnvironment != null)
1195                 _theHostingEnvironment.DecrementBusyCountInternal();
1196         }
1197
1198
1199         public static void MessageReceived() {
1200             if (_theHostingEnvironment != null)
1201                 _theHostingEnvironment.MessageReceivedInternal();
1202         }
1203
1204         public static bool InClientBuildManager {
1205             get {
1206                 return BuildManagerHost.InClientBuildManager;
1207             }
1208         }
1209
1210         public static bool IsHosted {
1211             get {
1212                 return (_theHostingEnvironment != null);
1213             }
1214         }
1215
1216         internal static bool IsUnderIISProcess {
1217             get {
1218                 String process = VersionInfo.ExeName;
1219
1220                 return process == "aspnet_wp" ||
1221                        process == "w3wp" ||
1222                        process == "inetinfo";
1223             }
1224         }
1225
1226         internal static bool IsUnderIIS6Process {
1227             get {
1228                 return VersionInfo.ExeName == "w3wp";
1229             }
1230         }
1231
1232         public static IApplicationHost ApplicationHost {
1233             //DevDivBugs 109864: ASP.NET: path discovery issue - In low trust, it is possible to get the physical path of any virtual path on the machine
1234             [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1235             get {
1236                 if (_theHostingEnvironment == null)
1237                     return null;
1238
1239                 return _theHostingEnvironment._appHost;
1240             }
1241         }
1242
1243        internal static IApplicationHost ApplicationHostInternal {
1244             get {
1245                 if (_theHostingEnvironment == null)
1246                     return null;
1247
1248                 return _theHostingEnvironment._appHost;
1249             }
1250         }
1251
1252         internal IApplicationHost InternalApplicationHost {
1253             get {
1254                 return _appHost;
1255             }
1256         }
1257
1258         internal static int BusyCount {
1259             get {
1260                 if (_theHostingEnvironment == null)
1261                     return 0;
1262
1263                 return _theHostingEnvironment._busyCount;
1264             }
1265         }
1266
1267         internal static bool ShutdownInitiated {
1268             get {
1269                 if (_theHostingEnvironment == null)
1270                     return false;
1271
1272                 return _theHostingEnvironment._shutdownInitiated;
1273             }
1274         }
1275
1276
1277         internal static bool ShutdownInProgress {
1278             get {
1279                 if (_theHostingEnvironment == null)
1280                     return false;
1281
1282                 return _theHostingEnvironment._shutdownInProgress;
1283             }
1284         }
1285
1286
1287         /// <devdoc>
1288         ///    <para>The application ID (metabase path in IIS hosting).</para>
1289         /// </devdoc>
1290         public static String ApplicationID {
1291             get {
1292                 if (_theHostingEnvironment == null)
1293                     return null;
1294
1295                 InternalSecurityPermissions.AspNetHostingPermissionLevelHigh.Demand();
1296                 return _theHostingEnvironment._appId;
1297             }
1298         }
1299
1300         internal static String ApplicationIDNoDemand {
1301             get {
1302                 if (_theHostingEnvironment == null) {
1303                     return null;
1304                 }
1305
1306                 return _theHostingEnvironment._appId;
1307             }
1308         }
1309
1310
1311         /// <devdoc>
1312         ///    <para>Physical path to the application root.</para>
1313         /// </devdoc>
1314         public static String ApplicationPhysicalPath {
1315             get {
1316                 if (_theHostingEnvironment == null)
1317                     return null;
1318
1319                 InternalSecurityPermissions.AppPathDiscovery.Demand();
1320                 return _theHostingEnvironment._appPhysicalPath;
1321             }
1322         }
1323
1324
1325         /// <devdoc>
1326         ///    <para>Virtual path to the application root.</para>
1327         /// </devdoc>
1328         public static String ApplicationVirtualPath {
1329             get {
1330                 return VirtualPath.GetVirtualPathStringNoTrailingSlash(ApplicationVirtualPathObject);
1331             }
1332         }
1333
1334         internal static VirtualPath ApplicationVirtualPathObject {
1335             get {
1336                 if (_theHostingEnvironment == null)
1337                     return null;
1338
1339                 return _theHostingEnvironment._appVirtualPath;
1340             }
1341         }
1342
1343
1344         /// <devdoc>
1345         ///    <para>Site name.</para>
1346         /// </devdoc>
1347         public static String SiteName {
1348             get {
1349                 if (_theHostingEnvironment == null)
1350                     return null;
1351
1352                 InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand();
1353                 return _theHostingEnvironment.GetSiteName();
1354             }
1355         }
1356
1357         internal static String SiteNameNoDemand {
1358             get {
1359                 if (_theHostingEnvironment == null)
1360                     return null;
1361
1362                 return _theHostingEnvironment.GetSiteName();
1363             }
1364         }
1365
1366         internal static String SiteID {
1367             get {
1368                 if (_theHostingEnvironment == null)
1369                     return null;
1370
1371                 return _theHostingEnvironment.GetSiteID();
1372             }
1373         }
1374
1375         internal static IConfigMapPath ConfigMapPath {
1376             get {
1377                 if (_theHostingEnvironment == null)
1378                     return null;
1379
1380                 return _theHostingEnvironment._configMapPath;
1381             }
1382         }
1383
1384         internal static String AppConfigPath {
1385             get {
1386                 if (_theHostingEnvironment == null) {
1387                     return null;
1388                 }
1389
1390                 return _theHostingEnvironment.GetAppConfigPath();
1391             }
1392         }
1393
1394         // See comments in ApplicationManager.CreateAppDomainWithHostingEnvironment. This is the public API to access the
1395         // information we determined in that method. Defaults to 'false' if our AppDomain data isn't present.
1396         public static bool IsDevelopmentEnvironment {
1397             get {
1398                 return (AppDomain.CurrentDomain.GetData(".devEnvironment") as bool?) == true;
1399             }
1400         }
1401
1402
1403         /// <devdoc>
1404         ///    <para>
1405         ///       Gets a reference to the System.Web.Cache.Cache object for the current request.
1406         ///    </para>
1407         /// </devdoc>
1408         public static Cache Cache {
1409             get { return HttpRuntime.Cache; }
1410         }
1411
1412         // count of all app domain from app manager
1413         internal static int AppDomainsCount {
1414             get {
1415                 ApplicationManager appManager = GetApplicationManager();
1416                 return (appManager != null) ? appManager.AppDomainsCount : 0;
1417             }
1418         }
1419
1420         internal static HostingEnvironmentParameters HostingParameters {
1421             get {
1422                 if (_theHostingEnvironment == null)
1423                     return null;
1424
1425                 return _theHostingEnvironment._hostingParameters;
1426             }
1427         }
1428
1429         // Return an integer that is unique for each appdomain.  This can be used
1430         // to create things like once-per-appdomain temp files without having different
1431         // processes/appdomains step on each other
1432         private static int s_appDomainUniqueInteger;
1433         internal static int AppDomainUniqueInteger {
1434             get {
1435                 if (s_appDomainUniqueInteger == 0) {
1436                     s_appDomainUniqueInteger = Guid.NewGuid().GetHashCode();
1437                 }
1438
1439                 return s_appDomainUniqueInteger;
1440             }
1441         }
1442
1443         public static ApplicationShutdownReason ShutdownReason {
1444             get { return HttpRuntime.ShutdownReason; }
1445         }
1446
1447         // Was CGlobalModule::OnGlobalStopListening called?
1448         internal static bool StopListeningWasCalled {
1449             get {
1450                 return _stopListeningWasCalled;
1451             }
1452         }
1453
1454         [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive", Justification = "See comment in function.")]
1455         internal static void SetupStopListeningHandler() {
1456             StopListeningWaitHandle waitHandle = new StopListeningWaitHandle();
1457
1458             RegisteredWaitHandle registeredWaitHandle = null;
1459             registeredWaitHandle = ThreadPool.UnsafeRegisterWaitForSingleObject(waitHandle, (_, __) => {
1460                 // Referencing the field from within the callback should be sufficient to keep the GC
1461                 // from reclaiming the RegisteredWaitHandle; the race condition is fine.
1462                 GC.KeepAlive(registeredWaitHandle);
1463                 OnGlobalStopListening();
1464             }, null, Timeout.Infinite, executeOnlyOnce: true);
1465         }
1466
1467         private static void OnGlobalStopListening() {
1468             _stopListeningWasCalled = true;
1469
1470             EventHandler eventHandler = StopListening;
1471             if (eventHandler != null) {
1472                 eventHandler(null /* static means no sender */, EventArgs.Empty);
1473             }
1474
1475             if (_theHostingEnvironment != null) {
1476                 _theHostingEnvironment.FireStopListeningHandlers();
1477             }
1478         }
1479
1480         [SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity", Justification = "'this' always has strong identity.")]
1481         private void FireStopListeningHandlers() {
1482             List<IStopListeningRegisteredObject> listeners = new List<IStopListeningRegisteredObject>();
1483             lock (this) {
1484                 foreach (DictionaryEntry e in _registeredObjects) {
1485                     IStopListeningRegisteredObject listener = e.Key as IStopListeningRegisteredObject;
1486                     if (listener != null) {
1487                         listeners.Add(listener);
1488                     }
1489                 }
1490             }
1491
1492             foreach (var listener in listeners) {
1493                 listener.StopListening();
1494             }
1495         }
1496
1497         /// <devdoc>
1498         ///    <para>Initiate app domain unloading for the current app.</para>
1499         /// </devdoc>
1500         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1501         public static void InitiateShutdown() {
1502             if (_theHostingEnvironment != null)
1503                 _theHostingEnvironment.InitiateShutdownInternal();
1504         }
1505
1506         internal static void InitiateShutdownWithoutDemand() {
1507             if (_theHostingEnvironment != null)
1508                 _theHostingEnvironment.InitiateShutdownInternal();
1509         }
1510
1511         //
1512         // Internal methods for the ApplicationManager to suspend / resume this application.
1513         // Using GCHandle instead of ObjectHandle means we don't need to worry about lease lifetimes.
1514         //
1515
1516         internal IntPtr SuspendApplication() {
1517             var state = _suspendManager.Suspend();
1518             return GCUtil.RootObject(state);
1519         }
1520
1521         internal void ResumeApplication(IntPtr state) {
1522             var unwrappedState = GCUtil.UnrootObject(state);
1523             _suspendManager.Resume(unwrappedState);
1524         }
1525
1526         /// <devdoc>
1527         ///    <para>Maps a virtual path to a physical path.</para>
1528         /// </devdoc>
1529         public static string MapPath(string virtualPath) {
1530             return MapPath(VirtualPath.Create(virtualPath));
1531         }
1532
1533         internal static string MapPath(VirtualPath virtualPath) {
1534             if (_theHostingEnvironment == null)
1535                 return null;
1536
1537             String path = MapPathInternal(virtualPath);
1538
1539             if (path != null)
1540                 InternalSecurityPermissions.PathDiscovery(path).Demand();
1541
1542             return path;
1543         }
1544
1545         internal static String MapPathInternal(string virtualPath) {
1546             return MapPathInternal(VirtualPath.Create(virtualPath));
1547         }
1548
1549         internal static String MapPathInternal(VirtualPath virtualPath) {
1550             if (_theHostingEnvironment == null) {
1551                 return null;
1552             }
1553
1554             return _theHostingEnvironment.MapPathActual(virtualPath, false);
1555         }
1556
1557         internal static String MapPathInternal(string virtualPath, bool permitNull) {
1558             return MapPathInternal(VirtualPath.Create(virtualPath), permitNull);
1559         }
1560
1561         internal static String MapPathInternal(VirtualPath virtualPath, bool permitNull) {
1562             if (_theHostingEnvironment == null) {
1563                 return null;
1564             }
1565
1566             return _theHostingEnvironment.MapPathActual(virtualPath, permitNull);
1567         }
1568
1569         internal static string MapPathInternal(string virtualPath, string baseVirtualDir, bool allowCrossAppMapping) {
1570             return MapPathInternal(VirtualPath.Create(virtualPath),
1571                 VirtualPath.CreateNonRelative(baseVirtualDir), allowCrossAppMapping);
1572         }
1573
1574         internal static string MapPathInternal(VirtualPath virtualPath, VirtualPath baseVirtualDir, bool allowCrossAppMapping) {
1575             Debug.Assert(baseVirtualDir != null, "baseVirtualDir != null");
1576
1577             // Combine it with the base and reduce
1578             virtualPath = baseVirtualDir.Combine(virtualPath);
1579
1580             if (!allowCrossAppMapping && !virtualPath.IsWithinAppRoot)
1581                 throw new ArgumentException(SR.GetString(SR.Cross_app_not_allowed, virtualPath));
1582
1583             return MapPathInternal(virtualPath);
1584         }
1585
1586         internal static WebApplicationLevel GetPathLevel(String path) {
1587             WebApplicationLevel pathLevel = WebApplicationLevel.AboveApplication;
1588
1589             if (_theHostingEnvironment != null && !String.IsNullOrEmpty(path)) {
1590                 String appPath = ApplicationVirtualPath;
1591
1592                 if (appPath == "/") {
1593                     if (path == "/") {
1594                         pathLevel = WebApplicationLevel.AtApplication;
1595                     }
1596                     else if (path[0] == '/') {
1597                         pathLevel = WebApplicationLevel.BelowApplication;
1598                     }
1599                 }
1600                 else {
1601                     if (StringUtil.EqualsIgnoreCase(appPath, path)) {
1602                         pathLevel = WebApplicationLevel.AtApplication;
1603                     }
1604                     else if (path.Length > appPath.Length && path[appPath.Length] == '/' &&
1605                         StringUtil.StringStartsWithIgnoreCase(path, appPath)) {
1606
1607                         pathLevel = WebApplicationLevel.BelowApplication;
1608                     }
1609                 }
1610             }
1611
1612             return pathLevel;
1613         }
1614
1615
1616         //
1617         // Impersonation helpers
1618         //
1619         // user token for the app (hosting / unc)
1620         internal static IntPtr ApplicationIdentityToken {
1621             get {
1622                 if (_theHostingEnvironment == null) {
1623                     return IntPtr.Zero;
1624                 }
1625                 else {
1626                     if (_theHostingEnvironment._appIdentityTokenSet)
1627                         return _theHostingEnvironment._appIdentityToken;
1628                     else
1629                         return _theHostingEnvironment._configToken;
1630                 }
1631             }
1632         }
1633
1634
1635         // check if application impersonation != process impersonation
1636         internal static bool HasHostingIdentity {
1637             get {
1638                 return (ApplicationIdentityToken != IntPtr.Zero);
1639             }
1640         }
1641
1642         // impersonate application identity
1643         [SecurityPermission(SecurityAction.Demand, ControlPrincipal = true)]
1644         public static IDisposable Impersonate() {
1645             return new ApplicationImpersonationContext();
1646         }
1647
1648         // impersonate the given user identity
1649         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1650         public static IDisposable Impersonate(IntPtr token) {
1651             if (token == IntPtr.Zero) {
1652                 return new ProcessImpersonationContext();
1653             }
1654             else {
1655                 return new ImpersonationContext(token);
1656             }
1657         }
1658
1659         // impersonate as configured for a given path
1660         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1661         public static IDisposable Impersonate(IntPtr userToken, String virtualPath) {
1662             virtualPath = UrlPath.MakeVirtualPathAppAbsoluteReduceAndCheck(virtualPath);
1663
1664             if (_theHostingEnvironment == null) {
1665                 return Impersonate(userToken);
1666             }
1667
1668             IdentitySection c = RuntimeConfig.GetConfig(virtualPath).Identity;
1669             if (c.Impersonate) {
1670                 if (c.ImpersonateToken != IntPtr.Zero) {
1671                     return new ImpersonationContext(c.ImpersonateToken);
1672                 }
1673                 else {
1674                     return new ImpersonationContext(userToken);
1675                 }
1676             }
1677             else {
1678                 return new ApplicationImpersonationContext();
1679             }
1680         }
1681
1682         //
1683         //  Culture helpers
1684         //
1685
1686         public static IDisposable SetCultures() {
1687             return SetCultures(RuntimeConfig.GetAppLKGConfig().Globalization);
1688         }
1689
1690         public static IDisposable SetCultures(string virtualPath) {
1691             virtualPath = UrlPath.MakeVirtualPathAppAbsoluteReduceAndCheck(virtualPath);
1692             return SetCultures(RuntimeConfig.GetConfig(virtualPath).Globalization);
1693         }
1694
1695         private static IDisposable SetCultures(GlobalizationSection gs) {
1696             CultureContext c = new CultureContext();
1697
1698             if (gs != null) {
1699                 CultureInfo culture = null;
1700                 CultureInfo uiCulture = null;
1701
1702                 if (gs.Culture != null && gs.Culture.Length > 0) {
1703                     try {
1704                         culture = HttpServerUtility.CreateReadOnlyCultureInfo(gs.Culture);
1705                     }
1706                     catch {
1707                     }
1708                 }
1709
1710                 if (gs.UICulture != null && gs.UICulture.Length > 0) {
1711                     try {
1712                         uiCulture = HttpServerUtility.CreateReadOnlyCultureInfo(gs.UICulture);
1713                     }
1714                     catch {
1715                     }
1716                 }
1717
1718                 c.SetCultures(culture, uiCulture);
1719             }
1720
1721             return c;
1722         }
1723
1724
1725         class CultureContext : IDisposable {
1726             CultureInfo _savedCulture;
1727             CultureInfo _savedUICulture;
1728
1729             internal CultureContext() {
1730             }
1731
1732             void IDisposable.Dispose() {
1733                 RestoreCultures();
1734             }
1735
1736             internal void SetCultures(CultureInfo culture, CultureInfo uiCulture) {
1737                 CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
1738                 CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;
1739
1740                 if (culture != null && culture != currentCulture) {
1741                     Thread.CurrentThread.CurrentCulture = culture;
1742                     _savedCulture = currentCulture;
1743                 }
1744
1745                 if (uiCulture != null && uiCulture != currentCulture) {
1746                     Thread.CurrentThread.CurrentUICulture = uiCulture;
1747                     _savedUICulture = currentUICulture;
1748                 }
1749             }
1750
1751             internal void RestoreCultures() {
1752                 if (_savedCulture != null && _savedCulture != Thread.CurrentThread.CurrentCulture) {
1753                     Thread.CurrentThread.CurrentCulture = _savedCulture;
1754                     _savedCulture = null;
1755                 }
1756
1757                 if (_savedUICulture != null && _savedUICulture != Thread.CurrentThread.CurrentUICulture) {
1758                     Thread.CurrentThread.CurrentUICulture = _savedUICulture;
1759                     _savedUICulture = null;
1760                 }
1761             }
1762         }
1763
1764         //
1765         // VirtualPathProvider related code
1766         //
1767
1768         private VirtualPathProvider _virtualPathProvider;
1769         private VirtualPathProvider _mapPathBasedVirtualPathProvider;
1770
1771
1772         public static VirtualPathProvider VirtualPathProvider {
1773             get {
1774                 if (_theHostingEnvironment == null)
1775                     return null;
1776
1777                 // Set/Get the VPP on the call context so as not to affect other concurrent requests  (Dev10 852255)
1778                 var tempVPP = CallContext.GetData(TemporaryVirtualPathProviderKey);
1779                 if (tempVPP != null) {
1780                     return tempVPP as VirtualPathProvider;
1781                 }
1782
1783                 return _theHostingEnvironment._virtualPathProvider;
1784             }
1785         }
1786
1787         internal static bool UsingMapPathBasedVirtualPathProvider {
1788             get {
1789                 if (_theHostingEnvironment == null)
1790                     return true;
1791
1792                 return (_theHostingEnvironment._virtualPathProvider ==
1793                     _theHostingEnvironment._mapPathBasedVirtualPathProvider);
1794             }
1795         }
1796
1797         // [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.High)]
1798         // Removed the above LinkDemand for AspNetHostingPermissionLevel.High. If we decide to add VPP
1799         // support for config in the future, we should have a separate API with a demand for registering
1800         // VPPs supporting configuration.
1801         public static void RegisterVirtualPathProvider(VirtualPathProvider virtualPathProvider) {
1802
1803             if (_theHostingEnvironment == null)
1804                 throw new InvalidOperationException();
1805
1806             // Ignore the VirtualPathProvider on precompiled sites (VSWhidbey 368169,404844)
1807             if (BuildManager.IsPrecompiledApp)
1808                 return;
1809
1810             RegisterVirtualPathProviderInternal(virtualPathProvider);
1811         }
1812
1813         internal static void RegisterVirtualPathProviderInternal(VirtualPathProvider virtualPathProvider) {
1814             VirtualPathProvider previous = _theHostingEnvironment._virtualPathProvider;
1815             _theHostingEnvironment._virtualPathProvider = virtualPathProvider;
1816
1817             // Give it the previous provider so it can delegate if needed
1818             virtualPathProvider.Initialize(previous);
1819         }
1820
1821         // Helper class used to keep track of state when using
1822         // AddVirtualPathToFileMapping & ClearVirtualPathToFileMapping
1823         internal class VirtualPathToFileMappingState {
1824             internal VirtualPath VirtualPath;
1825             internal VirtualPathProvider VirtualPathProvider;
1826         }
1827
1828         internal static IProcessHostSupportFunctions SupportFunctions {
1829             get {
1830                 return _functions;
1831             }
1832             set {
1833                 _functions = value;
1834             }
1835         }
1836
1837         [SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased", 
1838                          Justification="matches casing of config attribute")]
1839         public static int MaxConcurrentRequestsPerCPU {
1840             get {
1841                 if (!HttpRuntime.UseIntegratedPipeline) {
1842                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1843                 }
1844                 return UnsafeIISMethods.MgdGetMaxConcurrentRequestsPerCPU();
1845             }
1846             [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1847             set {
1848                 if (!HttpRuntime.UseIntegratedPipeline) {
1849                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1850                 }
1851                 int hr = UnsafeIISMethods.MgdSetMaxConcurrentRequestsPerCPU(value);
1852                 switch (hr) {
1853                     case HResults.S_FALSE:
1854                         // Because "maxConcurrentRequestsPerCPU" is currently zero, we cannot set the value, since that would
1855                         // enable the feature, which can only be done via configuration.
1856                         throw new InvalidOperationException(SR.GetString(SR.Queue_limit_is_zero, "maxConcurrentRequestsPerCPU"));
1857                     case HResults.E_INVALIDARG:
1858                         // The value must be greater than zero.  A value of zero would disable the feature, but this can only be done via configuration.
1859                         throw new ArgumentException(SR.GetString(SR.Invalid_queue_limit));
1860                 }
1861             }
1862         }
1863
1864         [SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased",
1865                          Justification="matches casing of config attribute")]
1866         public static int MaxConcurrentThreadsPerCPU {
1867             get {
1868                 if (!HttpRuntime.UseIntegratedPipeline) {
1869                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1870                 }
1871                 return UnsafeIISMethods.MgdGetMaxConcurrentThreadsPerCPU();
1872             }
1873             [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
1874             set {
1875                 if (!HttpRuntime.UseIntegratedPipeline) {
1876                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1877                 }
1878                 int hr = UnsafeIISMethods.MgdSetMaxConcurrentThreadsPerCPU(value);
1879                 switch (hr) {
1880                     case HResults.S_FALSE:
1881                         // Because "maxConcurrentThreadsPerCPU" is currently zero, we cannot set the value, since that would
1882                         // enable the feature, which can only be done via configuration.
1883                         throw new InvalidOperationException(SR.GetString(SR.Queue_limit_is_zero, "maxConcurrentThreadsPerCPU"));
1884                     case HResults.E_INVALIDARG:
1885                         // The value must be greater than zero.  A value of zero would disable the feature, but this can only be done via configuration.
1886                         throw new ArgumentException(SR.GetString(SR.Invalid_queue_limit));
1887                 }
1888             }
1889         }
1890
1891         /// <summary>
1892         /// Returns the ASP.NET hosted domain.
1893         /// </summary>
1894         internal AppDomain HostedAppDomain {
1895             get {
1896                 return AppDomain.CurrentDomain;
1897             }
1898         }
1899
1900     }
1901 }
1902