* HttpContext.cs: added resource manager caching in GetResourceObject
[mono.git] / mcs / class / System.Web / System.Web / HttpContext.cs
1 //
2 // System.Web.HttpContext.cs 
3 //
4 // Author:
5 //      Miguel de Icaza (miguel@novell.com)
6 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
7 //
8
9 //
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections;
33 using System.Configuration;
34 using System.Globalization;
35 using System.Runtime.Remoting.Messaging;
36 using System.Security.Permissions;
37 using System.Security.Principal;
38 using System.Threading;
39 using System.Web.Caching;
40 using System.Web.Configuration;
41 using System.Web.SessionState;
42 using System.Web.UI;
43 using System.Web.Util;
44 #if NET_2_0
45 using System.Collections.Generic;
46 using System.IO;
47 using System.Reflection;
48 using System.Resources;
49 using System.Web.Compilation;
50 using System.Web.Profile;
51 using CustomErrorMode = System.Web.Configuration.CustomErrorsMode;
52 #endif
53
54 namespace System.Web {
55         // CAS - no InheritanceDemand here as the class is sealed
56         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
57         public sealed partial class HttpContext : IServiceProvider {
58                 internal HttpWorkerRequest WorkerRequest;
59                 HttpApplication app_instance;
60                 HttpRequest request;
61                 HttpResponse response;
62                 HttpSessionState session_state;
63                 HttpServerUtility server;
64                 TraceContext trace_context;
65                 IHttpHandler handler;
66                 string error_page;
67                 bool skip_authorization = false;
68                 IPrincipal user;
69                 object errors;
70                 Hashtable items;
71                 object config_timeout;
72                 int timeout_possible;
73                 DateTime time_stamp = DateTime.UtcNow;
74                 Timer timer;
75                 Thread thread;
76                 bool _isProcessingInclude;
77
78 #if NET_2_0
79                 
80 #if TARGET_JVM
81                 const string app_global_res_key = "HttpContext.app_global_res_key";
82                 internal static Assembly AppGlobalResourcesAssembly {
83                         get { return (Assembly) AppDomain.CurrentDomain.GetData (app_global_res_key); }
84                         set { AppDomain.CurrentDomain.SetData (app_global_res_key, value); }
85                 }
86 #else
87                 static Hashtable resourceManagerCache = new Hashtable ();
88                 RequestNotification _currentNotification;
89                 internal static Assembly AppGlobalResourcesAssembly;
90 #endif
91                 ProfileBase profile = null;
92                 LinkedList<IHttpHandler> handlers;
93 #endif
94                 
95                 public HttpContext (HttpWorkerRequest wr)
96                 {
97                         WorkerRequest = wr;
98                         request = new HttpRequest (WorkerRequest, this);
99                         response = new HttpResponse (WorkerRequest, this);
100                 }
101
102                 public HttpContext (HttpRequest request, HttpResponse response)
103                 {
104                         this.request = request;
105                         this.response = response;
106                         
107                 }
108
109                 internal bool IsProcessingInclude {
110                         get { return _isProcessingInclude; }
111                         set { _isProcessingInclude = value; }
112                 }
113
114                 public Exception [] AllErrors {
115                         get {
116                                 if (errors == null)
117                                         return null;
118
119                                 if (errors is Exception){
120                                         Exception [] all = new Exception [1];
121                                         all [0] = (Exception) errors;
122                                         return all;
123                                 } 
124                                 return (Exception []) (((ArrayList) errors).ToArray (typeof (Exception)));
125                         }
126                 }
127
128                 public HttpApplicationState Application {
129                         get {
130                                 return HttpApplicationFactory.ApplicationState;
131                         }
132                 }
133
134                 public HttpApplication ApplicationInstance {
135                         get {
136                                 return app_instance;
137                         }
138
139                         set {
140                                 app_instance = value;
141                         }
142                               
143                 }
144
145                 public Cache Cache {
146                         get {
147                                 return HttpRuntime.Cache;
148                         }
149                 }
150
151                 internal Cache InternalCache {
152                         get {
153                                 return HttpRuntime.InternalCache;
154                         }
155                 }
156                 
157                 //
158                 // The "Current" property is set just after we have constructed it with 
159                 // the 'HttpContext (HttpWorkerRequest)' constructor.
160                 //
161 #if !TARGET_JVM // No remoting CallContext support in Grasshopper
162                 public static HttpContext Current {
163                         get {
164                                 return (HttpContext) CallContext.GetData ("c");
165                         }
166
167                         set {
168                                 CallContext.SetData ("c", value);
169                         }
170                 }
171 #endif
172
173                 public Exception Error {
174                         get {
175                                 if (errors == null || (errors is Exception))
176                                         return (Exception) errors;
177                                 return (Exception) (((ArrayList) errors) [0]);
178                         }
179                 }
180
181                 public IHttpHandler Handler {
182                         get {
183                                 return handler;
184                         }
185
186                         set {
187                                 handler = value;
188                         }
189                 }
190
191                 public bool IsCustomErrorEnabled {
192                         get {
193                                 try {
194                                         return IsCustomErrorEnabledUnsafe;
195                                 }
196                                 catch {
197                                         return false;
198                                 }
199                         }
200                 }
201
202                 internal bool IsCustomErrorEnabledUnsafe {
203                         get {
204 #if NET_2_0
205                                 CustomErrorsSection cfg = (CustomErrorsSection) WebConfigurationManager.GetSection ("system.web/customErrors");
206 #else
207                                 CustomErrorsConfig cfg = null;
208                                 try {
209                                         cfg = (CustomErrorsConfig) GetConfig ("system.web/customErrors");
210                                 } catch {
211                                 }
212
213                                 if (cfg == null)
214                                         return false;
215 #endif
216
217                                 if (cfg.Mode == CustomErrorMode.On)
218                                         return true;
219
220                                 return (cfg.Mode == CustomErrorMode.RemoteOnly) && !Request.IsLocal;
221                         }
222                 }
223 #if !TARGET_JVM
224                 public bool IsDebuggingEnabled {
225                         get {
226 #if NET_2_0
227                                 CompilationSection section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
228                                 return section.Debug;
229 #else
230                                 try {
231                                         return CompilationConfiguration.GetInstance (this).Debug;
232                                 } catch {
233                                         return false;
234                                 }
235 #endif
236                         }
237                 }
238 #endif
239                 public IDictionary Items {
240                         get {
241                                 if (items == null)
242                                         items = new Hashtable ();
243                                 return items;
244                         }
245                 }
246
247                 public HttpRequest Request {
248                         get {
249                                 return request;
250                         }
251                 }
252
253                 public HttpResponse Response {
254                         get {
255                                 return response;
256                         }
257                 }
258
259                 public HttpServerUtility Server {
260                         get {
261                                 if (server == null)
262                                         server = new HttpServerUtility (this);
263                                 return server;
264                         }
265                 }
266
267                 public HttpSessionState Session {
268                         get {
269                                 return session_state;
270                         }
271                 }
272
273                 public bool SkipAuthorization {
274                         get {
275                                 return skip_authorization;
276                         }
277
278                         [SecurityPermission (SecurityAction.Demand, ControlPrincipal = true)]
279                         set {
280                                 skip_authorization = value;
281                         }
282                 }
283
284                 public DateTime Timestamp {
285                         get {
286                                 return time_stamp.ToLocalTime ();
287                         }
288                 }
289                 
290                 public TraceContext Trace {
291                         get {
292                                 if (trace_context == null)
293                                         trace_context = new TraceContext (this);
294                                 return trace_context;
295                         }
296                 }
297
298                 public IPrincipal User {
299                         get {
300                                 return user;
301                         }
302
303                         [SecurityPermission (SecurityAction.Demand, ControlPrincipal = true)]
304                         set {
305                                 user = value;
306                         }
307                 }
308
309 #if NET_2_0
310                 // The two properties below are defined only when the IIS7 integrated mode is used.
311                 // They are useless under Mono
312                 public RequestNotification CurrentNotification {
313                         get { throw new PlatformNotSupportedException ("This property is not supported on Mono.");  }
314                 }
315
316                 public bool IsPostNotification {
317                         get { throw new PlatformNotSupportedException ("This property is not supported on Mono."); }
318                 }
319                 
320                 internal void PushHandler (IHttpHandler handler)
321                 {
322                         if (handler == null)
323                                 return;
324                         if (handlers == null)
325                                 handlers = new LinkedList <IHttpHandler> ();
326                         handlers.AddLast (handler);
327                 }
328
329                 internal void PopHandler ()
330                 {
331                         if (handlers == null || handlers.Count == 0)
332                                 return;
333                         handlers.RemoveLast ();
334                 }
335                 
336                 IHttpHandler GetCurrentHandler ()
337                 {
338                         if (handlers == null || handlers.Count == 0)
339                                 return null;
340                         
341                         return handlers.Last.Value;
342                 }
343
344                 IHttpHandler GetPreviousHandler ()
345                 {
346                         if (handlers == null || handlers.Count <= 1)
347                                 return null;
348                         LinkedListNode <IHttpHandler> previous = handlers.Last.Previous;
349                         if (previous != null)
350                                 return previous.Value;
351                         return null;
352                 }
353                 
354                 public IHttpHandler CurrentHandler {
355                         get { return GetCurrentHandler (); }
356                 }
357
358                 public IHttpHandler PreviousHandler {
359                         get { return GetPreviousHandler (); }
360                 }
361
362                 internal bool ProfileInitialized {
363                         get { return profile != null; }
364                 }
365
366                 public ProfileBase Profile {
367                         get {
368                                 if (profile == null) {
369                                         if (Request.IsAuthenticated)
370                                                 profile = ProfileBase.Create (User.Identity.Name);
371                                         else
372                                                 profile = ProfileBase.Create (Request.AnonymousID, false);
373                                 }
374                                 return profile;
375                         }
376
377                         internal set {
378                                 profile = value;
379                         }
380                 }
381 #endif
382
383                 public void AddError (Exception errorInfo)
384                 {
385                         if (errors == null){
386                                 errors = errorInfo;
387                                 return;
388                         }
389                         ArrayList l;
390                         if (errors is Exception){
391                                 l = new ArrayList ();
392                                 l.Add (errors);
393                                 errors = l;
394                         } else 
395                                 l = (ArrayList) errors;
396                         l.Add (errorInfo);
397                 }
398
399                 internal void ClearError (Exception e)
400                 {
401                         if (errors == e)
402                                 errors = null;
403                 }
404
405                 internal bool HasError (Exception e)
406                 {
407                         if (errors == e)
408                                 return true;
409
410                         return (errors is ArrayList) ?
411                                 ((ArrayList) errors).Contains (e) : false;
412                 }
413
414                 public void ClearError ()
415                 {
416                         errors = null;
417                 }
418
419 #if NET_2_0
420                 [Obsolete ("use WebConfigurationManager.GetWebApplicationSection")]
421 #endif
422                 public static object GetAppConfig (string name)
423                 {
424                         object o = ConfigurationSettings.GetConfig (name);
425
426                         return o;
427                 }
428
429 #if NET_2_0
430                 [Obsolete ("see GetSection")]
431 #endif
432                 public object GetConfig (string name)
433                 {
434 #if NET_2_0
435                         return GetSection (name);
436 #else
437                         return WebConfigurationSettings.GetConfig (name, this);
438 #endif
439                 }
440
441 #if NET_2_0
442                 static object GetResourceObject (string classKey, string resourceKey, CultureInfo culture, Assembly assembly)
443                 {
444                         ResourceManager rm;
445                         try {
446                                 ResourceManagerCacheKey key = new ResourceManagerCacheKey (classKey, assembly);
447                                 rm = (ResourceManager) resourceManagerCache [key];
448
449                                 if (rm == null) {
450                                         rm = new ResourceManager (classKey, assembly);
451                                         rm.IgnoreCase = true;
452                                         Hashtable tmp = (Hashtable) resourceManagerCache.Clone ();
453                                         tmp[key] = rm;
454                                         resourceManagerCache = tmp;
455                                 }
456                                 
457                                 return rm.GetObject (resourceKey, culture);
458                         } catch (MissingManifestResourceException) {
459                                 throw;
460                         } catch (Exception ex) {
461                                 throw new HttpException ("Failed to retrieve the specified global resource object.", ex);
462                         }
463                 }
464                 
465                 public static object GetGlobalResourceObject (string classKey, string resourceKey)
466                 {
467                         return GetGlobalResourceObject (classKey, resourceKey, Thread.CurrentThread.CurrentUICulture);
468                 }
469
470                 public static object GetGlobalResourceObject (string classKey, string resourceKey, CultureInfo culture)
471                 {
472                         if (AppGlobalResourcesAssembly == null)
473                                 throw new MissingManifestResourceException ();
474                         return GetResourceObject ("Resources." + classKey, resourceKey, culture, AppGlobalResourcesAssembly);
475                 }
476
477                 public static object GetLocalResourceObject (string virtualPath, string resourceKey)
478                 {
479                         return GetLocalResourceObject (virtualPath, resourceKey, Thread.CurrentThread.CurrentUICulture);
480                 }
481
482                 public static object GetLocalResourceObject (string virtualPath, string resourceKey, CultureInfo culture)
483                 {
484                         if (!VirtualPathUtility.IsAbsolute (virtualPath))
485                                 throw new ArgumentException ("The specified virtualPath was not rooted.");
486                         
487                         string path = Path.GetDirectoryName (virtualPath);
488                         Assembly asm = AppResourcesCompiler.GetCachedLocalResourcesAssembly (path);
489                         if (asm == null) {
490                                 AppResourcesCompiler ac = new AppResourcesCompiler (path);
491                                 asm = ac.Compile ();
492                                 if (asm == null)
493                                         throw new MissingManifestResourceException ("A resource object was not found at the specified virtualPath.");
494                         }
495                         
496                         path = Path.GetFileName (virtualPath);
497                         return GetResourceObject (path, resourceKey, culture, asm);
498                 }
499
500                 public object GetSection (string name)
501                 {
502                         return WebConfigurationManager.GetSection (name);
503                 }
504
505                 private sealed class ResourceManagerCacheKey
506                 {
507                         readonly string _name;
508                         readonly Assembly _asm;
509
510                         public ResourceManagerCacheKey (string name, Assembly asm)
511                         {
512                                 _name = name;
513                                 _asm = asm;
514                         }
515
516                         public override bool Equals (object obj)
517                         {
518                                 if (!(obj is ResourceManagerCacheKey))
519                                         return false;
520                                 ResourceManagerCacheKey key = (ResourceManagerCacheKey) obj;
521                                 return key._asm == _asm && _name.Equals (key._name, StringComparison.Ordinal);
522                         }
523
524                         public override int GetHashCode ()
525                         {
526                                 return _name.GetHashCode () + _asm.GetHashCode ();
527                         }
528                 }
529 #endif
530                 object IServiceProvider.GetService (Type service)
531                 {
532                         if (service == typeof (HttpWorkerRequest))
533                                 return WorkerRequest;
534
535                         //
536                         // We return everything out of properties in case
537                         // they are dynamically computed in some form in the future.
538                         //
539                         if (service == typeof (HttpApplication))
540                                 return ApplicationInstance;
541
542                         if (service == typeof (HttpRequest))
543                                 return Request;
544
545                         if (service == typeof (HttpResponse))
546                                 return Response;
547
548                         if (service == typeof (HttpSessionState))
549                                 return Session;
550
551                         if (service == typeof (HttpApplicationState))
552                                 return Application;
553
554                         if (service == typeof (IPrincipal))
555                                 return User;
556
557                         if (service == typeof (Cache))
558                                 return Cache;
559
560                         if (service == typeof (HttpContext))
561                                 return Current;
562
563                         if (service == typeof (IHttpHandler))
564                                 return Handler;
565
566                         if (service == typeof (HttpServerUtility))
567                                 return Server;
568                         
569                         if (service == typeof (TraceContext))
570                                 return Trace;
571                         
572                         return null;
573                 }
574
575                 public void RewritePath (string path)
576                 {
577 #if NET_2_0
578                         RewritePath (path, true);
579 #else
580                         RewritePath (path, false);
581 #endif
582                 }
583
584                 public void RewritePath (string filePath, string pathInfo, string queryString)
585                 {
586                         RewritePath (filePath, pathInfo, queryString, false);
587                 }
588
589 #if NET_2_0
590                 public
591 #else
592                 internal
593 #endif
594                 void RewritePath (string path, bool rebaseClientPath)
595                 {
596                         int qmark = path.IndexOf ('?');
597                         if (qmark != -1)
598                                 RewritePath (path.Substring (0, qmark), "", path.Substring (qmark + 1), rebaseClientPath);
599                         else
600                                 RewritePath (path, null, null, rebaseClientPath);
601                 }
602
603 #if NET_2_0
604                 public
605 #else
606                 internal
607 #endif
608                 void RewritePath (string filePath, string pathInfo, string queryString, bool setClientFilePath)
609                 {
610                         filePath = UrlUtils.Combine (Request.BaseVirtualDir, filePath);
611                         if (!StrUtils.StartsWith (filePath, HttpRuntime.AppDomainAppVirtualPath))
612                                 throw new HttpException (404, "The virtual path '" + filePath +
613                                         "' maps to another application.");
614
615                         Request.SetCurrentExePath (filePath);
616                         // A null pathInfo or queryString is ignored and previous values remain untouched
617                         if (pathInfo != null)
618                                 Request.SetPathInfo (pathInfo);
619
620                         if (queryString != null)
621                                 Request.QueryStringRaw = queryString;
622 #if NET_2_0
623                         if (setClientFilePath)
624                                 Request.SetFilePath (filePath);
625 #endif
626                 }
627
628 #region internals
629                 
630                 internal void SetSession (HttpSessionState state)
631                 {
632                         session_state = state;
633                 }
634
635                 // URL of a page used for error redirection.
636                 internal string ErrorPage {
637                         get {
638                                 return error_page;
639                         }
640
641                         set {
642                                 error_page = value;
643                         }
644                 }
645
646                 internal TimeSpan ConfigTimeout {
647                         get {
648                                 if (config_timeout == null) {
649 #if NET_2_0
650                                         HttpRuntimeSection section = (HttpRuntimeSection)WebConfigurationManager.GetSection ("system.web/httpRuntime");
651                                         config_timeout = section.ExecutionTimeout;
652 #else
653                                         HttpRuntimeConfig config = (HttpRuntimeConfig)
654                                                                 GetConfig ("system.web/httpRuntime");
655                                         config_timeout = new TimeSpan (0, 0, config.ExecutionTimeout);
656 #endif
657                                 }
658
659                                 return (TimeSpan) config_timeout;
660                         }
661
662                         set {
663                                 config_timeout = value;
664 #if !TARGET_J2EE
665                                 if (timer != null) {
666                                         TimeSpan remaining = value - (DateTime.UtcNow - time_stamp);
667                                         int remaining_ms = Math.Max ((int)remaining.TotalMilliseconds, 0);
668                                         timer.Change (remaining_ms, Timeout.Infinite);
669                                 }
670 #endif
671                         }
672                 }
673
674 #if !TARGET_J2EE
675                 void TimeoutReached(object state) {
676                         if (Interlocked.CompareExchange (ref timeout_possible, 0, 0) == 0) {
677                                 timer.Change(2000, 0);
678                                 return;                 
679                         }
680                         StopTimeoutTimer();
681                         
682                         thread.Abort (new StepTimeout ());
683                 }
684                 
685                 internal void StartTimeoutTimer() {
686                         thread = Thread.CurrentThread;
687                         timer = new Timer (TimeoutReached, null, (int)ConfigTimeout.TotalMilliseconds, Timeout.Infinite);
688                 }
689                 
690                 internal void StopTimeoutTimer() {
691                         if(timer != null) {
692                                 timer.Dispose ();
693                                 timer = null;
694                         }
695                 }
696
697                 internal bool TimeoutPossible {
698                         get { return (Interlocked.CompareExchange (ref timeout_possible, 1, 1) == 1); }
699                 }
700
701                 internal void BeginTimeoutPossible ()
702                 {
703                         timeout_possible = 1;
704                 }
705
706                 internal void EndTimeoutPossible ()
707                 {
708                         Interlocked.CompareExchange (ref timeout_possible, 0, 1);
709                 }
710 #endif
711 #endregion
712         }
713         
714         class StepTimeout
715         {
716         }
717 }