2007-07-21 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpRuntime.cs
index e1df27aa701eace4dee5fa4c46f5232fad9e7f65..b442dd6b9a87f4da6f7c984a5164182753632bd6 100644 (file)
-/**\r
- * Namespace: System.Web.UI.Utils\r
- * Class:     HttpRuntime\r
- * \r
- * Author:  Gaurav Vaish\r
- * Contact: <gvaish@iitk.ac.in>\r
- * Status:  100%\r
- * \r
- * (C) Gaurav Vaish (2001)\r
- */\r
-\r
-using System;\r
-using System.Web.UI;\r
-\r
-namespace System.Web\r
-{\r
-       public sealed class HttpRuntime\r
-       {\r
-               internal static byte[]  autogenKeys;\r
-\r
-               private static bool        initialized;\r
-               private static string      installDir;\r
-               private static HttpRuntime runtime;\r
-               \r
-               /// <summary>\r
-               /// Loading of ISAPI\r
-               /// </summary>\r
-               private static bool        isapiLoaded;\r
-               \r
-               static HttpRuntime()\r
-               {\r
-                       autogenKeys = new byte[88];\r
-                       initialized = false;\r
-                       isapiLoaded = false;\r
-                       if(!DesignTimeParseData.InDesigner)\r
-                               Initialize();\r
-                       runtime     = new HttpRuntime();\r
-                       if(!DesignerTimeParseData.InDesigner)\r
-                               runtime.Init();\r
-               }\r
-               \r
-               internal static void Initialize()\r
-               {\r
-                       throw new NotImplementedException();\r
-               }\r
-               \r
-               private void Init()\r
-               {\r
-                       throw new NotImplementedException();\r
-               }\r
-       }\r
-}\r
+//
+// System.Web.HttpRuntime.cs 
+// 
+// Author:
+//     Miguel de Icaza (miguel@novell.com)
+//
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+//
+// TODO: Call HttpRequest.CloseInputStream when we finish a request, as we are using the IntPtr stream.
+//
+using System.IO;
+using System.Text;
+using System.Globalization;
+using System.Collections;
+using System.Reflection;
+using System.Security;
+using System.Security.Permissions;
+using System.Web.Caching;
+using System.Web.Configuration;
+using System.Web.UI;
+using System.Web.Util;
+using System.Threading;
+
+#if NET_2_0 && !TARGET_JVM
+using System.CodeDom.Compiler;
+using System.Web.Compilation;
+#endif
+
+namespace System.Web {
+       
+       // CAS - no InheritanceDemand here as the class is sealed
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       public sealed class HttpRuntime {
+#if TARGET_J2EE
+               static QueueManager queue_manager { get { return _runtime._queue_manager; } }
+               static TraceManager trace_manager { get { return _runtime._trace_manager; } }
+               static Cache cache { get { return _runtime._cache; } }
+               static Cache internalCache { get { return _runtime._internalCache; } }
+               static WaitCallback do_RealProcessRequest;
+               
+               QueueManager _queue_manager;
+               TraceManager _trace_manager;
+               Cache _cache;
+               Cache _internalCache;
+
+               static HttpRuntime ()
+               {
+                       do_RealProcessRequest = new WaitCallback (RealProcessRequest);
+               }
+
+               public HttpRuntime ()
+               {
+                       WebConfigurationManager.Init ();
+                       _queue_manager = new QueueManager ();
+                       _trace_manager = new TraceManager ();
+                       _cache = new Cache ();
+                       _internalCache = new Cache();
+               }
+
+               static private HttpRuntime _runtimeInstance {
+                       get {
+                               HttpRuntime runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
+                               if (runtime == null)
+                                       lock (typeof (HttpRuntime)) {
+                                               runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
+                                               if (runtime == null) {
+                                                       runtime = new HttpRuntime ();
+                                                       AppDomain.CurrentDomain.SetData ("HttpRuntime", runtime);
+                                               }
+                                       }
+                               return runtime;
+                       }
+               }
+               static private HttpRuntime _runtime
+               {
+                       get
+                       {
+                               if (HttpContext.Current != null)
+                                       return HttpContext.Current.HttpRuntimeInstance;
+                               else
+                                       return _runtimeInstance;
+                       }
+               }
+#else
+               static QueueManager queue_manager;
+               static TraceManager trace_manager;
+               static TimeoutManager timeout_manager;
+               static Cache cache;
+               static Cache internalCache;
+               static WaitCallback do_RealProcessRequest;
+               static Exception initialException;
+               static bool firstRun;
+               
+#if NET_2_0
+               static bool assemblyMappingEnabled;
+               static object assemblyMappingLock = new object ();
+#endif
+               
+               static HttpRuntime ()
+               {
+                       firstRun = true;
+#if NET_2_0
+                       try {
+                               WebConfigurationManager.Init ();
+                       } catch (Exception ex) {
+                               initialException = ex;
+                       }
+                       
+#endif
+
+                       // The classes in whose constructors exceptions may be thrown, should be handled the same way QueueManager
+                       // and TraceManager are below. The constructors themselves MUST NOT throw any exceptions - we MUST be sure
+                       // the objects are created here. The exceptions will be dealt with below, in RealProcessRequest.
+                       queue_manager = new QueueManager ();
+                       if (queue_manager.HasException)
+                               initialException = queue_manager.InitialException;
+
+                       trace_manager = new TraceManager ();
+                       if (trace_manager.HasException)
+                                       initialException = trace_manager.InitialException;
+
+                       timeout_manager = new TimeoutManager ();
+                       cache = new Cache ();
+                       internalCache = new Cache ();
+                       do_RealProcessRequest = new WaitCallback (RealProcessRequest);
+               }
+
+#if ONLY_1_1
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
+#endif
+               public HttpRuntime ()
+               {
+
+               }
+#endif
+               
+#region AppDomain handling
+               //
+               // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
+               //
+               public static string AppDomainAppId {
+                       [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
+                       get {
+                               //
+                               // This value should not change across invocations
+                               //
+                               string dirname = (string) AppDomain.CurrentDomain.GetData (".appId");
+                               if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
+                       }
+               }
+
+               // Physical directory for the application
+               public static string AppDomainAppPath {
+                       get {
+                               string dirname = (string) AppDomain.CurrentDomain.GetData (".appPath");
+                               if (SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
+                       }
+               }
+
+               public static string AppDomainAppVirtualPath {
+                       get {
+                               return (string) AppDomain.CurrentDomain.GetData (".appVPath");
+                       }
+               }
+
+               public static string AppDomainId {
+                       [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
+                       get {
+                               return (string) AppDomain.CurrentDomain.GetData (".domainId");
+                       }
+               }
+
+               public static string AspInstallDirectory {
+                       get {
+                               string dirname = (string) AppDomain.CurrentDomain.GetData (".hostingInstallDir");
+                               if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
+                       }
+               }
+#endregion
+               
+               public static string BinDirectory {
+                       get {
+                               string dirname = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
+                               if (SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
+                       }
+               }
+
+               public static Cache Cache {
+                       get {
+                               return cache;
+                       }
+               }
+
+               internal static Cache InternalCache {
+                       get {
+                               return internalCache;
+                       }
+               }
+               
+               public static string ClrInstallDirectory {
+                       get {
+                               string dirname = Path.GetDirectoryName (typeof (Object).Assembly.Location);
+                               if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
+                       }
+               }
+
+               public static string CodegenDir {
+                       get {
+                               string dirname = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
+                               if (SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
+                       }
+               }
+
+               [MonoTODO]
+               public static bool IsOnUNCShare {
+                       [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
+                       get {
+                               throw new NotImplementedException ();
+                       }
+               }
+
+               public static string MachineConfigurationDirectory {
+                       get {
+                               string dirname = Path.GetDirectoryName (ICalls.GetMachineConfigPath ());
+                               if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
+                       }
+               }
+
+               
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
+               public static void Close ()
+               {
+                       // Remove all items from cache.
+               }
+
+               static void QueuePendingRequests ()
+               {
+                       HttpWorkerRequest request = queue_manager.GetNextRequest (null);
+                       if (request == null)
+                               return;
+                       ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
+               }
+               
+               static void RealProcessRequest (object o)
+               {
+                       HttpContext context = new HttpContext ((HttpWorkerRequest) o);
+                       HttpContext.Current = context;
+
+                       bool error = false;
+#if !TARGET_J2EE
+                       if (firstRun) {
+                               firstRun = false;
+                               if (initialException != null) {
+                                       FinishWithException ((HttpWorkerRequest) o, new HttpException ("Initial exception", initialException));
+                                       error = true;
+                               }
+                       }
+#endif
+
+                       //
+                       // Get application instance (create or reuse an instance of the correct class)
+                       //
+                       HttpApplication app = null;
+                       if (!error) {
+                               try {
+                                       app = HttpApplicationFactory.GetApplication (context);
+                               } catch (Exception e) {
+                                       FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
+                                       error = true;
+                               }
+                       }
+                       
+                       if (error) {
+                               context.Request.ReleaseResources ();
+                               context.Response.ReleaseResources ();
+                               HttpContext.Current = null;
+                       } else {
+                               context.ApplicationInstance = app;
+                               
+                               //
+                               // Ask application to service the request
+                               //
+                               IHttpAsyncHandler ihah = app;
+
+                               IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
+                               ihah.EndProcessRequest (appiar);
+
+                               HttpApplicationFactory.Recycle (app);
+                       }
+                       
+                       QueuePendingRequests ();
+               }
+               
+               //
+               // ProcessRequest method is executed in the AppDomain of the application
+               //
+               // Observations:
+               //    ProcessRequest does not guarantee that `wr' will be processed synchronously,
+               //    the request can be queued and processed later.
+               //
+               [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
+               public static void ProcessRequest (HttpWorkerRequest wr)
+               {
+                       if (wr == null)
+                               throw new ArgumentNullException ("wr");
+                       //
+                       // Queue our request, fetch the next available one from the queue
+                       //
+                       HttpWorkerRequest request = queue_manager.GetNextRequest (wr);
+                       if (request == null)
+                               return;
+
+                       RealProcessRequest (request);
+               }
+
+               //
+               // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
+               //
+               static void request_processed (IAsyncResult iar)
+               {
+                       HttpContext context = (HttpContext) iar.AsyncState;
+
+                       context.Request.ReleaseResources ();
+                       context.Response.ReleaseResources ();
+               }
+
+               //
+               // Called when we are shutting down or we need to reload an application
+               // that has been modified (touch global.asax) 
+               //
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
+               public static void UnloadAppDomain ()
+               {
+                       //
+                       // TODO: call ReleaseResources
+                       //
+                       ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
+               }
+
+               //
+               // Shuts down the AppDomain
+               //
+               static void ShutdownAppDomain (object args)
+               {
+                       queue_manager.Dispose ();
+                       // This will call Session_End if needed.
+                       InternalCache.InvokePrivateCallbacks ();
+                       // Kill our application.
+                       HttpApplicationFactory.Dispose ();
+                       ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
+               }
+
+               static void DoUnload (object state)
+               {
+                       if (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") == null)
+                               System.Web.Hosting.ApplicationHost.ClearDynamicBaseDirectory (
+                                       AppDomain.CurrentDomain.SetupInformation.DynamicBase
+                               );
+#if TARGET_J2EE
+                       // No unload support for appdomains under Grasshopper
+#else
+                       AppDomain.Unload (AppDomain.CurrentDomain);
+#endif
+               }
+
+                static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
+                       "<html><head>\n<title>503 Server Unavailable</title>\n</head><body>\n" +
+                       "<h1>Server Unavailable</h1>\n" +
+                       "</body></html>\n";
+
+               static void FinishWithException (HttpWorkerRequest wr, HttpException e)
+               {
+                       int code = e.GetHttpCode ();
+                       wr.SendStatus (code, HttpWorkerRequest.GetStatusDescription (code));
+                       wr.SendUnknownResponseHeader ("Connection", "close");
+                       wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
+                       Encoding enc = Encoding.ASCII;
+                       wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
+                       string msg = e.GetHtmlErrorMessage ();
+                       byte [] contentBytes = enc.GetBytes (msg);
+                       wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
+                       wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
+                       wr.FlushResponse (true);
+                       wr.CloseConnection ();
+               }
+
+               //
+               // This is called from the QueueManager if a request
+               // can not be processed (load, no resources, or
+               // appdomain unload).
+               //
+               static internal void FinishUnavailable (HttpWorkerRequest wr)
+               {
+                       wr.SendStatus (503, "Service unavailable");
+                       wr.SendUnknownResponseHeader ("Connection", "close");
+                       wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
+                       Encoding enc = Encoding.ASCII;
+                       wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
+                       byte [] contentBytes = enc.GetBytes (content503);
+                       wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
+                       wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
+                       wr.FlushResponse (true);
+                       wr.CloseConnection ();
+               }
+
+#if NET_2_0 && !TARGET_J2EE
+               static internal void WritePreservationFile (Assembly asm, string genericNameBase)
+               {
+                       if (asm == null)
+                               throw new ArgumentNullException ("asm");
+                       if (String.IsNullOrEmpty (genericNameBase))
+                               throw new ArgumentNullException ("genericNameBase");
+
+                       string compiled = Path.Combine (AppDomain.CurrentDomain.SetupInformation.DynamicBase,
+                                                       genericNameBase + ".compiled");
+                       PreservationFile pf = new PreservationFile ();
+                       try {
+                               pf.VirtualPath = String.Format ("/{0}/", genericNameBase);
+
+                               AssemblyName an = asm.GetName ();
+                               pf.Assembly = an.Name;
+                               pf.ResultType = BuildResultTypeCode.TopLevelAssembly;
+                               pf.Save (compiled);
+                       } catch (Exception ex) {
+                               throw new HttpException (
+                                       String.Format ("Failed to write preservation file {0}", genericNameBase + ".compiled"),
+                                       ex);
+                       }
+               }
+               
+               static Assembly ResolveAssemblyHandler(object sender, ResolveEventArgs e)
+               {
+                       AssemblyName an = new AssemblyName (e.Name);
+                       string dynamic_base = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
+                       string compiled = Path.Combine (dynamic_base, an.Name + ".compiled");
+
+                       if (!File.Exists (compiled))
+                               return null;
+
+                       PreservationFile pf;
+                       try {
+                               pf = new PreservationFile (compiled);
+                       } catch (Exception ex) {
+                               throw new HttpException (
+                                       String.Format ("Failed to read preservation file {0}", an.Name + ".compiled"),
+                                       ex);
+                       }
+                       
+                       Assembly ret = null;
+                       try {
+                               string asmPath = Path.Combine (dynamic_base, pf.Assembly + ".dll");
+                               ret = Assembly.LoadFrom (asmPath);
+                       } catch (Exception) {
+                               // ignore
+                       }
+                       
+                       return ret;
+               }
+               
+               internal static void EnableAssemblyMapping (bool enable)
+               {
+                       lock (assemblyMappingLock) {
+                               if (assemblyMappingEnabled == enable)
+                                       return;
+                               if (enable)
+                                       AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (ResolveAssemblyHandler);
+                               else
+                                       AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler (ResolveAssemblyHandler);
+                       }
+               }
+#endif
+               
+               internal static TraceManager TraceManager {
+                       get {
+                               return trace_manager;
+                       }
+               }
+
+#if !TARGET_JVM
+               internal static TimeoutManager TimeoutManager {
+                       get {
+                               return timeout_manager;
+                       }
+               }
+#endif
+       }
+}