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 _runtime {
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);
93 static QueueManager queue_manager;
94 static TraceManager trace_manager;
95 static TimeoutManager timeout_manager;
97 static WaitCallback do_RealProcessRequest;
100 static bool assemblyMappingEnabled;
101 static object assemblyMappingLock = new object ();
104 static HttpRuntime ()
107 WebConfigurationManager.Init ();
109 queue_manager = new QueueManager ();
110 trace_manager = new TraceManager ();
111 timeout_manager = new TimeoutManager ();
112 cache = new Cache ();
113 do_RealProcessRequest = new WaitCallback (RealProcessRequest);
117 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
119 public HttpRuntime ()
124 #region AppDomain handling
126 // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
128 public static string AppDomainAppId {
129 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
132 // This value should not change across invocations
134 string dirname = (string) AppDomain.CurrentDomain.GetData (".appId");
135 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
136 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
142 // Physical directory for the application
143 public static string AppDomainAppPath {
145 string dirname = (string) AppDomain.CurrentDomain.GetData (".appPath");
146 if (SecurityManager.SecurityEnabled) {
147 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
153 public static string AppDomainAppVirtualPath {
155 return (string) AppDomain.CurrentDomain.GetData (".appVPath");
159 public static string AppDomainId {
160 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
162 return (string) AppDomain.CurrentDomain.GetData (".domainId");
166 public static string AspInstallDirectory {
168 string dirname = (string) AppDomain.CurrentDomain.GetData (".hostingInstallDir");
169 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
170 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
177 public static string BinDirectory {
179 string dirname = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
180 if (SecurityManager.SecurityEnabled) {
181 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
187 public static Cache Cache {
193 public static string ClrInstallDirectory {
195 string dirname = Path.GetDirectoryName (typeof (Object).Assembly.Location);
196 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
197 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
203 public static string CodegenDir {
205 string dirname = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
206 if (SecurityManager.SecurityEnabled) {
207 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
214 public static bool IsOnUNCShare {
215 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
217 throw new NotImplementedException ();
221 public static string MachineConfigurationDirectory {
224 string dirname = Path.GetDirectoryName (WebConfigurationManager.OpenMachineConfiguration().FilePath);
226 string dirname = Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
228 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
229 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
235 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
236 public static void Close ()
238 // Remove all items from cache.
241 static void QueuePendingRequests ()
243 HttpWorkerRequest request = queue_manager.GetNextRequest (null);
246 ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
249 static void RealProcessRequest (object o)
251 HttpContext context = new HttpContext ((HttpWorkerRequest) o);
252 HttpContext.Current = context;
255 // Get application instance (create or reuse an instance of the correct class)
257 HttpApplication app = null;
260 app = HttpApplicationFactory.GetApplication (context);
261 } catch (Exception e) {
262 FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
267 context.Request.ReleaseResources ();
268 context.Response.ReleaseResources ();
269 HttpContext.Current = null;
271 context.ApplicationInstance = app;
273 #if NET_2_0 && !TARGET_JVM
275 // Compile the local resources, if any
277 AppResourcesCompiler ac = new AppResourcesCompiler (context, false);
282 // Ask application to service the request
284 IHttpAsyncHandler ihah = app;
286 IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
287 ihah.EndProcessRequest (appiar);
289 HttpApplicationFactory.Recycle (app);
292 QueuePendingRequests ();
296 // ProcessRequest method is executed in the AppDomain of the application
299 // ProcessRequest does not guarantee that `wr' will be processed synchronously,
300 // the request can be queued and processed later.
302 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
303 public static void ProcessRequest (HttpWorkerRequest wr)
306 throw new ArgumentNullException ("wr");
308 // Queue our request, fetch the next available one from the queue
310 HttpWorkerRequest request = queue_manager.GetNextRequest (wr);
314 RealProcessRequest (request);
318 // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
320 static void request_processed (IAsyncResult iar)
322 HttpContext context = (HttpContext) iar.AsyncState;
324 context.Request.ReleaseResources ();
325 context.Response.ReleaseResources ();
329 // Called when we are shutting down or we need to reload an application
330 // that has been modified (touch global.asax)
332 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
333 public static void UnloadAppDomain ()
336 // TODO: call ReleaseResources
338 ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
342 // Shuts down the AppDomain
344 static void ShutdownAppDomain (object args)
346 queue_manager.Dispose ();
347 // This will call Session_End if needed.
348 Cache.InvokePrivateCallbacks ();
349 // Kill our application.
350 HttpApplicationFactory.Dispose ();
351 ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
354 #if TARGET_J2EE // No unload support for appdomains under Grasshopper
355 static void DoUnload (object state)
359 static void DoUnload (object state)
361 AppDomain.Unload (AppDomain.CurrentDomain);
365 static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
366 "<html><head>\n<title>503 Server Unavailable</title>\n</head><body>\n" +
367 "<h1>Server Unavailable</h1>\n" +
370 static void FinishWithException (HttpWorkerRequest wr, HttpException e)
372 int code = e.GetHttpCode ();
373 wr.SendStatus (code, HttpWorkerRequest.GetStatusDescription (code));
374 wr.SendUnknownResponseHeader ("Connection", "close");
375 wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
376 Encoding enc = Encoding.ASCII;
377 wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
378 string msg = e.GetHtmlErrorMessage ();
379 byte [] contentBytes = enc.GetBytes (msg);
380 wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
381 wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
382 wr.FlushResponse (true);
383 wr.CloseConnection ();
387 // This is called from the QueueManager if a request
388 // can not be processed (load, no resources, or
389 // appdomain unload).
391 static internal void FinishUnavailable (HttpWorkerRequest wr)
393 wr.SendStatus (503, "Service unavailable");
394 wr.SendUnknownResponseHeader ("Connection", "close");
395 wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
396 Encoding enc = Encoding.ASCII;
397 wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
398 byte [] contentBytes = enc.GetBytes (content503);
399 wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
400 wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
401 wr.FlushResponse (true);
402 wr.CloseConnection ();
405 #if NET_2_0 && !TARGET_J2EE
406 static internal void WritePreservationFile (Assembly asm, string genericNameBase)
409 throw new ArgumentNullException ("asm");
410 if (String.IsNullOrEmpty (genericNameBase))
411 throw new ArgumentNullException ("genericNameBase");
413 string compiled = Path.Combine (AppDomain.CurrentDomain.SetupInformation.DynamicBase,
414 genericNameBase + ".compiled");
415 PreservationFile pf = new PreservationFile ();
417 pf.VirtualPath = String.Format ("/{0}/", genericNameBase);
419 AssemblyName an = asm.GetName ();
420 pf.Assembly = an.Name;
421 pf.ResultType = BuildResultTypeCode.TopLevelAssembly;
423 } catch (Exception ex) {
424 throw new HttpException (
425 String.Format ("Failed to write preservation file {0}", genericNameBase + ".compiled"),
430 static Assembly ResolveAssemblyHandler(object sender, ResolveEventArgs e)
432 AssemblyName an = new AssemblyName (e.Name);
433 string dynamic_base = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
434 string compiled = Path.Combine (dynamic_base, an.Name + ".compiled");
436 if (!File.Exists (compiled))
441 pf = new PreservationFile (compiled);
442 } catch (Exception ex) {
443 throw new HttpException (
444 String.Format ("Failed to read preservation file {0}", an.Name + ".compiled"),
450 string asmPath = Path.Combine (dynamic_base, pf.Assembly + ".dll");
451 ret = Assembly.LoadFrom (asmPath);
452 } catch (Exception) {
459 internal static void EnableAssemblyMapping (bool enable)
461 lock (assemblyMappingLock) {
462 if (assemblyMappingEnabled == enable)
465 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (ResolveAssemblyHandler);
467 AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler (ResolveAssemblyHandler);
472 internal static TraceManager TraceManager {
474 return trace_manager;
479 internal static TimeoutManager TimeoutManager {
481 return timeout_manager;