1 // 2 // System.Web.Hosting.HostingEnvironment.cs 3 // 4 // Author: 5 // Chris Toshok (toshok@ximian.com) 6 // Gonzalo Paniagua Javier (gonzalo@ximian.com) 7 // 8 9 // 10 // Copyright (C) 2005,2006 Novell, Inc (http://www.novell.com) 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining 13 // a copy of this software and associated documentation files (the 14 // "Software"), to deal in the Software without restriction, including 15 // without limitation the rights to use, copy, modify, merge, publish, 16 // distribute, sublicense, and/or sell copies of the Software, and to 17 // permit persons to whom the Software is furnished to do so, subject to 18 // the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be 21 // included in all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 // 31 32 33 using System; 34 using System.Collections.Generic; 35 using System.Globalization; 36 using System.Linq; 37 using System.Security.Permissions; 38 using System.Threading; 39 using System.Threading.Tasks; 40 using System.Web.Configuration; 41 using System.Web.Caching; 42 using System.Web.Util; 43 44 namespace System.Web.Hosting { 45 46 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] 47 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.High)] 48 public sealed class HostingEnvironment : MarshalByRefObject 49 { 50 static bool is_hosted; 51 #pragma warning disable 0649 52 static string site_name; 53 static ApplicationShutdownReason shutdown_reason; 54 #pragma warning restore 0649 55 internal static BareApplicationHost Host; 56 static VirtualPathProvider vpath_provider = (HttpRuntime.AppDomainAppVirtualPath == null) ? null : 57 new DefaultVirtualPathProvider (); 58 static int busy_count; 59 static BackgroundWorkScheduler _backgroundWorkScheduler = null; // created on demand 60 static readonly Task<object> _completedTask = Task.FromResult<object>(null); 61 62 internal static bool HaveCustomVPP { 63 get; 64 private set; 65 } 66 HostingEnvironment()67 public HostingEnvironment () 68 { 69 // The documentation says that this is called once per domain by the ApplicationManager and 70 // then it throws InvalidOperationException whenever called. 71 throw new InvalidOperationException (); 72 } 73 74 public static string ApplicationID { 75 get { return HttpRuntime.AppDomainAppId; } 76 } 77 78 public static string ApplicationPhysicalPath { 79 get { return HttpRuntime.AppDomainAppPath; } 80 } 81 82 public static string ApplicationVirtualPath { 83 get { return HttpRuntime.AppDomainAppVirtualPath; } 84 } 85 86 public static Cache Cache { 87 get { return HttpRuntime.Cache; } 88 } 89 90 public static Exception InitializationException { 91 get { return HttpApplication.InitializationException; } 92 } 93 94 public static bool IsHosted { 95 get { return is_hosted; } 96 internal set { is_hosted = value; } 97 } 98 99 public static ApplicationShutdownReason ShutdownReason { 100 get { return shutdown_reason; } 101 } 102 103 public static string SiteName { 104 get { return site_name; } 105 internal set { site_name = value; } 106 } 107 108 public static VirtualPathProvider VirtualPathProvider { 109 get { return vpath_provider; } 110 } 111 112 public static bool InClientBuildManager { 113 get { 114 // Mono doesn't have a ClientBuildManager, so we can't be in it. Simple as that. 115 return false; 116 } 117 } 118 DecrementBusyCount()119 public static void DecrementBusyCount () 120 { 121 Interlocked.Decrement (ref busy_count); 122 } 123 124 [MonoTODO ("Not implemented")] Impersonate()125 public static IDisposable Impersonate () 126 { 127 throw new NotImplementedException (); 128 } 129 130 [MonoTODO ("Not implemented")] Impersonate(IntPtr token)131 public static IDisposable Impersonate (IntPtr token) 132 { 133 throw new NotImplementedException (); 134 } 135 136 [MonoTODO ("Not implemented")] Impersonate(IntPtr userToken, string virtualPath)137 public static IDisposable Impersonate (IntPtr userToken, string virtualPath) 138 { 139 throw new NotImplementedException (); 140 } 141 IncrementBusyCount()142 public static void IncrementBusyCount () 143 { 144 Interlocked.Increment (ref busy_count); 145 } 146 InitializeLifetimeService()147 public override object InitializeLifetimeService () 148 { 149 return null; 150 } 151 InitiateShutdown()152 public static void InitiateShutdown () 153 { 154 HttpRuntime.UnloadAppDomain (); 155 } 156 MapPath(string virtualPath)157 public static string MapPath (string virtualPath) 158 { 159 if (virtualPath == null || virtualPath == "") 160 throw new ArgumentNullException ("virtualPath"); 161 162 HttpContext context = HttpContext.Current; 163 HttpRequest req = context == null ? null : context.Request; 164 if (req == null) 165 return null; 166 167 return req.MapPath (virtualPath); 168 } 169 RegisterObject(IRegisteredObject obj)170 public static void RegisterObject (IRegisteredObject obj) 171 { 172 if (obj == null) 173 throw new ArgumentNullException ("obj"); 174 175 if (Host != null) 176 Host.RegisterObject (obj, false); 177 } 178 RegisterVirtualPathProvider(VirtualPathProvider virtualPathProvider)179 public static void RegisterVirtualPathProvider (VirtualPathProvider virtualPathProvider) 180 { 181 if (HttpRuntime.AppDomainAppVirtualPath == null) 182 throw new InvalidOperationException (); 183 184 if (virtualPathProvider == null) 185 throw new ArgumentNullException ("virtualPathProvider"); 186 187 VirtualPathProvider previous = vpath_provider; 188 vpath_provider = virtualPathProvider; 189 vpath_provider.InitializeAndSetPrevious (previous); 190 if (!(virtualPathProvider is DefaultVirtualPathProvider)) 191 HaveCustomVPP = true; 192 else 193 HaveCustomVPP = false; 194 } 195 SetCultures(string virtualPath)196 public static IDisposable SetCultures (string virtualPath) 197 { 198 GlobalizationSection gs = WebConfigurationManager.GetSection ("system.web/globalization", virtualPath) as GlobalizationSection; 199 IDisposable ret = Thread.CurrentThread.CurrentCulture as IDisposable; 200 string culture = gs.Culture; 201 if (String.IsNullOrEmpty (culture)) 202 return ret; 203 Thread.CurrentThread.CurrentCulture = new CultureInfo (culture); 204 return ret; 205 } 206 SetCultures()207 public static IDisposable SetCultures () 208 { 209 return SetCultures ("~/"); 210 } 211 UnregisterObject(IRegisteredObject obj)212 public static void UnregisterObject (IRegisteredObject obj) 213 { 214 if (obj == null) 215 throw new ArgumentNullException ("obj"); 216 217 if (Host != null) 218 Host.UnregisterObject (obj); 219 } 220 221 // Schedules a task which can run in the background, independent of any request. 222 // This differs from a normal ThreadPool work item in that ASP.NET can keep track 223 // of how many work items registered through this API are currently running, and 224 // the ASP.NET runtime will try not to delay AppDomain shutdown until these work 225 // items have finished executing. 226 // 227 // Usage notes: 228 // - This API cannot be called outside of an ASP.NET-managed AppDomain. 229 // - The caller's ExecutionContext is not flowed to the work item. 230 // - Scheduled work items are not guaranteed to ever execute, e.g., when AppDomain 231 // shutdown has already started by the time this API was called. 232 // - The provided CancellationToken will be signaled when the application is 233 // shutting down. The work item should make every effort to honor this token. 234 // If a work item does not honor this token and continues executing it will 235 // eventually be considered rogue, and the ASP.NET runtime will rudely unload 236 // the AppDomain without waiting for the work item to finish. 237 // 238 // This overload of QueueBackgroundWorkItem takes a void-returning callback; the 239 // work item will be considered finished when the callback returns. 240 [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] QueueBackgroundWorkItem(Action<CancellationToken> workItem)241 public static void QueueBackgroundWorkItem(Action<CancellationToken> workItem) { 242 if (workItem == null) { 243 throw new ArgumentNullException("workItem"); 244 } 245 246 QueueBackgroundWorkItem(ct => { workItem(ct); return _completedTask; }); 247 } 248 249 // See documentation on the other overload for a general API overview. 250 // 251 // This overload of QueueBackgroundWorkItem takes a Task-returning callback; the 252 // work item will be considered finished when the returned Task transitions to a 253 // terminal state. 254 [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem)255 public static void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem) { 256 if (workItem == null) { 257 throw new ArgumentNullException("workItem"); 258 } 259 if (Host == null) { 260 throw new InvalidOperationException(); // can only be called within an ASP.NET AppDomain 261 } 262 263 QueueBackgroundWorkItemInternal(workItem); 264 } 265 QueueBackgroundWorkItemInternal(Func<CancellationToken, Task> workItem)266 static void QueueBackgroundWorkItemInternal(Func<CancellationToken, Task> workItem) { 267 Debug.Assert(workItem != null); 268 269 BackgroundWorkScheduler scheduler = Volatile.Read(ref _backgroundWorkScheduler); 270 271 // If the scheduler doesn't exist, lazily create it, but only allow one instance to ever be published to the backing field 272 if (scheduler == null) { 273 BackgroundWorkScheduler newlyCreatedScheduler = new BackgroundWorkScheduler(UnregisterObject, WriteUnhandledException); 274 scheduler = Interlocked.CompareExchange(ref _backgroundWorkScheduler, newlyCreatedScheduler, null) ?? newlyCreatedScheduler; 275 if (scheduler == newlyCreatedScheduler) { 276 RegisterObject(scheduler); // Only call RegisterObject if we just created the "winning" one 277 } 278 } 279 280 scheduler.ScheduleWorkItem(workItem); 281 } 282 WriteUnhandledException(AppDomain appDomain, Exception exception)283 static void WriteUnhandledException (AppDomain appDomain, Exception exception) 284 { 285 Console.Error.WriteLine ("Error in background work item: " + exception); 286 } 287 } 288 } 289 290