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