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.Threading;
45 #if NET_2_0 && !TARGET_JVM
46 using System.CodeDom.Compiler;
47 using System.Web.Compilation;
50 namespace System.Web {
52 // CAS - no InheritanceDemand here as the class is sealed
53 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
54 public sealed class HttpRuntime {
56 static QueueManager queue_manager { get { return _runtime._queue_manager; } }
57 static TraceManager trace_manager { get { return _runtime._trace_manager; } }
58 static Cache cache { get { return _runtime._cache; } }
59 static WaitCallback do_RealProcessRequest;
61 QueueManager _queue_manager;
62 TraceManager _trace_manager;
67 do_RealProcessRequest = new WaitCallback (RealProcessRequest);
72 WebConfigurationManager.Init ();
73 _queue_manager = new QueueManager ();
74 _trace_manager = new TraceManager ();
75 _cache = new Cache ();
78 static private HttpRuntime _runtimeInstance {
80 HttpRuntime runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
82 lock (typeof (HttpRuntime)) {
83 runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
84 if (runtime == null) {
85 runtime = new HttpRuntime ();
86 AppDomain.CurrentDomain.SetData ("HttpRuntime", runtime);
92 static private HttpRuntime _runtime
96 if (HttpContext.Current != null)
97 return HttpContext.Current.HttpRuntimeInstance;
99 return _runtimeInstance;
103 static QueueManager queue_manager;
104 static TraceManager trace_manager;
105 static TimeoutManager timeout_manager;
107 static WaitCallback do_RealProcessRequest;
110 static bool assemblyMappingEnabled;
111 static object assemblyMappingLock = new object ();
114 static HttpRuntime ()
117 WebConfigurationManager.Init ();
119 queue_manager = new QueueManager ();
120 trace_manager = new TraceManager ();
121 timeout_manager = new TimeoutManager ();
122 cache = new Cache ();
123 do_RealProcessRequest = new WaitCallback (RealProcessRequest);
127 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
129 public HttpRuntime ()
134 #region AppDomain handling
136 // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
138 public static string AppDomainAppId {
139 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
142 // This value should not change across invocations
144 string dirname = (string) AppDomain.CurrentDomain.GetData (".appId");
145 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
146 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
152 // Physical directory for the application
153 public static string AppDomainAppPath {
155 string dirname = (string) AppDomain.CurrentDomain.GetData (".appPath");
156 if (SecurityManager.SecurityEnabled) {
157 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
163 public static string AppDomainAppVirtualPath {
165 return (string) AppDomain.CurrentDomain.GetData (".appVPath");
169 public static string AppDomainId {
170 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
172 return (string) AppDomain.CurrentDomain.GetData (".domainId");
176 public static string AspInstallDirectory {
178 string dirname = (string) AppDomain.CurrentDomain.GetData (".hostingInstallDir");
179 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
180 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
187 public static string BinDirectory {
189 string dirname = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
190 if (SecurityManager.SecurityEnabled) {
191 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
197 public static Cache Cache {
203 public static string ClrInstallDirectory {
205 string dirname = Path.GetDirectoryName (typeof (Object).Assembly.Location);
206 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
207 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
213 public static string CodegenDir {
215 string dirname = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
216 if (SecurityManager.SecurityEnabled) {
217 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
224 public static bool IsOnUNCShare {
225 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
227 throw new NotImplementedException ();
231 public static string MachineConfigurationDirectory {
234 string dirname = Path.GetDirectoryName (WebConfigurationManager.OpenMachineConfiguration().FilePath);
236 string dirname = Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
238 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
239 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
245 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
246 public static void Close ()
248 // Remove all items from cache.
251 static void QueuePendingRequests ()
253 HttpWorkerRequest request = queue_manager.GetNextRequest (null);
256 ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
259 static void RealProcessRequest (object o)
261 HttpContext context = new HttpContext ((HttpWorkerRequest) o);
262 HttpContext.Current = context;
265 // Get application instance (create or reuse an instance of the correct class)
267 HttpApplication app = null;
270 app = HttpApplicationFactory.GetApplication (context);
271 } catch (Exception e) {
272 FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
277 context.Request.ReleaseResources ();
278 context.Response.ReleaseResources ();
279 HttpContext.Current = null;
281 context.ApplicationInstance = app;
284 // Ask application to service the request
286 IHttpAsyncHandler ihah = app;
288 IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
289 ihah.EndProcessRequest (appiar);
291 HttpApplicationFactory.Recycle (app);
294 QueuePendingRequests ();
298 // ProcessRequest method is executed in the AppDomain of the application
301 // ProcessRequest does not guarantee that `wr' will be processed synchronously,
302 // the request can be queued and processed later.
304 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
305 public static void ProcessRequest (HttpWorkerRequest wr)
308 throw new ArgumentNullException ("wr");
310 // Queue our request, fetch the next available one from the queue
312 HttpWorkerRequest request = queue_manager.GetNextRequest (wr);
316 RealProcessRequest (request);
320 // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
322 static void request_processed (IAsyncResult iar)
324 HttpContext context = (HttpContext) iar.AsyncState;
326 context.Request.ReleaseResources ();
327 context.Response.ReleaseResources ();
331 // Called when we are shutting down or we need to reload an application
332 // that has been modified (touch global.asax)
334 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
335 public static void UnloadAppDomain ()
338 // TODO: call ReleaseResources
340 ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
344 // Shuts down the AppDomain
346 static void ShutdownAppDomain (object args)
348 queue_manager.Dispose ();
349 // This will call Session_End if needed.
350 Cache.InvokePrivateCallbacks ();
351 // Kill our application.
352 HttpApplicationFactory.Dispose ();
353 ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
356 #if TARGET_J2EE // No unload support for appdomains under Grasshopper
357 static void DoUnload (object state)
361 static void DoUnload (object state)
363 AppDomain.Unload (AppDomain.CurrentDomain);
367 static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
368 "<html><head>\n<title>503 Server Unavailable</title>\n</head><body>\n" +
369 "<h1>Server Unavailable</h1>\n" +
372 static void FinishWithException (HttpWorkerRequest wr, HttpException e)
374 int code = e.GetHttpCode ();
375 wr.SendStatus (code, HttpWorkerRequest.GetStatusDescription (code));
376 wr.SendUnknownResponseHeader ("Connection", "close");
377 wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
378 Encoding enc = Encoding.ASCII;
379 wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
380 string msg = e.GetHtmlErrorMessage ();
381 byte [] contentBytes = enc.GetBytes (msg);
382 wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
383 wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
384 wr.FlushResponse (true);
385 wr.CloseConnection ();
389 // This is called from the QueueManager if a request
390 // can not be processed (load, no resources, or
391 // appdomain unload).
393 static internal void FinishUnavailable (HttpWorkerRequest wr)
395 wr.SendStatus (503, "Service unavailable");
396 wr.SendUnknownResponseHeader ("Connection", "close");
397 wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
398 Encoding enc = Encoding.ASCII;
399 wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
400 byte [] contentBytes = enc.GetBytes (content503);
401 wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
402 wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
403 wr.FlushResponse (true);
404 wr.CloseConnection ();
407 #if NET_2_0 && !TARGET_J2EE
408 static internal void WritePreservationFile (Assembly asm, string genericNameBase)
411 throw new ArgumentNullException ("asm");
412 if (String.IsNullOrEmpty (genericNameBase))
413 throw new ArgumentNullException ("genericNameBase");
415 string compiled = Path.Combine (AppDomain.CurrentDomain.SetupInformation.DynamicBase,
416 genericNameBase + ".compiled");
417 PreservationFile pf = new PreservationFile ();
419 pf.VirtualPath = String.Format ("/{0}/", genericNameBase);
421 AssemblyName an = asm.GetName ();
422 pf.Assembly = an.Name;
423 pf.ResultType = BuildResultTypeCode.TopLevelAssembly;
425 } catch (Exception ex) {
426 throw new HttpException (
427 String.Format ("Failed to write preservation file {0}", genericNameBase + ".compiled"),
432 static Assembly ResolveAssemblyHandler(object sender, ResolveEventArgs e)
434 AssemblyName an = new AssemblyName (e.Name);
435 string dynamic_base = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
436 string compiled = Path.Combine (dynamic_base, an.Name + ".compiled");
438 if (!File.Exists (compiled))
443 pf = new PreservationFile (compiled);
444 } catch (Exception ex) {
445 throw new HttpException (
446 String.Format ("Failed to read preservation file {0}", an.Name + ".compiled"),
452 string asmPath = Path.Combine (dynamic_base, pf.Assembly + ".dll");
453 ret = Assembly.LoadFrom (asmPath);
454 } catch (Exception) {
461 internal static void EnableAssemblyMapping (bool enable)
463 lock (assemblyMappingLock) {
464 if (assemblyMappingEnabled == enable)
467 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (ResolveAssemblyHandler);
469 AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler (ResolveAssemblyHandler);
474 internal static TraceManager TraceManager {
476 return trace_manager;
481 internal static TimeoutManager TimeoutManager {
483 return timeout_manager;