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