2 // System.Web.HttpRuntime.cs
5 // Miguel de Icaza (miguel@novell.com)
8 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 // TODO: Call HttpRequest.CloseInputStream when we finish a request, as we are using the IntPtr stream.
35 using System.Globalization;
36 using System.Collections;
37 using System.Reflection;
38 using System.Security;
39 using System.Security.Permissions;
40 using System.Web.Caching;
41 using System.Web.Configuration;
43 using System.Web.Util;
44 using System.Threading;
46 #if NET_2_0 && !TARGET_JVM
47 using System.CodeDom.Compiler;
48 using System.Web.Compilation;
51 namespace System.Web {
53 // CAS - no InheritanceDemand here as the class is sealed
54 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
55 public sealed class HttpRuntime {
57 static QueueManager queue_manager { get { return _runtime._queue_manager; } }
58 static TraceManager trace_manager { get { return _runtime._trace_manager; } }
59 static Cache cache { get { return _runtime._cache; } }
60 static Cache internalCache { get { return _runtime._internalCache; } }
61 static WaitCallback do_RealProcessRequest;
63 QueueManager _queue_manager;
64 TraceManager _trace_manager;
70 do_RealProcessRequest = new WaitCallback (RealProcessRequest);
75 WebConfigurationManager.Init ();
76 _queue_manager = new QueueManager ();
77 _trace_manager = new TraceManager ();
78 _cache = new Cache ();
79 _internalCache = new Cache();
82 static private HttpRuntime _runtimeInstance {
84 HttpRuntime runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
86 lock (typeof (HttpRuntime)) {
87 runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
88 if (runtime == null) {
89 runtime = new HttpRuntime ();
90 AppDomain.CurrentDomain.SetData ("HttpRuntime", runtime);
96 static private HttpRuntime _runtime
100 if (HttpContext.Current != null)
101 return HttpContext.Current.HttpRuntimeInstance;
103 return _runtimeInstance;
107 static QueueManager queue_manager;
108 static TraceManager trace_manager;
109 static TimeoutManager timeout_manager;
111 static Cache internalCache;
112 static WaitCallback do_RealProcessRequest;
113 static Exception initialException;
114 static bool firstRun;
117 static bool assemblyMappingEnabled;
118 static object assemblyMappingLock = new object ();
119 static object appOfflineLock = new object ();
122 static HttpRuntime ()
127 WebConfigurationManager.Init ();
128 } catch (Exception ex) {
129 initialException = ex;
134 // The classes in whose constructors exceptions may be thrown, should be handled the same way QueueManager
135 // and TraceManager are below. The constructors themselves MUST NOT throw any exceptions - we MUST be sure
136 // the objects are created here. The exceptions will be dealt with below, in RealProcessRequest.
137 queue_manager = new QueueManager ();
138 if (queue_manager.HasException)
139 initialException = queue_manager.InitialException;
141 trace_manager = new TraceManager ();
142 if (trace_manager.HasException)
143 initialException = trace_manager.InitialException;
145 timeout_manager = new TimeoutManager ();
146 cache = new Cache ();
147 internalCache = new Cache ();
148 do_RealProcessRequest = new WaitCallback (RealProcessRequest);
152 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
154 public HttpRuntime ()
160 #region AppDomain handling
162 // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
164 public static string AppDomainAppId {
165 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
168 // This value should not change across invocations
170 string dirname = (string) AppDomain.CurrentDomain.GetData (".appId");
171 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
172 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
178 // Physical directory for the application
179 public static string AppDomainAppPath {
181 string dirname = (string) AppDomain.CurrentDomain.GetData (".appPath");
182 if (SecurityManager.SecurityEnabled) {
183 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
189 public static string AppDomainAppVirtualPath {
191 return (string) AppDomain.CurrentDomain.GetData (".appVPath");
195 public static string AppDomainId {
196 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
198 return (string) AppDomain.CurrentDomain.GetData (".domainId");
202 public static string AspInstallDirectory {
204 string dirname = (string) AppDomain.CurrentDomain.GetData (".hostingInstallDir");
205 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
206 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
213 public static string BinDirectory {
215 string dirname = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
216 if (SecurityManager.SecurityEnabled) {
217 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
223 public static Cache Cache {
229 internal static Cache InternalCache {
231 return internalCache;
235 public static string ClrInstallDirectory {
237 string dirname = Path.GetDirectoryName (typeof (Object).Assembly.Location);
238 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
239 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
245 public static string CodegenDir {
247 string dirname = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
248 if (SecurityManager.SecurityEnabled) {
249 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
256 public static bool IsOnUNCShare {
257 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
259 throw new NotImplementedException ();
263 public static string MachineConfigurationDirectory {
265 string dirname = Path.GetDirectoryName (ICalls.GetMachineConfigPath ());
266 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
267 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
274 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
275 public static void Close ()
277 // Remove all items from cache.
280 static void QueuePendingRequests ()
282 HttpWorkerRequest request = queue_manager.GetNextRequest (null);
285 ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
289 static readonly string[] app_offline_files = {"app_offline.htm", "App_Offline.htm", "APP_OFFLINE.HTM"};
290 static FileSystemWatcher app_offline_watcher;
292 static void AppOfflineFileRenamed (object sender, RenamedEventArgs args)
294 AppOfflineFileChanged (sender, args);
297 static void AppOfflineFileChanged (object sender, FileSystemEventArgs args)
299 lock (appOfflineLock) {
300 HttpApplicationFactory.DisableWatchers ();
301 app_offline_watcher.EnableRaisingEvents = false;
303 // Restart application
304 HttpRuntime.UnloadAppDomain();
308 static bool AppIsOffline (HttpContext context)
310 string app_path = context.Request.PhysicalApplicationPath;
311 string aof_path = null;
312 bool haveOfflineFile = false;
314 foreach (string aof in app_offline_files) {
315 aof_path = Path.Combine (app_path, aof);
316 if (File.Exists (aof_path)) {
317 haveOfflineFile = true;
322 lock (appOfflineLock) {
323 if (!haveOfflineFile) {
324 if (HttpApplicationFactory.ApplicationDisabled) {
325 HttpApplicationFactory.ApplicationDisabled = false;
326 HttpApplicationFactory.EnableWatchers ();
332 HttpApplicationFactory.ApplicationDisabled = true;
333 HttpApplicationFactory.DisableWatchers ();
335 FileSystemEventHandler seh = new FileSystemEventHandler (AppOfflineFileChanged);
336 RenamedEventHandler reh = new RenamedEventHandler (AppOfflineFileRenamed);
338 app_offline_watcher = new FileSystemWatcher ();
339 app_offline_watcher.Path = Path.GetDirectoryName (aof_path);
340 app_offline_watcher.Filter = Path.GetFileName (aof_path);
341 app_offline_watcher.NotifyFilter |= NotifyFilters.Size;
342 app_offline_watcher.Deleted += seh;
343 app_offline_watcher.Changed += seh;
344 app_offline_watcher.Renamed += reh;
345 app_offline_watcher.EnableRaisingEvents = true;
348 HttpResponse response = context.Response;
350 response.ContentType = "text/html";
351 response.ExpiresAbsolute = DateTime.UtcNow;
352 response.TransmitFile (aof_path, true);
354 context.Request.ReleaseResources ();
355 context.Response.ReleaseResources ();
356 HttpContext.Current = null;
362 static void RealProcessRequest (object o)
364 HttpContext context = new HttpContext ((HttpWorkerRequest) o);
365 HttpContext.Current = context;
371 if (initialException != null) {
372 FinishWithException ((HttpWorkerRequest) o, new HttpException ("Initial exception", initialException));
379 if (AppIsOffline (context))
384 // Get application instance (create or reuse an instance of the correct class)
386 HttpApplication app = null;
389 app = HttpApplicationFactory.GetApplication (context);
390 } catch (Exception e) {
391 FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
397 context.Request.ReleaseResources ();
398 context.Response.ReleaseResources ();
399 HttpContext.Current = null;
401 context.ApplicationInstance = app;
404 // Ask application to service the request
406 IHttpAsyncHandler ihah = app;
408 IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
409 ihah.EndProcessRequest (appiar);
411 HttpApplicationFactory.Recycle (app);
414 QueuePendingRequests ();
418 // ProcessRequest method is executed in the AppDomain of the application
421 // ProcessRequest does not guarantee that `wr' will be processed synchronously,
422 // the request can be queued and processed later.
424 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
425 public static void ProcessRequest (HttpWorkerRequest wr)
428 throw new ArgumentNullException ("wr");
430 // Queue our request, fetch the next available one from the queue
432 HttpWorkerRequest request = queue_manager.GetNextRequest (wr);
436 RealProcessRequest (request);
440 // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
442 static void request_processed (IAsyncResult iar)
444 HttpContext context = (HttpContext) iar.AsyncState;
446 context.Request.ReleaseResources ();
447 context.Response.ReleaseResources ();
451 // Called when we are shutting down or we need to reload an application
452 // that has been modified (touch global.asax)
454 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
455 public static void UnloadAppDomain ()
458 // TODO: call ReleaseResources
460 ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
464 // Shuts down the AppDomain
466 static void ShutdownAppDomain (object args)
468 queue_manager.Dispose ();
469 // This will call Session_End if needed.
470 InternalCache.InvokePrivateCallbacks ();
471 // Kill our application.
472 HttpApplicationFactory.Dispose ();
473 ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
476 static void DoUnload (object state)
478 if (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") == null)
479 System.Web.Hosting.ApplicationHost.ClearDynamicBaseDirectory (
480 AppDomain.CurrentDomain.SetupInformation.DynamicBase
483 // No unload support for appdomains under Grasshopper
485 AppDomain.Unload (AppDomain.CurrentDomain);
489 static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
490 "<html><head>\n<title>503 Server Unavailable</title>\n</head><body>\n" +
491 "<h1>Server Unavailable</h1>\n" +
494 static void FinishWithException (HttpWorkerRequest wr, HttpException e)
496 int code = e.GetHttpCode ();
497 wr.SendStatus (code, HttpWorkerRequest.GetStatusDescription (code));
498 wr.SendUnknownResponseHeader ("Connection", "close");
499 wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
500 Encoding enc = Encoding.ASCII;
501 wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
502 string msg = e.GetHtmlErrorMessage ();
503 byte [] contentBytes = enc.GetBytes (msg);
504 wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
505 wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
506 wr.FlushResponse (true);
507 wr.CloseConnection ();
511 // This is called from the QueueManager if a request
512 // can not be processed (load, no resources, or
513 // appdomain unload).
515 static internal void FinishUnavailable (HttpWorkerRequest wr)
517 wr.SendStatus (503, "Service unavailable");
518 wr.SendUnknownResponseHeader ("Connection", "close");
519 wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
520 Encoding enc = Encoding.ASCII;
521 wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
522 byte [] contentBytes = enc.GetBytes (content503);
523 wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
524 wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
525 wr.FlushResponse (true);
526 wr.CloseConnection ();
529 #if NET_2_0 && !TARGET_J2EE
530 static internal void WritePreservationFile (Assembly asm, string genericNameBase)
533 throw new ArgumentNullException ("asm");
534 if (String.IsNullOrEmpty (genericNameBase))
535 throw new ArgumentNullException ("genericNameBase");
537 string compiled = Path.Combine (AppDomain.CurrentDomain.SetupInformation.DynamicBase,
538 genericNameBase + ".compiled");
539 PreservationFile pf = new PreservationFile ();
541 pf.VirtualPath = String.Format ("/{0}/", genericNameBase);
543 AssemblyName an = asm.GetName ();
544 pf.Assembly = an.Name;
545 pf.ResultType = BuildResultTypeCode.TopLevelAssembly;
547 } catch (Exception ex) {
548 throw new HttpException (
549 String.Format ("Failed to write preservation file {0}", genericNameBase + ".compiled"),
554 static Assembly ResolveAssemblyHandler(object sender, ResolveEventArgs e)
556 AssemblyName an = new AssemblyName (e.Name);
557 string dynamic_base = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
558 string compiled = Path.Combine (dynamic_base, an.Name + ".compiled");
560 if (!File.Exists (compiled))
565 pf = new PreservationFile (compiled);
566 } catch (Exception ex) {
567 throw new HttpException (
568 String.Format ("Failed to read preservation file {0}", an.Name + ".compiled"),
574 string asmPath = Path.Combine (dynamic_base, pf.Assembly + ".dll");
575 ret = Assembly.LoadFrom (asmPath);
576 } catch (Exception) {
583 internal static void EnableAssemblyMapping (bool enable)
585 lock (assemblyMappingLock) {
586 if (assemblyMappingEnabled == enable)
589 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (ResolveAssemblyHandler);
591 AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler (ResolveAssemblyHandler);
596 internal static TraceManager TraceManager {
598 return trace_manager;
603 internal static TimeoutManager TimeoutManager {
605 return timeout_manager;