//
// 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
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 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; } }
+ public sealed class HttpRuntime
+ {
+ static bool domainUnloading;
+ static SplitOrderedList <string, string> registeredAssemblies;
+ static QueueManager queue_manager;
+ static TraceManager trace_manager;
+ static Cache cache;
+ static Cache internalCache;
static WaitCallback do_RealProcessRequest;
-
- QueueManager _queue_manager;
- TraceManager _trace_manager;
- Cache _cache;
-
- static HttpRuntime ()
- {
- do_RealProcessRequest = new WaitCallback (RealProcessRequest);
- }
-
+ static HttpWorkerRequest.EndOfSendNotification end_of_send_cb;
+ static Exception initialException;
+ static bool firstRun;
+ static bool assemblyMappingEnabled;
+ static object assemblyMappingLock = new object ();
+ static object appOfflineLock = new object ();
+ static HttpRuntimeSection runtime_section;
+
public HttpRuntime ()
{
- WebConfigurationManager.Init ();
- _queue_manager = new QueueManager ();
- _trace_manager = new TraceManager ();
- _cache = new Cache ();
- }
- static private HttpRuntime _runtime {
- 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;
- }
}
-#else
- static QueueManager queue_manager;
- static TraceManager trace_manager;
- static TimeoutManager timeout_manager;
- static Cache cache;
- static WaitCallback do_RealProcessRequest;
static HttpRuntime ()
{
-#if NET_2_0
- WebConfigurationManager.Init ();
-#endif
+ firstRun = true;
+
+ try {
+ WebConfigurationManager.Init ();
+ SettingsMappingManager.Init ();
+ runtime_section = (HttpRuntimeSection) WebConfigurationManager.GetSection ("system.web/httpRuntime");
+ } catch (Exception ex) {
+ initialException = ex;
+ }
+
+ // 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) {
+ 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 ();
- timeout_manager = new TimeoutManager ();
+ 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);
+ }
+ }
+
+ registeredAssemblies = new SplitOrderedList <string, string> (StringComparer.Ordinal);
cache = new Cache ();
- do_RealProcessRequest = new WaitCallback (RealProcessRequest);
+ internalCache = new Cache ();
+ 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
//
}
}
#endregion
-
+
+ static string _actual_bin_directory;
public static string BinDirectory {
get {
- string dirname = Path.Combine (AppDomainAppPath, "bin");
- if (SecurityManager.SecurityEnabled) {
- new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
+ if (_actual_bin_directory == null) {
+ string[] parts = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath.Split (';');
+ string mypath = AppDomainAppPath;
+ string tmp;
+
+ foreach (string p in parts) {
+ tmp = Path.Combine (mypath, p);
+ if (Directory.Exists (tmp)) {
+ _actual_bin_directory = tmp;
+ break;
+ }
+ }
+
+ if (_actual_bin_directory == null)
+ _actual_bin_directory = Path.Combine (mypath, "bin");
+
+ if (_actual_bin_directory [_actual_bin_directory.Length - 1] != Path.DirectorySeparatorChar)
+ _actual_bin_directory += Path.DirectorySeparatorChar;
}
- return dirname;
+
+ if (SecurityManager.SecurityEnabled)
+ new FileIOPermission (FileIOPermissionAccess.PathDiscovery, _actual_bin_directory).Demand ();
+
+ return _actual_bin_directory;
}
}
}
}
+ internal static Cache InternalCache {
+ get {
+ return internalCache;
+ }
+ }
+
public static string ClrInstallDirectory {
get {
string dirname = Path.GetDirectoryName (typeof (Object).Assembly.Location);
}
}
- [MonoTODO]
public static bool IsOnUNCShare {
[AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
get {
- throw new NotImplementedException ();
+ return RuntimeHelpers.IsUncShare;
}
}
public static string MachineConfigurationDirectory {
get {
-#if NET_2_0
- string dirname = Path.GetDirectoryName (WebConfigurationManager.OpenMachineConfiguration().FilePath);
-#else
- string dirname = Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
-#endif
+ string dirname = Path.GetDirectoryName (ICalls.GetMachineConfigPath ());
if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
}
}
}
+ 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 ()
{
// 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;
+ }
+
+ static readonly string[] app_offline_files = {"app_offline.htm", "App_Offline.htm", "APP_OFFLINE.HTM"};
+ static string app_offline_file;
+
+ static bool AppIsOffline (HttpContext context)
+ {
+ if (!HttpApplicationFactory.ApplicationDisabled || app_offline_file == null)
+ return false;
+
+ HttpResponse response = context.Response;
+ 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;
+ }
+
+ static void AppOfflineFileRenamed (object sender, RenamedEventArgs args)
+ {
+ AppOfflineFileChanged (sender, args);
}
+ static void AppOfflineFileChanged (object sender, FileSystemEventArgs args)
+ {
+ lock (appOfflineLock) {
+ bool offline;
+
+ switch (args.ChangeType) {
+ case WatcherChangeTypes.Created:
+ case WatcherChangeTypes.Changed:
+ offline = true;
+ break;
+
+ case WatcherChangeTypes.Deleted:
+ offline = false;
+ break;
+
+ case WatcherChangeTypes.Renamed:
+ RenamedEventArgs rargs = args as RenamedEventArgs;
+
+ if (rargs != null &&
+ String.Compare (rargs.Name, "app_offline.htm", StringComparison.OrdinalIgnoreCase) == 0)
+ offline = true;
+ else
+ offline = false;
+ break;
+
+ default:
+ offline = false;
+ break;
+ }
+ SetOfflineMode (offline, args.FullPath);
+ }
+ }
+
+ static void SetOfflineMode (bool offline, string filePath)
+ {
+ if (!offline) {
+ app_offline_file = null;
+ if (HttpApplicationFactory.ApplicationDisabled)
+ HttpRuntime.UnloadAppDomain ();
+ } else {
+ app_offline_file = filePath;
+ HttpApplicationFactory.DisableWatchers ();
+ HttpApplicationFactory.ApplicationDisabled = true;
+ InternalCache.InvokePrivateCallbacks ();
+ HttpApplicationFactory.Dispose ();
+ }
+ }
+
+ static void SetupOfflineWatch ()
+ {
+ lock (appOfflineLock) {
+ FileSystemEventHandler seh = new FileSystemEventHandler (AppOfflineFileChanged);
+ RenamedEventHandler reh = new RenamedEventHandler (AppOfflineFileRenamed);
+
+ string app_dir = AppDomainAppPath;
+ FileSystemWatcher watcher;
+ string offlineFile = null, tmp;
+
+ foreach (string f in app_offline_files) {
+ watcher = new FileSystemWatcher ();
+ watcher.Path = Path.GetDirectoryName (app_dir);
+ watcher.Filter = Path.GetFileName (f);
+ watcher.NotifyFilter |= NotifyFilters.Size;
+ watcher.Deleted += seh;
+ watcher.Changed += seh;
+ watcher.Created += seh;
+ watcher.Renamed += reh;
+ watcher.EnableRaisingEvents = true;
+
+ tmp = Path.Combine (app_dir, f);
+ if (File.Exists (tmp))
+ offlineFile = tmp;
+ }
+
+ if (offlineFile != null)
+ SetOfflineMode (true, offlineFile);
+ }
+ }
+
static void RealProcessRequest (object o)
{
- HttpContext context = new HttpContext ((HttpWorkerRequest) o);
- 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 (firstRun) {
+ firstRun = false;
+ if (initialException != null) {
+ FinishWithException (req, HttpException.NewWithCode ("Initial exception", initialException, WebEventCodes.RuntimeErrorRequestAbort));
+ error = true;
+ }
+ SetupOfflineWatch ();
+ }
+ HttpContext context = new HttpContext (req);
+ HttpContext.Current = context;
+ if (AppIsOffline (context))
+ return;
+
//
// Get application instance (create or reuse an instance of the correct class)
//
HttpApplication app = null;
- bool error = false;
- try {
- app = HttpApplicationFactory.GetApplication (context);
- } catch (Exception e) {
- FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
- error = true;
+ if (!error) {
+ try {
+ app = HttpApplicationFactory.GetApplication (context);
+ } catch (Exception e) {
+ FinishWithException (req, HttpException.NewWithCode (String.Empty, e, WebEventCodes.RuntimeErrorRequestAbort));
+ error = true;
+ }
}
-
+
if (error) {
context.Request.ReleaseResources ();
context.Response.ReleaseResources ();
HttpContext.Current = null;
} else {
context.ApplicationInstance = app;
-
-#if NET_2_0 && !TARGET_JVM
- //
- // Compile the local resources, if any
- //
- AppResourcesCompiler ac = new AppResourcesCompiler (context, false);
- ac.Compile ();
-#endif
-
+ req.SetEndOfSendNotification (end_of_send_cb, context);
+
//
// Ask application to service the request
//
- IHttpAsyncHandler ihah = app;
-
- IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
- ihah.EndProcessRequest (appiar);
+
+ 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
//
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)
//
// 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.
- Cache.InvokePrivateCallbacks ();
+ InternalCache.InvokePrivateCallbacks ();
// Kill our application.
HttpApplicationFactory.Dispose ();
- ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
+ ThreadPool.QueueUserWorkItem (delegate {
+ try {
+ DoUnload ();
+ } catch {
+ }});
}
-#if TARGET_J2EE // No unload support for appdomains under Grasshopper
- static void DoUnload (object state)
- {
- }
-#else
- static void DoUnload (object state)
+ static void DoUnload ()
{
AppDomain.Unload (AppDomain.CurrentDomain);
}
-#endif
- static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
+ 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";
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 ();
wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
wr.FlushResponse (true);
wr.CloseConnection ();
+ HttpApplication.requests_total_counter.Increment ();
}
//
{
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.SendResponseFromMemory (contentBytes, contentBytes.Length);
wr.FlushResponse (true);
wr.CloseConnection ();
+ HttpApplication.requests_total_counter.Increment ();
}
- internal static TraceManager TraceManager {
- get {
- return trace_manager;
+ [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)
+ 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.Concat ("/", 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");
+ string asmPath;
+
+ 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 !TARGET_JVM
- internal static TimeoutManager TimeoutManager {
+ if (String.IsNullOrEmpty (asmPath))
+ return null;
+
+ Assembly ret = null;
+ try {
+ 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);
+ assemblyMappingEnabled = enable;
+ }
+ }
+
+ internal static TraceManager TraceManager {
get {
- return timeout_manager;
+ return trace_manager;
}
}
-#endif
}
}