2007-07-21 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpRuntime.cs
index d2daacd6c08fadd1e920ff114ab2a4606dacd3ad..b442dd6b9a87f4da6f7c984a5164182753632bd6 100644 (file)
@@ -1,12 +1,11 @@
+//
+// System.Web.HttpRuntime.cs 
 // 
-// System.Web.HttpRuntime
+// Author:
+//     Miguel de Icaza (miguel@novell.com)
 //
-// Authors:
-//     Patrik Torstensson (ptorsten@hotmail.com)
-//     Gaurav Vaish (gvaish@iitk.ac.in)
-//     Gonzalo Paniagua Javier (gonzalo@ximian.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
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-using System;
-using System.Collections;
+
+//
+// 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.Threading;
+using System.Web.Caching;
 using System.Web.Configuration;
 using System.Web.UI;
 using System.Web.Util;
-using System.Web.Caching;
+using System.Threading;
 
-namespace System.Web {
+#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 {
-
-               // Security permission helper objects
-               private static IStackWalk appPathDiscoveryStackWalk;
-               private static IStackWalk ctrlPrincipalStackWalk;
-               private static IStackWalk sensitiveInfoStackWalk;
-               private static IStackWalk unmgdCodeStackWalk;
-               private static IStackWalk unrestrictedStackWalk;
-               private static IStackWalk reflectionStackWalk;
-
-               private static HttpRuntime _runtime;
-               private static string appDomainAppId;
-               private static string appDomainId;
-               private static string appDomainAppPath;
-               private static string appDomainAppVirtualPath;
-               private Cache _cache;
-
-               private int _activeRequests;
-               private HttpWorkerRequest.EndOfSendNotification _endOfSendCallback;
-               private AsyncCallback _handlerCallback;
-               private WaitCallback unloadDomainCallback;
-
-               private bool _firstRequestStarted;
-               //private bool _firstRequestExecuted;
-
-               private Exception _initError;
-               private TimeoutManager timeoutManager;
-               private QueueManager queueManager;
-               private TraceManager traceManager;
-               private WaitCallback doRequestCallback;
-               private int pendingCallbacks;
+#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 ()
                {
-                       _runtime = new HttpRuntime ();
-                       _runtime.Init();
+                       do_RealProcessRequest = new WaitCallback (RealProcessRequest);
                }
 
                public HttpRuntime ()
                {
-                       doRequestCallback = new WaitCallback (DoRequest);
-               }
-
-               static internal object CreateInternalObject(Type type) {
-                       return Activator.CreateInstance(type, true);
-               }
-
-               private void Init ()
-               {
-                       try {
-                               _cache = new Cache ();
-                               timeoutManager = new TimeoutManager ();
-
-                               _endOfSendCallback = new HttpWorkerRequest.EndOfSendNotification (OnEndOfSend);
-                               _handlerCallback = new AsyncCallback (OnHandlerReady);
-                               unloadDomainCallback = new WaitCallback (DoUnload);
-                               AppDomain.CurrentDomain.DomainUnload += new EventHandler (OnDomainUnload);
-                       } 
-                       catch (Exception error) {
-                               _initError = error;
-                       }
-               }
-
-               private void OnFirstRequestStart(HttpContext context) {
-                       if (_initError != null)
-                               throw _initError;
-
-                       try {
-                               WebConfigurationSettings.Init (context);
-                               traceManager = new TraceManager ();
-                               queueManager = new QueueManager ();
-                       } catch (Exception e) {
-                               _initError = e;
-                       }
-
-                       // If we got an error during init, throw to client now..
-                       if (null != _initError)
-                               throw _initError;
+                       WebConfigurationManager.Init ();
+                       _queue_manager = new QueueManager ();
+                       _trace_manager = new TraceManager ();
+                       _cache = new Cache ();
+                       _internalCache = new Cache();
                }
 
-               /* Not used
-               private void OnFirstRequestEnd() {
-               }
-               */
-
-               private void OnHandlerReady(IAsyncResult ar) {
-                       HttpContext context = (HttpContext) ar.AsyncState;
-                       try {
-                               IHttpAsyncHandler handler = context.AsyncHandler;
-
-                               try {
-                                       handler.EndProcessRequest(ar);
-                               }
-                               catch (Exception error) {
-                                       context.AddError(error);
-                               }
-                       }
-                       finally {
-                               context.AsyncHandler = null;
-                       }
-
-                       FinishRequest(context, context.Error);
-               }
-
-               private void OnEndOfSend(HttpWorkerRequest request, object data) {
-                       HttpContext context = (HttpContext) data;
-
-                       context.Request.Dispose();
-                       context.Response.Dispose();
-               }
-
-               internal void FinishRequest(HttpContext context, Exception error) {
-                       if (error == null) {
-                               try {
-                                       context.Response.FlushAtEndOfRequest();
-                               } catch (Exception obj) {
-                                       error = obj;
-                               }
-                       }
-
-                       HttpWorkerRequest request = context.WorkerRequest;
-                       if (null != error) {
-                               WebTrace.WriteLine (error.ToString ());
-
-                               // We don't want the 'headers sent error'
-                               context.Response.SetHeadersSent (false);
-                               context.Response.Clear ();
-                               context.Response.ClearHeaders ();
-
-                               if (!(error is HttpException)) {
-                                       error = new HttpException (String.Empty, error);
-                                       context.Response.StatusCode = 500;
-                               } else {
-                                       context.Response.StatusCode = ((HttpException) error).GetHttpCode ();
-                               }
-
-                               if (!RedirectCustomError (context))
-                                       context.Response.Write (((HttpException) error).GetHtmlErrorMessage ());
-
-                               context.Response.FinalFlush ();
-                       }
-
-                       /*
-                        * This is not being used. OnFirstRequestEnd is empty.
-                       if (!_firstRequestExecuted) {
-                               lock (this) {
-                                       if (!_firstRequestExecuted) {
-                                               _firstRequestExecuted = true;
-                                               OnFirstRequestEnd();
+               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;
                        }
-                       */
-
-                       Interlocked.Decrement(ref _activeRequests);
-
-                       if (null != request)
-                               request.EndOfRequest();
-
-                       TryExecuteQueuedRequests ();
                }
-
-               bool RedirectCustomError (HttpContext context)
+               static private HttpRuntime _runtime
                {
-                       if (!context.IsCustomErrorEnabled)
-                               return false;
-
-                       CustomErrorsConfig config = null;
-                       try {
-                               config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
-                       } catch { }
-
-                       if (config == null) {
-                               if (context.ErrorPage != null)
-                                       return context.Response.RedirectCustomError (context.ErrorPage);
-
-                               return false;
+                       get
+                       {
+                               if (HttpContext.Current != null)
+                                       return HttpContext.Current.HttpRuntimeInstance;
+                               else
+                                       return _runtimeInstance;
                        }
-
-                       string redirect =  config [context.Response.StatusCode];
-                       if (redirect == null) {
-                               redirect = context.ErrorPage;
-                               if (redirect == null)
-                                       redirect = config.DefaultRedirect;
-                       }
-
-                       if (redirect == null)
-                               return false;
-
-                       return context.Response.RedirectCustomError (redirect);
-               }
-
-               internal static void FinishUnavailable (HttpWorkerRequest wr)
-               {
-                       HttpContext context = new HttpContext (wr);
-                       HttpException exception = new HttpException (503, "Service unavailable");
-                       Interlocked.Increment (ref _runtime._activeRequests);
-                       context.Response.InitializeWriter ();
-                       _runtime.FinishRequest (context, exception);
-               }
-
-               void DoUnload (object state)
-               {
-                       try { 
-                               AppDomain.Unload (AppDomain.CurrentDomain);
-                       } catch {}
-               }
-
-               internal void Dispose() {
-                       WaitForRequests (2000);
-                       queueManager.Dispose (); // Send a 503 to all queued requests
-                       queueManager = null;
-                       
-                       _cache = null;
-                       HttpApplicationFactory.EndApplication ();
-               }
-
-               void OnDomainUnload (object o, EventArgs args)
-               {
-                       HttpApplicationFactory.EndApplication ();
-               }
-
-               internal void ByeByeDomain ()
-               {
-                       HttpApplicationFactory.EndApplication ();
-                       ThreadPool.QueueUserWorkItem (unloadDomainCallback);
-               }
-
-               internal void WaitForRequests(int ms) {
-                       DateTime timeout = DateTime.Now.AddMilliseconds(ms);
-
-                       do {
-                               if (Interlocked.CompareExchange (ref _activeRequests, 0, 0) == 0)
-                                       return;
-
-                               Thread.Sleep (100);
-                       } while (timeout > DateTime.Now);
                }
-
-               internal void InternalExecuteRequest (HttpWorkerRequest request)
+#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 ()
                {
-                       IHttpHandler handler;
-                       IHttpAsyncHandler async_handler;
-
-                       HttpContext context = new HttpContext(request);
-
-                       request.SetEndOfSendNotification(_endOfSendCallback, context);
-                       
-                       Interlocked.Increment(ref _activeRequests);
-
+                       firstRun = true;
+#if NET_2_0
                        try {
-                               if (!_firstRequestStarted) {
-                                       lock (this) {
-                                               if (!_firstRequestStarted) {
-                                                       OnFirstRequestStart(context);
-                                                       _firstRequestStarted = true;
-                                               }
-                                       }
-                               }
-
-                               // This *must* be done after the configuration is initialized.
-                               context.Response.InitializeWriter ();
-                               handler = HttpApplicationFactory.GetInstance(context);
-                               if (null == handler)
-                                       throw new HttpException(FormatResourceString("unable_to_create_app"));
-
-                               if (handler is IHttpAsyncHandler) {
-                                       async_handler = (IHttpAsyncHandler) handler;
-
-                                       context.AsyncHandler = async_handler;
-                                       async_handler.BeginProcessRequest(context, _handlerCallback, context);
-                               } else {
-                                       handler.ProcessRequest(context);
-                                       FinishRequest(context, null);
-                               }
+                               WebConfigurationManager.Init ();
+                       } catch (Exception ex) {
+                               initialException = ex;
                        }
-                       catch (Exception error) {
-                               context.Response.InitializeWriter ();
-                               FinishRequest(context, error);
-                       }
-               }
+                       
+#endif
 
-               void DoRequest (object o)
-               {
-                       Interlocked.Decrement (ref pendingCallbacks);
-                       InternalExecuteRequest ((HttpWorkerRequest) o);
-               }
-               
-               void TryExecuteQueuedRequests ()
-               {
-                       // Wait for pending jobs to start
-                       if (Interlocked.CompareExchange (ref pendingCallbacks, 3, 3) >= 3)
-                               return;
+                       // 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;
 
-                       HttpWorkerRequest wr = queueManager.GetNextRequest (null);
-                       if (wr == null)
-                               return;
+                       trace_manager = new TraceManager ();
+                       if (trace_manager.HasException)
+                                       initialException = trace_manager.InitialException;
 
-                       Interlocked.Increment (ref pendingCallbacks);
-                       ThreadPool.QueueUserWorkItem (doRequestCallback, wr);
-                       TryExecuteQueuedRequests ();
+                       timeout_manager = new TimeoutManager ();
+                       cache = new Cache ();
+                       internalCache = new Cache ();
+                       do_RealProcessRequest = new WaitCallback (RealProcessRequest);
                }
 
-               public static void ProcessRequest (HttpWorkerRequest request)
+#if ONLY_1_1
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
+#endif
+               public HttpRuntime ()
                {
-                       if (request == null)
-                               throw new ArgumentNullException ("request");
 
-                       QueueManager mgr = _runtime.queueManager;
-                       if (_runtime._firstRequestStarted && mgr != null) {
-                               request = mgr.GetNextRequest (request);
-                               // We're busy, return immediately
-                               if (request == null)
-                                       return;
-                       }
-
-                       _runtime.InternalExecuteRequest (request);
-               }
-
-#if NET_1_1
-               public static void UnloadAppDomain ()
-               {
-                       _runtime.ByeByeDomain ();
                }
 #endif
-               public static Cache Cache {
-                       get {
-                               return _runtime._cache;
-                       }
-               }      
-
+               
+#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 {
-                               if (appDomainAppId == null)
-                                       appDomainAppId = (string) AppDomain.CurrentDomain.GetData (".appId");
-
-                               return appDomainAppId;
+                               //
+                               // 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 {
-                               if (appDomainAppPath == null)
-                                       appDomainAppPath = (string) AppDomain.CurrentDomain.GetData (".appPath");
-
-                               return appDomainAppPath;
+                               string dirname = (string) AppDomain.CurrentDomain.GetData (".appPath");
+                               if (SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
                        }
                }
 
                public static string AppDomainAppVirtualPath {
                        get {
-                               if (appDomainAppVirtualPath == null)
-                                       appDomainAppVirtualPath = (string) AppDomain.CurrentDomain.GetData (".appVPath");
-
-                               return appDomainAppVirtualPath;
+                               return (string) AppDomain.CurrentDomain.GetData (".appVPath");
                        }
                }
 
                public static string AppDomainId {
+                       [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
                        get {
-                               if (appDomainId == null)
-                                       appDomainId = (string) AppDomain.CurrentDomain.GetData (".domainId");
-
-                               return appDomainId;
+                               return (string) AppDomain.CurrentDomain.GetData (".domainId");
                        }
                }
 
                public static string AspInstallDirectory {
                        get {
-                               return ICalls.GetMachineInstallDirectory ();
+                               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 {
-                               return Path.Combine (AppDomainAppPath, "bin");
+                               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 {
-                               return ICalls.GetMachineInstallDirectory ();
+                               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 {
-                               return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
+                               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 {
-                               // IsUnc broken under unix?
-#if NET_2_0
-                               return (Environment.OSVersion.Platform != PlatformID.Unix &&
-#else
-                               return (!((int) Environment.OSVersion.Platform == 128) &&
-#endif
-                                       new Uri ("file://" + ClrInstallDirectory).IsUnc);
+                               throw new NotImplementedException ();
                        }
                }
 
                public static string MachineConfigurationDirectory {
                        get {
-                               return Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
-                       }
-               }
-
-               internal static TimeoutManager TimeoutManager {
-                       get {
-                               return HttpRuntime._runtime.timeoutManager;
+                               string dirname = Path.GetDirectoryName (ICalls.GetMachineConfigPath ());
+                               if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
+                                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+                               }
+                               return dirname;
                        }
                }
 
-                internal static TraceManager TraceManager {
-                        get {
-                                return HttpRuntime._runtime.traceManager;
-                        }
-                }
-
+               
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public static void Close ()
                {
-                       _runtime.Dispose ();
+                       // Remove all items from cache.
                }
 
-               internal static string FormatResourceString (string key)
+               static void QueuePendingRequests ()
                {
-                       return GetResourceString (key);
+                       HttpWorkerRequest request = queue_manager.GetNextRequest (null);
+                       if (request == null)
+                               return;
+                       ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
                }
-
-               internal static string FormatResourceString (string key, string arg0)
+               
+               static void RealProcessRequest (object o)
                {
-                       /*string format = GetResourceString (key);
+                       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
 
-                       if (format == null)
-                               return null;
+                       //
+                       // 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;
+                               }
+                       }
                        
-                       return String.Format (format, arg0);
-                       */
-                       return String.Format ("{0}: {1}", key, arg0);
+                       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;
 
-               [MonoTODO ("FormatResourceString (string, string, string)")]
-               internal static string FormatResourceString (string key, string arg0, string type) {
-                       return String.Format ("{0}: {1} {2}", key, arg0, type);
+                       RealProcessRequest (request);
                }
 
-               [MonoTODO ("FormatResourceString (string, string, string, string)")]
-               internal static string FormatResourceString (string key, string arg0,
-                                                            string arg1, string arg2)
+               //
+               // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
+               //
+               static void request_processed (IAsyncResult iar)
                {
-                       return String.Format ("{0}: {1} {2} {3}", key, arg0, arg1, arg2);
+                       HttpContext context = (HttpContext) iar.AsyncState;
+
+                       context.Request.ReleaseResources ();
+                       context.Response.ReleaseResources ();
                }
 
-               [MonoTODO ("FormatResourceString (string, string[]")]
-               internal static string FormatResourceString (string key, string[] args)
+               //
+               // 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 ()
                {
-                       //StringBuilder sb = new StringBuilder ();
-                       /*sb.AppendFormat ("{0}: ", key);
-                       foreach (string s in args)
-                               sb.AppendFormat ("{0} ", s);
-
-                       if (sb.Length > 0)
-                               sb.Length--;
-                       return sb.ToString ();*/
-                       string s = key + ": ";
-                       if (args != null)
-                               foreach (string k in args)
-                                       s += k + " ";
-                       return s;
+                       //
+                       // TODO: call ReleaseResources
+                       //
+                       ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
                }
 
-               private static string GetResourceString (string key) {
-                       return _runtime.GetResourceStringFromResourceManager (key);
+               //
+               // 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);
                }
 
-               [MonoTODO ("GetResourceStringFromResourceManager (string)")]
-               private string GetResourceStringFromResourceManager (string key) {
-                       return key;
+               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
                }
 
-               #region Security Internal Methods (not impl)
-               [MonoTODO ("Get Application path from the appdomain object")]
-               internal static IStackWalk AppPathDiscovery {
-                       get {
-                               if (appPathDiscoveryStackWalk == null) {
-                                       appPathDiscoveryStackWalk = new FileIOPermission (
-                                               FileIOPermissionAccess.PathDiscovery, "<apppath>");
-                               }
-                               return appPathDiscoveryStackWalk;
-                       }
-               }
+                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";
 
-               internal static IStackWalk ControlPrincipal {
-                       get {
-                               if (ctrlPrincipalStackWalk == null) {
-                                       ctrlPrincipalStackWalk = new SecurityPermission (
-                                               SecurityPermissionFlag.ControlPrincipal);
-                               }
-                               return ctrlPrincipalStackWalk;
+               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");
 
-               internal static IStackWalk Reflection {
-                       get {
-                               if (reflectionStackWalk == null) {
-                                       reflectionStackWalk = new ReflectionPermission (
-                                               ReflectionPermissionFlag.TypeInformation |
-                                               ReflectionPermissionFlag.MemberAccess);
-                               }
-                               return reflectionStackWalk;
+                       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 IStackWalk SensitiveInformation {
-                       get {
-                               if (sensitiveInfoStackWalk == null) {
-                                       sensitiveInfoStackWalk = new EnvironmentPermission (
-                                               PermissionState.Unrestricted);
-                               }
-                               return sensitiveInfoStackWalk;
+               
+               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);
                        }
                }
-
-               internal static IStackWalk UnmanagedCode {
+#endif
+               
+               internal static TraceManager TraceManager {
                        get {
-                               if (unmgdCodeStackWalk == null) {
-                                       unmgdCodeStackWalk = new SecurityPermission (
-                                               SecurityPermissionFlag.UnmanagedCode);
-                               }
-                               return unmgdCodeStackWalk;
+                               return trace_manager;
                        }
                }
 
-               internal static IStackWalk Unrestricted {
+#if !TARGET_JVM
+               internal static TimeoutManager TimeoutManager {
                        get {
-                               if (unrestrictedStackWalk == null) {
-                                       unrestrictedStackWalk = new PermissionSet (
-                                               PermissionState.Unrestricted);
-                               }
-                               return unrestrictedStackWalk;
+                               return timeout_manager;
                        }
                }
-
-               internal static IStackWalk FileReadAccess (string file)
-               {
-                       return new FileIOPermission (FileIOPermissionAccess.Read, file);
-               }
-
-               internal static IStackWalk PathDiscoveryAccess (string path)
-               {
-                       return new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path);
-               }
-               #endregion
+#endif
        }
 }