2009-06-04 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpContext.cs
index 57a160a214eb05cb163bfc69beca95bb9363c90c..3666134304c9754a0fad78c38d9acd9bde56ce54 100644 (file)
@@ -42,15 +42,19 @@ using System.Web.SessionState;
 using System.Web.UI;
 using System.Web.Util;
 #if NET_2_0
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Resources;
+using System.Web.Compilation;
 using System.Web.Profile;
 using CustomErrorMode = System.Web.Configuration.CustomErrorsMode;
 #endif
 
 namespace System.Web {
-       
        // CAS - no InheritanceDemand here as the class is sealed
        [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
-       public sealed class HttpContext : IServiceProvider {
+       public sealed partial class HttpContext : IServiceProvider {
                internal HttpWorkerRequest WorkerRequest;
                HttpApplication app_instance;
                HttpRequest request;
@@ -67,8 +71,29 @@ namespace System.Web {
                object config_timeout;
                int timeout_possible;
                DateTime time_stamp = DateTime.UtcNow;
-#if TARGET_JVM // No remoting support (CallContext) yet in Grasshopper
-               static LocalDataStoreSlot _ContextSlot = Thread.GetNamedDataSlot ("Context");
+               Timer timer;
+               Thread thread;
+               bool _isProcessingInclude;
+
+#if NET_2_0
+               [ThreadStatic]
+               static ResourceProviderFactory provider_factory;
+               [ThreadStatic]
+               static Dictionary <string, IResourceProvider> resource_providers;
+               
+#if TARGET_JVM
+               const string app_global_res_key = "HttpContext.app_global_res_key";
+               internal static Assembly AppGlobalResourcesAssembly {
+                       get { return (Assembly) AppDomain.CurrentDomain.GetData (app_global_res_key); }
+                       set { AppDomain.CurrentDomain.SetData (app_global_res_key, value); }
+               }
+#else
+               [ThreadStatic]
+               static Dictionary <ResourceManagerCacheKey, ResourceManager> resourceManagerCache;
+               internal static Assembly AppGlobalResourcesAssembly;
+#endif
+               ProfileBase profile = null;
+               LinkedList<IHttpHandler> handlers;
 #endif
                
                public HttpContext (HttpWorkerRequest wr)
@@ -85,6 +110,11 @@ namespace System.Web {
                        
                }
 
+               internal bool IsProcessingInclude {
+                       get { return _isProcessingInclude; }
+                       set { _isProcessingInclude = value; }
+               }
+
                public Exception [] AllErrors {
                        get {
                                if (errors == null)
@@ -122,18 +152,17 @@ namespace System.Web {
                        }
                }
 
+               internal Cache InternalCache {
+                       get {
+                               return HttpRuntime.InternalCache;
+                       }
+               }
+               
                //
                // The "Current" property is set just after we have constructed it with 
                // the 'HttpContext (HttpWorkerRequest)' constructor.
                //
-#if TARGET_JVM // No remoting support (CallContext) yet in Grasshopper
-               [MonoTODO("Context - Use System.Remoting.Messaging.CallContext instead of Thread storage")]
-               public static HttpContext Current
-               {
-                       get { return (HttpContext) Thread.GetData (_ContextSlot); }
-                       set { Thread.SetData (_ContextSlot, value); }
-               }
-#else
+#if !TARGET_JVM // No remoting CallContext support in Grasshopper
                public static HttpContext Current {
                        get {
                                return (HttpContext) CallContext.GetData ("c");
@@ -165,6 +194,17 @@ namespace System.Web {
 
                public bool IsCustomErrorEnabled {
                        get {
+                               try {
+                                       return IsCustomErrorEnabledUnsafe;
+                               }
+                               catch {
+                                       return false;
+                               }
+                       }
+               }
+
+               internal bool IsCustomErrorEnabledUnsafe {
+                       get {
 #if NET_2_0
                                CustomErrorsSection cfg = (CustomErrorsSection) WebConfigurationManager.GetSection ("system.web/customErrors");
 #else
@@ -181,26 +221,12 @@ namespace System.Web {
                                if (cfg.Mode == CustomErrorMode.On)
                                        return true;
 
-                               return (cfg.Mode == CustomErrorMode.RemoteOnly) && 
-                                       (Request.WorkerRequest.GetLocalAddress () != Request.UserHostAddress);
+                               return (cfg.Mode == CustomErrorMode.RemoteOnly) && !Request.IsLocal;
                        }
                }
-#if TARGET_JVM
-               public bool IsDebuggingEnabled { get { return false; } }
-#else
+#if !TARGET_JVM
                public bool IsDebuggingEnabled {
-                       get {
-#if NET_2_0
-                               CompilationSection section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
-                               return section.Debug;
-#else
-                               try {
-                                       return CompilationConfiguration.GetInstance (this).Debug;
-                               } catch {
-                                       return false;
-                               }
-#endif
-                       }
+                       get { return HttpRuntime.IsDebuggingEnabled; }
                }
 #endif
                public IDictionary Items {
@@ -274,19 +300,81 @@ namespace System.Web {
                }
 
 #if NET_2_0
-               [MonoTODO]
+               internal bool MapRequestHandlerDone {
+                       get;
+                       set;
+               }
+               
+               // The two properties below are defined only when the IIS7 integrated mode is used.
+               // They are useless under Mono
+               public RequestNotification CurrentNotification {
+                       get { throw new PlatformNotSupportedException ("This property is not supported on Mono.");  }
+               }
+
+               public bool IsPostNotification {
+                       get { throw new PlatformNotSupportedException ("This property is not supported on Mono."); }
+               }
+               
+               internal void PushHandler (IHttpHandler handler)
+               {
+                       if (handler == null)
+                               return;
+                       if (handlers == null)
+                               handlers = new LinkedList <IHttpHandler> ();
+                       handlers.AddLast (handler);
+               }
+
+               internal void PopHandler ()
+               {
+                       if (handlers == null || handlers.Count == 0)
+                               return;
+                       handlers.RemoveLast ();
+               }
+               
+               IHttpHandler GetCurrentHandler ()
+               {
+                       if (handlers == null || handlers.Count == 0)
+                               return null;
+                       
+                       return handlers.Last.Value;
+               }
+
+               IHttpHandler GetPreviousHandler ()
+               {
+                       if (handlers == null || handlers.Count <= 1)
+                               return null;
+                       LinkedListNode <IHttpHandler> previous = handlers.Last.Previous;
+                       if (previous != null)
+                               return previous.Value;
+                       return null;
+               }
+               
                public IHttpHandler CurrentHandler {
-                       get { throw new NotImplementedException (); }
+                       get { return GetCurrentHandler (); }
                }
 
-               [MonoTODO]
                public IHttpHandler PreviousHandler {
-                       get { throw new NotImplementedException (); }
+                       get { return GetPreviousHandler (); }
+               }
+
+               internal bool ProfileInitialized {
+                       get { return profile != null; }
                }
 
-               [MonoTODO]
                public ProfileBase Profile {
-                       get { throw new NotImplementedException (); }
+                       get {
+                               if (profile == null) {
+                                       if (Request.IsAuthenticated)
+                                               profile = ProfileBase.Create (User.Identity.Name);
+                                       else
+                                               profile = ProfileBase.Create (Request.AnonymousID, false);
+                               }
+                               return profile;
+                       }
+
+                       internal set {
+                               profile = value;
+                       }
                }
 #endif
 
@@ -312,6 +400,15 @@ namespace System.Web {
                                errors = null;
                }
 
+               internal bool HasError (Exception e)
+               {
+                       if (errors == e)
+                               return true;
+
+                       return (errors is ArrayList) ?
+                               ((ArrayList) errors).Contains (e) : false;
+               }
+
                public void ClearError ()
                {
                        errors = null;
@@ -340,38 +437,165 @@ namespace System.Web {
                }
 
 #if NET_2_0
-               [MonoTODO]
+               static object GetResourceObject (string classKey, string resourceKey, CultureInfo culture, Assembly assembly)
+               {
+                       ResourceManager rm;
+                       try {
+                               if (resourceManagerCache == null)
+                                       resourceManagerCache = new Dictionary <ResourceManagerCacheKey, ResourceManager> ();
+                               
+                               ResourceManagerCacheKey key = new ResourceManagerCacheKey (classKey, assembly);
+                               if (!resourceManagerCache.TryGetValue (key, out rm)) {
+                                       rm = new ResourceManager (classKey, assembly);
+                                       rm.IgnoreCase = true;
+                                       resourceManagerCache.Add (key, rm);
+                               }
+                               
+                               return rm.GetObject (resourceKey, culture);
+                       } catch (MissingManifestResourceException) {
+                               throw;
+                       } catch (Exception ex) {
+                               throw new HttpException ("Failed to retrieve the specified global resource object.", ex);
+                       }
+               }
+               
                public static object GetGlobalResourceObject (string classKey, string resourceKey)
                {
-                       throw new NotImplementedException ();
+                       return GetGlobalResourceObject (classKey, resourceKey, Thread.CurrentThread.CurrentUICulture);
+               }
+
+               static bool EnsureProviderFactory ()
+               {
+                       if (resource_providers == null)
+                               resource_providers = new Dictionary <string, IResourceProvider> ();
+                       
+                       if (provider_factory != null)
+                               return true;
+
+                       GlobalizationSection gs = WebConfigurationManager.GetSection ("system.web/globalization") as GlobalizationSection;
+
+                       if (gs == null)
+                               return false;
+
+                       String rsfTypeName = gs.ResourceProviderFactoryType;
+                       if (String.IsNullOrEmpty (rsfTypeName))
+                               return false;
+                       
+                       Type rsfType = HttpApplication.LoadType (rsfTypeName, true);
+                       ResourceProviderFactory rpf = Activator.CreateInstance (rsfType) as ResourceProviderFactory;
+                       
+                       if (rpf == null)
+                               return false;
+
+                       provider_factory = rpf;
+                       return true;
+               }
+               
+               internal static IResourceProvider GetResourceProvider (string key, bool isLocal)
+               {
+                       if (!EnsureProviderFactory ())
+                               return null;
+
+                       IResourceProvider rp = null;
+                       if (!resource_providers.TryGetValue (key, out rp)) {
+                               if (isLocal)
+                                       rp = provider_factory.CreateLocalResourceProvider (key);
+                               else
+                                       rp = provider_factory.CreateGlobalResourceProvider (key);
+                               if (rp == null)
+                                       return null;
+                               resource_providers.Add (key, rp);
+                       }
+
+                       return rp;
                }
 
-               [MonoTODO]
+               static object GetGlobalObjectFromFactory (string classKey, string resourceKey, CultureInfo culture)
+               {
+                       // FIXME: Retention of data
+                       IResourceProvider rp = GetResourceProvider (classKey, false);
+                       if (rp == null)
+                               return null;
+                       
+                       return rp.GetObject (resourceKey, culture);
+               }
+               
                public static object GetGlobalResourceObject (string classKey, string resourceKey, CultureInfo culture)
                {
-                       throw new NotImplementedException ();
+                       object ret = GetGlobalObjectFromFactory (classKey, resourceKey, culture);
+                       if (ret != null)
+                               return ret;
+                       
+                       if (AppGlobalResourcesAssembly == null)
+                               return null;
+
+                       return GetResourceObject ("Resources." + classKey, resourceKey, culture, AppGlobalResourcesAssembly);
                }
 
-               [MonoTODO]
                public static object GetLocalResourceObject (string virtualPath, string resourceKey)
                {
-                       throw new NotImplementedException ();
+                       return GetLocalResourceObject (virtualPath, resourceKey, Thread.CurrentThread.CurrentUICulture);
                }
 
-               [MonoTODO]
+               static object GetLocalObjectFromFactory (string virtualPath, string resourceKey, CultureInfo culture)
+               {
+                       IResourceProvider rp = GetResourceProvider (virtualPath, true);
+                       if (rp == null)
+                               return null;
+                       
+                       return rp.GetObject (resourceKey, culture);
+               }
+               
                public static object GetLocalResourceObject (string virtualPath, string resourceKey, CultureInfo culture)
                {
-                       throw new NotImplementedException ();
+                       if (!VirtualPathUtility.IsAbsolute (virtualPath))
+                               throw new ArgumentException ("The specified virtualPath was not rooted.");
+
+                       object ret = GetLocalObjectFromFactory (virtualPath, resourceKey, culture);
+                       if (ret != null)
+                               return ret;
+                       
+                       string path = VirtualPathUtility.GetDirectory (virtualPath);
+                       Assembly asm = AppResourcesCompiler.GetCachedLocalResourcesAssembly (path);
+                       if (asm == null) {
+                               AppResourcesCompiler ac = new AppResourcesCompiler (path);
+                               asm = ac.Compile ();
+                               if (asm == null)
+                                       throw new MissingManifestResourceException ("A resource object was not found at the specified virtualPath.");
+                       }
+                       
+                       path = Path.GetFileName (virtualPath);
+                       return GetResourceObject (path, resourceKey, culture, asm);
                }
 
-               [MonoTODO]
                public object GetSection (string name)
                {
-#if NET_2_0
                        return WebConfigurationManager.GetSection (name);
-#else
-                       throw new NotImplementedException ();
-#endif
+               }
+
+               sealed class ResourceManagerCacheKey
+               {
+                       readonly string _name;
+                       readonly Assembly _asm;
+
+                       public ResourceManagerCacheKey (string name, Assembly asm)
+                       {
+                               _name = name;
+                               _asm = asm;
+                       }
+
+                       public override bool Equals (object obj)
+                       {
+                               if (!(obj is ResourceManagerCacheKey))
+                                       return false;
+                               ResourceManagerCacheKey key = (ResourceManagerCacheKey) obj;
+                               return key._asm == _asm && _name.Equals (key._name, StringComparison.Ordinal);
+                       }
+
+                       public override int GetHashCode ()
+                       {
+                               return _name.GetHashCode () + _asm.GetHashCode ();
+                       }
                }
 #endif
                object IServiceProvider.GetService (Type service)
@@ -419,23 +643,79 @@ namespace System.Web {
                        return null;
                }
 
+#if NET_2_0
+               public void RemapHandler (IHttpHandler handler)
+               {
+                       if (MapRequestHandlerDone)
+                               throw new InvalidOperationException ("The RemapHandler method was called after the MapRequestHandler event occurred.");
+                       Handler = handler;
+               }
+#endif
+               
                public void RewritePath (string path)
                {
+#if NET_2_0
+                       RewritePath (path, true);
+#else
+                       RewritePath (path, false);
+#endif
+               }
+
+               public void RewritePath (string filePath, string pathInfo, string queryString)
+               {
+                       RewritePath (filePath, pathInfo, queryString, false);
+               }
+
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               void RewritePath (string path, bool rebaseClientPath)
+               {
                        int qmark = path.IndexOf ('?');
                        if (qmark != -1)
-                               RewritePath (path.Substring (0, qmark), "", path.Substring (qmark + 1));
+                               RewritePath (path.Substring (0, qmark), "", path.Substring (qmark + 1), rebaseClientPath);
                        else
-                               RewritePath (path, null, null);
+                               RewritePath (path, null, null, rebaseClientPath);
                }
 
-               public void RewritePath (string filePath, string pathInfo, string queryString)
+#if NET_2_0
+               public
+#else
+               internal
+#endif
+               void RewritePath (string filePath, string pathInfo, string queryString, bool setClientFilePath)
                {
-                       filePath = UrlUtils.Combine (Request.BaseVirtualDir, filePath);
-                       if (!filePath.StartsWith (HttpRuntime.AppDomainAppVirtualPath))
-                               throw new HttpException (404, "The virtual path '" + filePath +
-                                       "' maps to another application.");
+                       if (filePath == null)
+                               throw new ArgumentNullException ("filePath");
+                       if (!VirtualPathUtility.IsValidVirtualPath (filePath))
+                               throw new HttpException ("'" + HttpUtility.HtmlEncode (filePath) + "' is not a valid virtual path.");
+
+                       bool pathRelative = VirtualPathUtility.IsAppRelative (filePath);
+                       bool pathAbsolute = pathRelative ? false : VirtualPathUtility.IsAbsolute (filePath);
+                       if (pathRelative || pathAbsolute) {
+                               bool needSubstring = false;
 
+                               if (pathRelative && filePath.Length > 1)
+                                       needSubstring = true;
+
+                               string bvd = Request.BaseVirtualDir;
+                               if (bvd.Length > 1)
+                                       bvd += "/";
+
+                               string canonizedFilePath = VirtualPathUtility.Canonize (filePath);
+                               filePath = VirtualPathUtility.Combine (bvd, needSubstring ? canonizedFilePath.Substring (2) : canonizedFilePath);
+                       } else 
+                               filePath = VirtualPathUtility.Combine (VirtualPathUtility.GetDirectory (Request.FilePath), filePath);
+                       
+                       if (!StrUtils.StartsWith (filePath, HttpRuntime.AppDomainAppVirtualPath))
+                               throw new HttpException (404, "The virtual path '" + HttpUtility.HtmlEncode (filePath) + "' maps to another application.", filePath);
+                       
                        Request.SetCurrentExePath (filePath);
+                       if (setClientFilePath)
+                               Request.SetFilePath (filePath);
+                       
                        // A null pathInfo or queryString is ignored and previous values remain untouched
                        if (pathInfo != null)
                                Request.SetPathInfo (pathInfo);
@@ -444,14 +724,6 @@ namespace System.Web {
                                Request.QueryStringRaw = queryString;
                }
 
-#if NET_2_0
-               [MonoTODO]
-               public void RewritePath (string path, bool rebaseClientPath)
-               {
-                       throw new NotImplementedException ();
-               }
-#endif
-
 #region internals
                
                internal void SetSession (HttpSessionState state)
@@ -488,16 +760,43 @@ namespace System.Web {
 
                        set {
                                config_timeout = value;
+#if !TARGET_J2EE
+                               if (timer != null) {
+                                       TimeSpan remaining = value - (DateTime.UtcNow - time_stamp);
+                                       long remaining_ms = Math.Max ((long)remaining.TotalMilliseconds, 0);
+
+                                       // See http://msdn2.microsoft.com/en-us/library/7hs7492w.aspx
+                                       if (remaining_ms > 4294967294)
+                                               remaining_ms = 4294967294;
+                                       
+                                       timer.Change (remaining_ms, (long)Timeout.Infinite);
+                               }
+#endif
                        }
                }
 
-               internal bool CheckIfTimeout (DateTime t)
-               {
-                       if (Interlocked.CompareExchange (ref timeout_possible, 0, 0) == 0)
-                               return false;
-
-                       TimeSpan ts = t - time_stamp;
-                       return (ts > ConfigTimeout);
+#if !TARGET_J2EE
+               void TimeoutReached(object state) {
+                       HttpRuntime.QueuePendingRequest (false);
+                       if (Interlocked.CompareExchange (ref timeout_possible, 0, 0) == 0) {
+                               timer.Change(2000, 0);
+                               return;                 
+                       }
+                       StopTimeoutTimer();
+                       
+                       thread.Abort (new StepTimeout ());
+               }
+               
+               internal void StartTimeoutTimer() {
+                       thread = Thread.CurrentThread;
+                       timer = new Timer (TimeoutReached, null, (int)ConfigTimeout.TotalMilliseconds, Timeout.Infinite);
+               }
+               
+               internal void StopTimeoutTimer() {
+                       if(timer != null) {
+                               timer.Dispose ();
+                               timer = null;
+                       }
                }
 
                internal bool TimeoutPossible {
@@ -513,20 +812,11 @@ namespace System.Web {
                {
                        Interlocked.CompareExchange (ref timeout_possible, 0, 1);
                }
-#endregion
-
-#if NET_2_0
-               Page last_page;
-               
-               internal Page LastPage {
-                       get {
-                               return last_page;
-                       }
-
-                       set {
-                               last_page = value;
-                       }
-               }
 #endif
+#endregion
+       }
+       
+       class StepTimeout
+       {
        }
 }