2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[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                 [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 #endif
108                 
109                 public HttpContext (HttpWorkerRequest wr)
110                 {
111                         WorkerRequest = wr;
112                         request = new HttpRequest (WorkerRequest, this);
113                         response = new HttpResponse (WorkerRequest, this);
114                 }
115
116                 public HttpContext (HttpRequest request, HttpResponse response)
117                 {
118                         this.request = request;
119                         this.response = response;
120                         
121                 }
122
123                 internal bool IsProcessingInclude {
124                         get { return _isProcessingInclude; }
125                         set { _isProcessingInclude = value; }
126                 }
127
128                 public Exception [] AllErrors {
129                         get {
130                                 if (errors == null)
131                                         return null;
132
133                                 if (errors is Exception){
134                                         Exception [] all = new Exception [1];
135                                         all [0] = (Exception) errors;
136                                         return all;
137                                 } 
138                                 return (Exception []) (((ArrayList) errors).ToArray (typeof (Exception)));
139                         }
140                 }
141
142                 public HttpApplicationState Application {
143                         get {
144                                 return HttpApplicationFactory.ApplicationState;
145                         }
146                 }
147
148                 public HttpApplication ApplicationInstance {
149                         get {
150                                 return app_instance;
151                         }
152
153                         set {
154                                 app_instance = value;
155                         }
156                               
157                 }
158
159                 public Cache Cache {
160                         get {
161                                 return HttpRuntime.Cache;
162                         }
163                 }
164
165                 internal Cache InternalCache {
166                         get {
167                                 return HttpRuntime.InternalCache;
168                         }
169                 }
170                 
171                 //
172                 // The "Current" property is set just after we have constructed it with 
173                 // the 'HttpContext (HttpWorkerRequest)' constructor.
174                 //
175 #if !TARGET_JVM // No remoting CallContext support in Grasshopper
176                 public static HttpContext Current {
177                         get {
178                                 return (HttpContext) CallContext.GetData ("c");
179                         }
180
181                         set {
182                                 CallContext.SetData ("c", value);
183                         }
184                 }
185 #endif
186
187                 public Exception Error {
188                         get {
189                                 if (errors == null || (errors is Exception))
190                                         return (Exception) errors;
191                                 return (Exception) (((ArrayList) errors) [0]);
192                         }
193                 }
194
195                 public IHttpHandler Handler {
196                         get {
197                                 return handler;
198                         }
199
200                         set {
201                                 handler = value;
202                         }
203                 }
204
205                 public bool IsCustomErrorEnabled {
206                         get {
207                                 try {
208                                         return IsCustomErrorEnabledUnsafe;
209                                 }
210                                 catch {
211                                         return false;
212                                 }
213                         }
214                 }
215
216                 internal bool IsCustomErrorEnabledUnsafe {
217                         get {
218 #if NET_2_0
219                                 CustomErrorsSection cfg = (CustomErrorsSection) WebConfigurationManager.GetSection ("system.web/customErrors");
220 #else
221                                 CustomErrorsConfig cfg = null;
222                                 try {
223                                         cfg = (CustomErrorsConfig) GetConfig ("system.web/customErrors");
224                                 } catch {
225                                 }
226
227                                 if (cfg == null)
228                                         return false;
229 #endif
230
231                                 if (cfg.Mode == CustomErrorMode.On)
232                                         return true;
233
234                                 return (cfg.Mode == CustomErrorMode.RemoteOnly) && !Request.IsLocal;
235                         }
236                 }
237 #if !TARGET_JVM
238                 public bool IsDebuggingEnabled {
239                         get { return HttpRuntime.IsDebuggingEnabled; }
240                 }
241 #endif
242                 public IDictionary Items {
243                         get {
244                                 if (items == null)
245                                         items = new Hashtable ();
246                                 return items;
247                         }
248                 }
249
250                 public HttpRequest Request {
251                         get {
252                                 return request;
253                         }
254                 }
255
256                 public HttpResponse Response {
257                         get {
258                                 return response;
259                         }
260                 }
261
262                 public HttpServerUtility Server {
263                         get {
264                                 if (server == null)
265                                         server = new HttpServerUtility (this);
266                                 return server;
267                         }
268                 }
269
270                 public HttpSessionState Session {
271                         get {
272                                 return session_state;
273                         }
274                 }
275
276                 public bool SkipAuthorization {
277                         get {
278                                 return skip_authorization;
279                         }
280
281                         [SecurityPermission (SecurityAction.Demand, ControlPrincipal = true)]
282                         set {
283                                 skip_authorization = value;
284                         }
285                 }
286
287                 public DateTime Timestamp {
288                         get {
289                                 return time_stamp.ToLocalTime ();
290                         }
291                 }
292                 
293                 public TraceContext Trace {
294                         get {
295                                 if (trace_context == null)
296                                         trace_context = new TraceContext (this);
297                                 return trace_context;
298                         }
299                 }
300
301                 public IPrincipal User {
302                         get {
303                                 return user;
304                         }
305
306                         [SecurityPermission (SecurityAction.Demand, ControlPrincipal = true)]
307                         set {
308                                 user = value;
309                         }
310                 }
311
312 #if NET_2_0
313                 internal bool MapRequestHandlerDone {
314                         get;
315                         set;
316                 }
317                 
318                 // The two properties below are defined only when the IIS7 integrated mode is used.
319                 // They are useless under Mono
320                 public RequestNotification CurrentNotification {
321                         get { throw new PlatformNotSupportedException ("This property is not supported on Mono.");  }
322                 }
323
324                 public bool IsPostNotification {
325                         get { throw new PlatformNotSupportedException ("This property is not supported on Mono."); }
326                 }
327                 
328                 internal void PushHandler (IHttpHandler handler)
329                 {
330                         if (handler == null)
331                                 return;
332                         if (handlers == null)
333                                 handlers = new LinkedList <IHttpHandler> ();
334                         handlers.AddLast (handler);
335                 }
336
337                 internal void PopHandler ()
338                 {
339                         if (handlers == null || handlers.Count == 0)
340                                 return;
341                         handlers.RemoveLast ();
342                 }
343                 
344                 IHttpHandler GetCurrentHandler ()
345                 {
346                         if (handlers == null || handlers.Count == 0)
347                                 return null;
348                         
349                         return handlers.Last.Value;
350                 }
351
352                 IHttpHandler GetPreviousHandler ()
353                 {
354                         if (handlers == null || handlers.Count <= 1)
355                                 return null;
356                         LinkedListNode <IHttpHandler> previous = handlers.Last.Previous;
357                         if (previous != null)
358                                 return previous.Value;
359                         return null;
360                 }
361                 
362                 public IHttpHandler CurrentHandler {
363                         get { return GetCurrentHandler (); }
364                 }
365
366                 public IHttpHandler PreviousHandler {
367                         get { return GetPreviousHandler (); }
368                 }
369
370                 internal bool ProfileInitialized {
371                         get { return profile != null; }
372                 }
373
374                 public ProfileBase Profile {
375                         get {
376                                 if (profile == null) {
377                                         if (Request.IsAuthenticated)
378                                                 profile = ProfileBase.Create (User.Identity.Name);
379                                         else
380                                                 profile = ProfileBase.Create (Request.AnonymousID, false);
381                                 }
382                                 return profile;
383                         }
384
385                         internal set {
386                                 profile = value;
387                         }
388                 }
389 #endif
390
391                 public void AddError (Exception errorInfo)
392                 {
393                         if (errors == null){
394                                 errors = errorInfo;
395                                 return;
396                         }
397                         ArrayList l;
398                         if (errors is Exception){
399                                 l = new ArrayList ();
400                                 l.Add (errors);
401                                 errors = l;
402                         } else 
403                                 l = (ArrayList) errors;
404                         l.Add (errorInfo);
405                 }
406
407                 internal void ClearError (Exception e)
408                 {
409                         if (errors == e)
410                                 errors = null;
411                 }
412
413                 internal bool HasError (Exception e)
414                 {
415                         if (errors == e)
416                                 return true;
417
418                         return (errors is ArrayList) ?
419                                 ((ArrayList) errors).Contains (e) : false;
420                 }
421
422                 public void ClearError ()
423                 {
424                         errors = null;
425                 }
426
427 #if NET_2_0
428                 [Obsolete ("use WebConfigurationManager.GetWebApplicationSection")]
429 #endif
430                 public static object GetAppConfig (string name)
431                 {
432                         object o = ConfigurationSettings.GetConfig (name);
433
434                         return o;
435                 }
436
437 #if NET_2_0
438                 [Obsolete ("see GetSection")]
439 #endif
440                 public object GetConfig (string name)
441                 {
442 #if NET_2_0
443                         return GetSection (name);
444 #else
445                         return WebConfigurationSettings.GetConfig (name, this);
446 #endif
447                 }
448
449 #if NET_2_0
450                 public static object GetGlobalResourceObject (string classKey, string resourceKey)
451                 {
452                         return GetGlobalResourceObject (classKey, resourceKey, Thread.CurrentThread.CurrentUICulture);
453                 }
454
455                 static bool EnsureProviderFactory ()
456                 {
457                         if (resource_providers == null)
458                                 resource_providers = new Dictionary <string, IResourceProvider> ();
459
460                         if (provider_factory != null)
461                                 return true;
462                         
463                         GlobalizationSection gs = WebConfigurationManager.GetSection ("system.web/globalization") as GlobalizationSection;
464
465                         if (gs == null)
466                                 return false;
467
468                         String rsfTypeName = gs.ResourceProviderFactoryType;
469                         bool usingDefault = false;
470                         if (String.IsNullOrEmpty (rsfTypeName)) {
471                                 usingDefault = true;
472                                 rsfTypeName = typeof (DefaultResourceProviderFactory).AssemblyQualifiedName;
473                         }
474                         
475                         Type rsfType = HttpApplication.LoadType (rsfTypeName, true);
476                         ResourceProviderFactory rpf = Activator.CreateInstance (rsfType) as ResourceProviderFactory;
477                         
478                         if (rpf == null && usingDefault)
479                                 return false;
480
481                         provider_factory = rpf;
482                         if (usingDefault)
483                                 default_provider_factory = rpf as DefaultResourceProviderFactory;
484                         
485                         return true;
486                 }
487
488                 internal static IResourceProvider GetResourceProvider (string key, bool isLocal)
489                 {
490                         if (!EnsureProviderFactory ())
491                                 return null;
492
493                         // TODO: check if it makes sense to cache the providers and, if yes, maybe
494                         // we should expire the entries (or just store them in InternalCache?)
495                         IResourceProvider rp = null;
496                         if (!resource_providers.TryGetValue (key, out rp)) {
497                                 if (isLocal)
498                                         rp = provider_factory.CreateLocalResourceProvider (key);
499                                 else
500                                         rp = provider_factory.CreateGlobalResourceProvider (key);
501                                 
502                                 if (rp == null) {
503                                         if (isLocal)
504                                                 rp = DefaultProviderFactory.CreateLocalResourceProvider (key);
505                                         else
506                                                 rp = DefaultProviderFactory.CreateGlobalResourceProvider (key);
507
508                                         if (rp == null)
509                                                 return null;
510                                 }
511                                 
512                                 resource_providers.Add (key, rp);
513                         }
514
515                         return rp;
516                 }
517
518                 static object GetGlobalObjectFromFactory (string classKey, string resourceKey, CultureInfo culture)
519                 {
520                         // FIXME: Retention of data
521                         IResourceProvider rp = GetResourceProvider (classKey, false);
522                         if (rp == null)
523                                 return null;
524                         
525                         return rp.GetObject (resourceKey, culture);
526                 }
527                 
528                 public static object GetGlobalResourceObject (string classKey, string resourceKey, CultureInfo culture)
529                 {
530                         return GetGlobalObjectFromFactory (classKey, resourceKey, culture);
531                 }
532
533                 public static object GetLocalResourceObject (string virtualPath, string resourceKey)
534                 {
535                         return GetLocalResourceObject (virtualPath, resourceKey, Thread.CurrentThread.CurrentUICulture);
536                 }
537
538                 static object GetLocalObjectFromFactory (string virtualPath, string resourceKey, CultureInfo culture)
539                 {
540                         IResourceProvider rp = GetResourceProvider (virtualPath, true);
541                         if (rp == null)
542                                 return null;
543                         
544                         return rp.GetObject (resourceKey, culture);
545                 }
546                 
547                 public static object GetLocalResourceObject (string virtualPath, string resourceKey, CultureInfo culture)
548                 {
549                         if (!VirtualPathUtility.IsAbsolute (virtualPath))
550                                 throw new ArgumentException ("The specified virtualPath was not rooted.");
551
552                         return GetLocalObjectFromFactory (virtualPath, resourceKey, culture);
553                 }
554
555                 public object GetSection (string name)
556                 {
557                         return WebConfigurationManager.GetSection (name);
558                 }
559 #endif
560                 object IServiceProvider.GetService (Type service)
561                 {
562                         if (service == typeof (HttpWorkerRequest))
563                                 return WorkerRequest;
564
565                         //
566                         // We return everything out of properties in case
567                         // they are dynamically computed in some form in the future.
568                         //
569                         if (service == typeof (HttpApplication))
570                                 return ApplicationInstance;
571
572                         if (service == typeof (HttpRequest))
573                                 return Request;
574
575                         if (service == typeof (HttpResponse))
576                                 return Response;
577
578                         if (service == typeof (HttpSessionState))
579                                 return Session;
580
581                         if (service == typeof (HttpApplicationState))
582                                 return Application;
583
584                         if (service == typeof (IPrincipal))
585                                 return User;
586
587                         if (service == typeof (Cache))
588                                 return Cache;
589
590                         if (service == typeof (HttpContext))
591                                 return Current;
592
593                         if (service == typeof (IHttpHandler))
594                                 return Handler;
595
596                         if (service == typeof (HttpServerUtility))
597                                 return Server;
598                         
599                         if (service == typeof (TraceContext))
600                                 return Trace;
601                         
602                         return null;
603                 }
604
605 #if NET_2_0
606                 public void RemapHandler (IHttpHandler handler)
607                 {
608                         if (MapRequestHandlerDone)
609                                 throw new InvalidOperationException ("The RemapHandler method was called after the MapRequestHandler event occurred.");
610                         Handler = handler;
611                 }
612 #endif
613                 
614                 public void RewritePath (string path)
615                 {
616 #if NET_2_0
617                         RewritePath (path, true);
618 #else
619                         RewritePath (path, false);
620 #endif
621                 }
622
623                 public void RewritePath (string filePath, string pathInfo, string queryString)
624                 {
625                         RewritePath (filePath, pathInfo, queryString, false);
626                 }
627
628 #if NET_2_0
629                 public
630 #else
631                 internal
632 #endif
633                 void RewritePath (string path, bool rebaseClientPath)
634                 {
635                         int qmark = path.IndexOf ('?');
636                         if (qmark != -1)
637                                 RewritePath (path.Substring (0, qmark), "", path.Substring (qmark + 1), rebaseClientPath);
638                         else
639                                 RewritePath (path, null, null, rebaseClientPath);
640                 }
641
642 #if NET_2_0
643                 public
644 #else
645                 internal
646 #endif
647                 void RewritePath (string filePath, string pathInfo, string queryString, bool setClientFilePath)
648                 {
649                         if (filePath == null)
650                                 throw new ArgumentNullException ("filePath");
651                         if (!VirtualPathUtility.IsValidVirtualPath (filePath))
652                                 throw new HttpException ("'" + HttpUtility.HtmlEncode (filePath) + "' is not a valid virtual path.");
653
654                         bool pathRelative = VirtualPathUtility.IsAppRelative (filePath);
655                         bool pathAbsolute = pathRelative ? false : VirtualPathUtility.IsAbsolute (filePath);
656                         if (pathRelative || pathAbsolute) {
657                                 bool needSubstring = false;
658
659                                 if (pathRelative && filePath.Length > 1)
660                                         needSubstring = true;
661
662                                 string bvd = Request.BaseVirtualDir;
663                                 if (bvd.Length > 1)
664                                         bvd += "/";
665
666                                 string canonizedFilePath = VirtualPathUtility.Canonize (filePath);
667                                 filePath = VirtualPathUtility.Combine (bvd, needSubstring ? canonizedFilePath.Substring (2) : canonizedFilePath);
668                         } else 
669                                 filePath = VirtualPathUtility.Combine (VirtualPathUtility.GetDirectory (Request.FilePath), filePath);
670                         
671                         if (!StrUtils.StartsWith (filePath, HttpRuntime.AppDomainAppVirtualPath))
672                                 throw new HttpException (404, "The virtual path '" + HttpUtility.HtmlEncode (filePath) + "' maps to another application.", filePath);
673                         
674                         Request.SetCurrentExePath (filePath);
675                         if (setClientFilePath)
676                                 Request.SetFilePath (filePath);
677                         
678                         // A null pathInfo or queryString is ignored and previous values remain untouched
679                         if (pathInfo != null)
680                                 Request.SetPathInfo (pathInfo);
681
682                         if (queryString != null)
683                                 Request.QueryStringRaw = queryString;
684                 }
685
686 #region internals
687                 
688                 internal void SetSession (HttpSessionState state)
689                 {
690                         session_state = state;
691                 }
692
693                 // URL of a page used for error redirection.
694                 internal string ErrorPage {
695                         get {
696                                 return error_page;
697                         }
698
699                         set {
700                                 error_page = value;
701                         }
702                 }
703
704                 internal TimeSpan ConfigTimeout {
705                         get {
706                                 if (config_timeout == null) {
707 #if NET_2_0
708                                         HttpRuntimeSection section = (HttpRuntimeSection)WebConfigurationManager.GetSection ("system.web/httpRuntime");
709                                         config_timeout = section.ExecutionTimeout;
710 #else
711                                         HttpRuntimeConfig config = (HttpRuntimeConfig)
712                                                                 GetConfig ("system.web/httpRuntime");
713                                         config_timeout = new TimeSpan (0, 0, config.ExecutionTimeout);
714 #endif
715                                 }
716
717                                 return (TimeSpan) config_timeout;
718                         }
719
720                         set {
721                                 config_timeout = value;
722 #if !TARGET_J2EE
723                                 if (timer != null) {
724                                         TimeSpan remaining = value - (DateTime.UtcNow - time_stamp);
725                                         long remaining_ms = Math.Max ((long)remaining.TotalMilliseconds, 0);
726
727                                         // See http://msdn2.microsoft.com/en-us/library/7hs7492w.aspx
728                                         if (remaining_ms > 4294967294)
729                                                 remaining_ms = 4294967294;
730                                         
731                                         timer.Change (remaining_ms, (long)Timeout.Infinite);
732                                 }
733 #endif
734                         }
735                 }
736
737 #if !TARGET_J2EE
738                 void TimeoutReached(object state) {
739                         HttpRuntime.QueuePendingRequest (false);
740                         if (Interlocked.CompareExchange (ref timeout_possible, 0, 0) == 0) {
741                                 timer.Change(2000, 0);
742                                 return;                 
743                         }
744                         StopTimeoutTimer();
745                         
746                         thread.Abort (new StepTimeout ());
747                 }
748                 
749                 internal void StartTimeoutTimer() {
750                         thread = Thread.CurrentThread;
751                         timer = new Timer (TimeoutReached, null, (int)ConfigTimeout.TotalMilliseconds, Timeout.Infinite);
752                 }
753                 
754                 internal void StopTimeoutTimer() {
755                         if(timer != null) {
756                                 timer.Dispose ();
757                                 timer = null;
758                         }
759                 }
760
761                 internal bool TimeoutPossible {
762                         get { return (Interlocked.CompareExchange (ref timeout_possible, 1, 1) == 1); }
763                 }
764
765                 internal void BeginTimeoutPossible ()
766                 {
767                         timeout_possible = 1;
768                 }
769
770                 internal void EndTimeoutPossible ()
771                 {
772                         Interlocked.CompareExchange (ref timeout_possible, 0, 1);
773                 }
774 #endif
775 #endregion
776         }
777         
778         class StepTimeout
779         {
780         }
781 }