Merge pull request #2087 from joelmartinez/mdoc-membername-fixup
[mono.git] / mcs / class / System.Web / System.Web / HttpRuntime.cs
index d03424f19e5c5932303860d3607f682903a0d579..d2822357434e406f738d294a8269f02c512fea75 100644 (file)
@@ -1,11 +1,12 @@
 //
 // System.Web.HttpRuntime.cs 
 // 
-// Author:
+// Authors:
 //     Miguel de Icaza (miguel@novell.com)
+//      Marek Habersack <mhabersack@novell.com>
 //
 //
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2010 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
@@ -34,133 +35,108 @@ using System.IO;
 using System.Text;
 using System.Globalization;
 using System.Collections;
+using System.Collections.Concurrent;
 using System.Reflection;
 using System.Security;
 using System.Security.Permissions;
 using System.Web.Caching;
 using System.Web.Configuration;
+using System.Web.Management;
 using System.Web.UI;
 using System.Web.Util;
+using Mono.Web.Util;
 using System.Threading;
-#if TARGET_J2EE
-using Mainsoft.Web;
-#endif
-
-#if NET_2_0 && !TARGET_JVM
 using System.CodeDom.Compiler;
 using System.Web.Compilation;
-#endif
 
-namespace System.Web {
-       
+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
+       public sealed class HttpRuntime
+       {
+               static bool domainUnloading;
+               static SplitOrderedList <string, string> registeredAssemblies;
                static QueueManager queue_manager;
                static TraceManager trace_manager;
-               static TimeoutManager timeout_manager;
                static Cache cache;
                static Cache internalCache;
                static WaitCallback do_RealProcessRequest;
+               static HttpWorkerRequest.EndOfSendNotification end_of_send_cb;
                static Exception initialException;
                static bool firstRun;
-               
-#if NET_2_0
                static bool assemblyMappingEnabled;
                static object assemblyMappingLock = new object ();
                static object appOfflineLock = new object ();
-#endif
+               static HttpRuntimeSection runtime_section;
                
+               public HttpRuntime ()
+               {
+
+               }
+
                static HttpRuntime ()
                {
                        firstRun = true;
-#if NET_2_0
+
                        try {
                                WebConfigurationManager.Init ();
+                               SettingsMappingManager.Init ();
+                               runtime_section = (HttpRuntimeSection) WebConfigurationManager.GetSection ("system.web/httpRuntime");
                        } 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;
+                       if (queue_manager.HasException) {
+                               if (initialException == null)
+                                       initialException = queue_manager.InitialException;
+                               else {
+                                       Console.Error.WriteLine ("Exception during QueueManager initialization:");
+                                       Console.Error.WriteLine (queue_manager.InitialException);
+                               }
+                       }
 
                        trace_manager = new TraceManager ();
-                       if (trace_manager.HasException)
+                       if (trace_manager.HasException) {
+                               if (initialException == null)
                                        initialException = trace_manager.InitialException;
+                               else {
+                                       Console.Error.WriteLine ("Exception during TraceManager initialization:");
+                                       Console.Error.WriteLine (trace_manager.InitialException);
+                               }
+                       }
 
-                       timeout_manager = new TimeoutManager ();
+                       registeredAssemblies = new SplitOrderedList <string, string> (StringComparer.Ordinal);
                        cache = new Cache ();
                        internalCache = new Cache ();
-                       do_RealProcessRequest = new WaitCallback (RealProcessRequest);
+                       internalCache.DependencyCache = internalCache;
+                       do_RealProcessRequest = new WaitCallback (state => {
+                               try {
+                                       RealProcessRequest (state);
+                               } catch {}
+                               });
+                       end_of_send_cb = new HttpWorkerRequest.EndOfSendNotification (EndOfSend);
                }
 
-#if ONLY_1_1
-               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
-#endif
-               public HttpRuntime ()
-               {
-
+               internal static SplitOrderedList <string, string> RegisteredAssemblies {
+                       get { return registeredAssemblies; }
                }
-#endif
                
 #region AppDomain handling
+               internal static bool DomainUnloading {
+                       get { return domainUnloading; }
+               }
+
+               [MonoDocumentationNote ("Currently returns path to the application root")]
+               public static string AspClientScriptPhysicalPath { get { return AppDomainAppPath; } }
+
+               [MonoDocumentationNote ("Currently returns path to the application root")]
+               public static string AspClientScriptVirtualPath { get { return AppDomainAppVirtualPath; } }
+               
                //
                // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
                //
@@ -275,11 +251,10 @@ namespace System.Web {
                        }
                }
 
-               [MonoTODO]
                public static bool IsOnUNCShare {
                        [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
                        get {
-                               throw new NotImplementedException ();
+                               return RuntimeHelpers.IsUncShare;
                        }
                }
 
@@ -293,6 +268,22 @@ namespace System.Web {
                        }
                }
 
+               internal static HttpRuntimeSection Section { get { return runtime_section; } }
+
+               public static bool UsingIntegratedPipeline { get { return false; } }
+
+               public static Version IISVersion {
+                       get {
+                               // Null means not hosted by IIS
+                               return null;
+                       }
+               }
+               
+               public static Version TargetFramework {
+                       get {
+                               return runtime_section.TargetFramework;
+                       }
+               }
                
                [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public static void Close ()
@@ -300,18 +291,21 @@ namespace System.Web {
                        // Remove all items from cache.
                }
 
-               static void QueuePendingRequests ()
+               internal static HttpWorkerRequest QueuePendingRequest (bool started_internally)
                {
-                       HttpWorkerRequest request = queue_manager.GetNextRequest (null);
-                       if (request == null)
-                               return;
-                       ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
+                       HttpWorkerRequest next = queue_manager.GetNextRequest (null);
+                       if (next == null)
+                               return null;
+
+                       if (!started_internally) {
+                               next.StartedInternally = true;
+                               ThreadPool.QueueUserWorkItem (do_RealProcessRequest, next);
+                               return null;
+                       }
+                       return next;
                }
 
-#if !TARGET_J2EE
-#if NET_2_0
                static readonly string[] app_offline_files = {"app_offline.htm", "App_Offline.htm", "APP_OFFLINE.HTM"};
-               static FileSystemWatcher[] app_offline_watchers;
                static string app_offline_file;
                
                static bool AppIsOffline (HttpContext context)
@@ -323,11 +317,13 @@ namespace System.Web {
                        response.Clear ();
                        response.ContentType = "text/html";
                        response.ExpiresAbsolute = DateTime.UtcNow;
+                       response.StatusCode = 503;
                        response.TransmitFile (app_offline_file, true);
                        
                        context.Request.ReleaseResources ();
                        context.Response.ReleaseResources ();
                        HttpContext.Current = null;
+                       HttpApplication.requests_total_counter.Increment ();
                        
                        return true;
                }
@@ -380,6 +376,8 @@ namespace System.Web {
                                app_offline_file = filePath;
                                HttpApplicationFactory.DisableWatchers ();
                                HttpApplicationFactory.ApplicationDisabled = true;
+                               InternalCache.InvokePrivateCallbacks ();
+                               HttpApplicationFactory.Dispose ();
                        }
                }
                
@@ -390,7 +388,6 @@ namespace System.Web {
                                RenamedEventHandler reh = new RenamedEventHandler (AppOfflineFileRenamed);
 
                                string app_dir = AppDomainAppPath;
-                               ArrayList watchers = new ArrayList ();
                                FileSystemWatcher watcher;
                                string offlineFile = null, tmp;
                                
@@ -401,10 +398,9 @@ namespace System.Web {
                                        watcher.NotifyFilter |= NotifyFilters.Size;
                                        watcher.Deleted += seh;
                                        watcher.Changed += seh;
+                                       watcher.Created += seh;
                                        watcher.Renamed += reh;
                                        watcher.EnableRaisingEvents = true;
-                                       
-                                       watchers.Add (watcher);
 
                                        tmp = Path.Combine (app_dir, f);
                                        if (File.Exists (tmp))
@@ -415,39 +411,37 @@ namespace System.Web {
                                        SetOfflineMode (true, offlineFile);
                        }
                }
-#endif
-#endif
                
                static void RealProcessRequest (object o)
                {
-#if TARGET_J2EE
-                       HttpContext context = HttpContext.Current;
-                       if (context == null)
-                               context = new HttpContext ((HttpWorkerRequest) o);
-                       else
-                               context.SetWorkerRequest ((HttpWorkerRequest) o);
-#else
-                       HttpContext context = new HttpContext ((HttpWorkerRequest) o);
-#endif
-                       HttpContext.Current = context;
+                       if (domainUnloading) {
+                               Console.Error.WriteLine ("Domain is unloading, not processing the request.");
+                               return;
+                       }
+
+                       HttpWorkerRequest req = (HttpWorkerRequest) o;
+                       bool started_internally = req.StartedInternally;
+                       do {
+                               Process (req);
+                               req = QueuePendingRequest (started_internally);
+                       } while (started_internally && req != null);
+               }
+
+               static void Process (HttpWorkerRequest req)
+               {
                        bool error = false;
-#if !TARGET_J2EE
                        if (firstRun) {
-#if NET_2_0
-                               SetupOfflineWatch ();
-#endif
                                firstRun = false;
                                if (initialException != null) {
-                                       FinishWithException ((HttpWorkerRequest) o, new HttpException ("Initial exception", initialException));
+                                       FinishWithException (req, HttpException.NewWithCode ("Initial exception", initialException, WebEventCodes.RuntimeErrorRequestAbort));
                                        error = true;
                                }
+                               SetupOfflineWatch ();
                        }
-
-#if NET_2_0
+                       HttpContext context = new HttpContext (req);
+                       HttpContext.Current = context;
                        if (AppIsOffline (context))
                                return;
-#endif
-#endif
                        
                        //
                        // Get application instance (create or reuse an instance of the correct class)
@@ -457,7 +451,7 @@ namespace System.Web {
                                try {
                                        app = HttpApplicationFactory.GetApplication (context);
                                } catch (Exception e) {
-                                       FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
+                                       FinishWithException (req, HttpException.NewWithCode (String.Empty, e, WebEventCodes.RuntimeErrorRequestAbort));
                                        error = true;
                                }
                        }
@@ -468,34 +462,25 @@ namespace System.Web {
                                HttpContext.Current = null;
                        } else {
                                context.ApplicationInstance = app;
-                               
+                               req.SetEndOfSendNotification (end_of_send_cb, context);
+
                                //
                                // Ask application to service the request
                                //
-#if TARGET_J2EE
-                               IHttpAsyncHandler ihah = app;
-                               if (context.Handler == null)
-                                       ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
-                               else
-                                       app.Tick ();
-                               //ihh.ProcessRequest (context);
-                               IHttpExtendedHandler extHandler = context.Handler as IHttpExtendedHandler;
-                               if (extHandler != null && !extHandler.IsCompleted)
-                                       return;
-
-                               ihah.EndProcessRequest (null);
-#else
-                               IHttpAsyncHandler ihah = app;
-                               IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
-                               ihah.EndProcessRequest (appiar);
-#endif
+                               
+                               IHttpHandler ihh = app;
+//                             IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
+//                             ihah.EndProcessRequest (appiar);
+                               ihh.ProcessRequest (context);
 
                                HttpApplicationFactory.Recycle (app);
                        }
-                       
-                       QueuePendingRequests ();
                }
-               
+
+               static void EndOfSend (HttpWorkerRequest ignored1, object ignored2)
+               {
+               }
+
                //
                // ProcessRequest method is executed in the AppDomain of the application
                //
@@ -515,20 +500,11 @@ namespace System.Web {
                        if (request == null)
                                return;
 
+                       QueuePendingRequest (false);
                        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) 
@@ -539,33 +515,36 @@ namespace System.Web {
                        //
                        // TODO: call ReleaseResources
                        //
-                       ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
+                       domainUnloading = true;
+                       HttpApplicationFactory.DisableWatchers ();
+                       ThreadPool.QueueUserWorkItem (delegate {
+                               try {
+                                       ShutdownAppDomain ();
+                               } catch (Exception e){
+                                       Console.Error.WriteLine (e);
+                               }
+                       });
                }
-
                //
                // Shuts down the AppDomain
                //
-               static void ShutdownAppDomain (object args)
+               static void ShutdownAppDomain ()
                {
                        queue_manager.Dispose ();
                        // This will call Session_End if needed.
                        InternalCache.InvokePrivateCallbacks ();
                        // Kill our application.
                        HttpApplicationFactory.Dispose ();
-                       ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
+                       ThreadPool.QueueUserWorkItem (delegate {
+                               try {
+                                       DoUnload ();
+                               } catch {
+                               }});
                }
 
-               static void DoUnload (object state)
+               static void DoUnload ()
                {
-                       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" +
@@ -578,7 +557,6 @@ namespace System.Web {
                        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 ();
@@ -587,6 +565,7 @@ namespace System.Web {
                        wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
                        wr.FlushResponse (true);
                        wr.CloseConnection ();
+                       HttpApplication.requests_total_counter.Increment ();
                }
 
                //
@@ -598,7 +577,6 @@ namespace System.Web {
                {
                        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);
@@ -606,9 +584,16 @@ namespace System.Web {
                        wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
                        wr.FlushResponse (true);
                        wr.CloseConnection ();
+                       HttpApplication.requests_total_counter.Increment ();
                }
 
-#if NET_2_0 && !TARGET_J2EE
+               [AspNetHostingPermissionAttribute(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Unrestricted)]
+               [MonoDocumentationNote ("Always returns null on Mono")]
+               public static NamedPermissionSet GetNamedPermissionSet ()
+               {
+                       return null;
+               }
+               
                static internal void WritePreservationFile (Assembly asm, string genericNameBase)
                {
                        if (asm == null)
@@ -620,7 +605,7 @@ namespace System.Web {
                                                        genericNameBase + ".compiled");
                        PreservationFile pf = new PreservationFile ();
                        try {
-                               pf.VirtualPath = String.Format ("/{0}/", genericNameBase);
+                               pf.VirtualPath = String.Concat ("/", genericNameBase, "/");
 
                                AssemblyName an = asm.GetName ();
                                pf.Assembly = an.Name;
@@ -638,22 +623,29 @@ namespace System.Web {
                        AssemblyName an = new AssemblyName (e.Name);
                        string dynamic_base = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
                        string compiled = Path.Combine (dynamic_base, an.Name + ".compiled");
+                       string asmPath;
 
-                       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);
+                       if (!File.Exists (compiled)) {
+                               string fn = an.FullName;
+                               if (!RegisteredAssemblies.Find ((uint)fn.GetHashCode (), fn, out asmPath))
+                                       return null;
+                       } else {
+                               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);
+                               }
+                               asmPath = Path.Combine (dynamic_base, pf.Assembly + ".dll");
                        }
+
+                       if (String.IsNullOrEmpty (asmPath))
+                               return null;
                        
                        Assembly ret = null;
                        try {
-                               string asmPath = Path.Combine (dynamic_base, pf.Assembly + ".dll");
                                ret = Assembly.LoadFrom (asmPath);
                        } catch (Exception) {
                                // ignore
@@ -671,22 +663,14 @@ namespace System.Web {
                                        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (ResolveAssemblyHandler);
                                else
                                        AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler (ResolveAssemblyHandler);
+                               assemblyMappingEnabled = enable;
                        }
                }
-#endif
                
                internal static TraceManager TraceManager {
                        get {
                                return trace_manager;
                        }
                }
-
-#if !TARGET_JVM
-               internal static TimeoutManager TimeoutManager {
-                       get {
-                               return timeout_manager;
-                       }
-               }
-#endif
        }
 }