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