2007-05-14 Igor Zelmanovich <igorz@mainsoft.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpApplication.cs
1 //
2 // System.Web.HttpApplication.cs 
3 //
4 // Author:
5 //      Miguel de Icaza (miguel@novell.com)
6 //      Gonzalo Paniagua (gonzalo@ximian.com)
7 //    
8 //
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 // The Application Processing Pipeline.
31 // 
32 //     The Http application pipeline implemented in this file is a
33 //     beautiful thing.  The application pipeline invokes a number of
34 //     hooks at various stages of the processing of a request.  These
35 //     hooks can be either synchronous or can be asynchronous.
36 //     
37 //     The pipeline must ensure that every step is completed before
38 //     moving to the next step.  A trivial thing for synchronous
39 //     hooks, but asynchronous hooks introduce an extra layer of
40 //     complexity: when the hook is invoked, the thread must
41 //     relinquish its control so that the thread can be reused in
42 //     another operation while waiting.
43 //
44 //     To implement this functionality we used C# iterators manually;
45 //     we drive the pipeline by executing the various hooks from the
46 //     `RunHooks' routine which is an enumerator that will yield the
47 //     value `false' if execution must proceed or `true' if execution
48 //     must be stopped.
49 //
50 //     By yielding values we can suspend execution of RunHooks.
51 //
52 //     Special attention must be given to `in_begin' and `must_yield'
53 //     variables.  These are used in the case that an async hook
54 //     completes synchronously as its important to not yield in that
55 //     case or we would hang.
56 //    
57 //     Many of Mono modules used to be declared async, but they would
58 //     actually be completely synchronous, this might resurface in the
59 //     future with other modules.
60 //
61 // TODO:
62 //    Events Disposed
63 //
64
65 using System.IO;
66 using System.Collections;
67 using System.ComponentModel;
68 using System.Configuration;
69 using System.Globalization;
70 using System.Security.Permissions;
71 using System.Security.Principal;
72 using System.Threading;
73 using System.Web.Configuration;
74 using System.Web.SessionState;
75 using System.Web.UI;
76
77 #if TARGET_J2EE
78 using vmw.@internal.j2ee;
79 #endif
80         
81 namespace System.Web {
82
83         // CAS
84         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
85         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
86         // attributes
87         [ToolboxItem(false)]
88         public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable {
89                 HttpContext context;
90                 HttpSessionState session;
91                 ISite isite;
92
93                 // The source, and the exposed API (cache).
94                 HttpModuleCollection modcoll;
95
96                 string assemblyLocation;
97
98                 //
99                 // The factory for the handler currently running.
100                 //
101                 IHttpHandlerFactory factory;
102
103                 //
104                 // Whether the thread culture is to be auto-set.
105                 // Used only in the 2.0 profile, always false for 1.x
106                 //
107                 bool autoCulture;
108                 bool autoUICulture;
109                 
110                 //
111                 // Whether the pipeline should be stopped
112                 //
113                 bool stop_processing;
114
115                 //
116                 // The Pipeline
117                 //
118                 IEnumerator pipeline;
119
120                 // To flag when we are done processing a request from BeginProcessRequest.
121                 ManualResetEvent done;
122
123                 // The current IAsyncResult for the running async request handler in the pipeline
124                 AsyncRequestState begin_iar;
125
126                 // Tracks the current AsyncInvocation being dispatched
127                 AsyncInvoker current_ai;
128
129                 // We don't use the EventHandlerList here, but derived classes might do
130                 EventHandlerList events;
131
132                 // Culture and IPrincipal
133                 CultureInfo app_culture;
134                 CultureInfo appui_culture;
135                 CultureInfo prev_app_culture;
136                 CultureInfo prev_appui_culture;
137                 IPrincipal prev_user;
138 #if NET_2_0
139 #if TARGET_J2EE
140                 const string initialization_exception_key = "System.Web.HttpApplication.initialization_exception";
141                 static Exception initialization_exception {
142                         get { return (Exception) AppDomain.CurrentDomain.GetData (initialization_exception_key); }
143                         set { AppDomain.CurrentDomain.SetData (initialization_exception_key, value); }
144                 }
145 #else
146                 static Exception initialization_exception;
147 #endif
148                 bool removeConfigurationFromCache;
149 #endif
150
151                 //
152                 // These are used to detect the case where the EndXXX method is invoked
153                 // from within the BeginXXXX delegate, so we detect whether we kick the
154                 // pipeline from here, or from the the RunHook routine
155                 //
156                 bool must_yield;
157                 bool in_begin;
158
159                 public HttpApplication ()
160                 {
161                         done = new ManualResetEvent (false);
162                 }
163                 
164                 internal void InitOnce (bool full_init)
165                 {
166                         lock (this) {
167                                 if (modcoll != null)
168                                         return;
169
170 #if NET_2_0
171                                 HttpModulesSection modules;
172                                 modules = (HttpModulesSection) WebConfigurationManager.GetSection ("system.web/httpModules", HttpRuntime.AppDomainAppVirtualPath);
173 #else
174                                 ModulesConfiguration modules;
175
176                                 modules = (ModulesConfiguration) HttpContext.GetAppConfig ("system.web/httpModules");
177 #endif
178
179                                 modcoll = modules.LoadModules (this);
180
181                                 if (full_init)
182                                         HttpApplicationFactory.AttachEvents (this);
183
184 #if NET_2_0
185                                 GlobalizationSection cfg;
186                                 cfg = (GlobalizationSection) WebConfigurationManager.GetSection ("system.web/globalization");
187                                 app_culture = cfg.GetCulture();
188                                 autoCulture = cfg.IsAutoCulture;
189                                 appui_culture = cfg.GetUICulture();
190                                 autoUICulture = cfg.IsAutoUICulture;
191 #else
192                                 GlobalizationConfiguration cfg;
193                                 cfg = GlobalizationConfiguration.GetInstance (null);
194                                 if (cfg != null) {
195                                         app_culture = cfg.Culture;
196                                         appui_culture = cfg.UICulture;
197                                 }
198 #endif
199                         }
200                 }
201
202                 internal string AssemblyLocation {
203                         get {
204                                 if (assemblyLocation == null)
205                                         assemblyLocation = GetType ().Assembly.Location;
206                                 return assemblyLocation;
207                         }
208                 }
209
210 #if NET_2_0
211                 internal static Exception InitializationException {
212                         get { return initialization_exception; }
213                 }
214 #endif
215
216                 [Browsable (false)]
217                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
218                 public HttpApplicationState Application {
219                         get {
220                                 return HttpApplicationFactory.ApplicationState;
221                         }
222                 }
223
224                 [Browsable (false)]
225                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
226                 public HttpContext Context {
227                         get {
228                                 return context;
229                         }
230                 }
231                                          
232                 protected EventHandlerList Events {
233                         get {
234                                 if (events == null)
235                                         events = new EventHandlerList ();
236
237                                 return events;
238                         }
239                 }
240
241                 [Browsable (false)]
242                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
243                 public HttpModuleCollection Modules {
244                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
245                         get {
246                                 if (modcoll == null)
247                                         modcoll = new HttpModuleCollection ();
248                                 
249                                 return modcoll;
250                         }
251                 }
252
253                 [Browsable (false)]
254                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
255                 public HttpRequest Request {
256                         get {
257                                 if (context == null)
258                                         throw new HttpException (Locale.GetText ("No context is available."));
259
260                                 if (false == HttpApplicationFactory.ContextAvailable)
261                                         throw new HttpException (Locale.GetText ("Request is not available in this context."));
262
263                                 return context.Request;
264                         }
265                 }
266
267                 [Browsable (false)]
268                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
269                 public HttpResponse Response {
270                         get {
271                                 if (context == null)
272                                         throw new HttpException (Locale.GetText ("No context is available."));
273
274                                 if (false == HttpApplicationFactory.ContextAvailable)
275                                         throw new HttpException (Locale.GetText ("Response is not available in this context."));
276
277                                 return context.Response;
278                         }
279                 }
280
281                 [Browsable (false)]
282                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
283                 public HttpServerUtility Server {
284                         get {
285                                 if (context != null)
286                                         return context.Server;
287
288                                 //
289                                 // This is so we can get the Server and call a few methods
290                                 // which are not context sensitive, see HttpServerUtilityTest
291                                 //
292                                 return new HttpServerUtility ((HttpContext) null);
293                         }
294                 }
295
296                 [Browsable (false)]
297                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
298                 public HttpSessionState Session {
299                         get {
300                                 // Only used for Session_End
301                                 if (session != null)
302                                         return session;
303
304                                 if (context == null)
305                                         throw new HttpException (Locale.GetText ("No context is available."));
306                                 return context.Session;
307                         }
308                 }
309
310                 [Browsable (false)]
311                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
312 #if NET_2_0
313                 public ISite Site {
314 #else
315                 public virtual ISite Site {
316 #endif
317                         get {
318                                 return isite;
319                         }
320
321                         set {
322                                 isite = value;
323                         }
324                 }
325
326                 [Browsable (false)]
327                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
328                 public IPrincipal User {
329                         get {
330                                 if (context == null)
331                                         throw new HttpException (Locale.GetText ("No context is available."));
332                                 if (context.User == null)
333                                         throw new HttpException (Locale.GetText ("No currently authenticated user."));
334                                 
335                                 return context.User;
336                         }
337                 }
338                 
339                 public virtual event EventHandler Disposed;
340                 public virtual event EventHandler Error;
341
342                 public event EventHandler PreSendRequestHeaders;
343                 internal void TriggerPreSendRequestHeaders ()
344                 {
345                         if (PreSendRequestHeaders != null)
346                                 PreSendRequestHeaders (this, EventArgs.Empty);
347                 }
348
349                 public event EventHandler PreSendRequestContent;
350                 internal void TriggerPreSendRequestContent ()
351                 {
352                         if (PreSendRequestContent != null)
353                                 PreSendRequestContent (this, EventArgs.Empty);
354                 }
355                 
356                 public event EventHandler AcquireRequestState;
357                 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
358                 {
359                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
360                         AcquireRequestState += new EventHandler (invoker.Invoke);
361                 }
362
363                 public event EventHandler AuthenticateRequest;
364                 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
365                 {
366                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
367                         AuthenticateRequest += new EventHandler (invoker.Invoke);
368                 }
369
370                 public event EventHandler AuthorizeRequest;
371                 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
372                 {
373                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
374                         AuthorizeRequest += new EventHandler (invoker.Invoke);
375                 }
376
377                 public event EventHandler BeginRequest;
378                 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh)
379                 {
380                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
381                         BeginRequest += new EventHandler (invoker.Invoke);
382                 }
383
384                 public event EventHandler EndRequest;
385                 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh)
386                 {
387                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
388                         EndRequest += new EventHandler (invoker.Invoke);
389                 }
390                 
391                 public event EventHandler PostRequestHandlerExecute;
392                 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
393                 {
394                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
395                         PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
396                 }
397
398                 public event EventHandler PreRequestHandlerExecute;
399                 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
400                 {
401                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
402                         PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
403                 }
404
405                 public event EventHandler ReleaseRequestState;
406                 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
407                 {
408                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
409                         ReleaseRequestState += new EventHandler (invoker.Invoke);
410                 }
411
412                 public event EventHandler ResolveRequestCache;
413                 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
414                 {
415                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
416                         ResolveRequestCache += new EventHandler (invoker.Invoke);
417                 }
418
419                 public event EventHandler UpdateRequestCache;
420                 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
421                 {
422                         AsyncInvoker invoker = new AsyncInvoker (bh, eh);
423                         UpdateRequestCache += new EventHandler (invoker.Invoke);
424                 }
425
426 #if NET_2_0
427                 public event EventHandler PostAuthenticateRequest;
428                 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
429                 {
430                         AddOnPostAuthenticateRequestAsync (bh, eh, null);
431                 }
432                         
433                 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
434                 {
435                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
436                         PostAuthenticateRequest += new EventHandler (invoker.Invoke);
437                 }
438                 
439                 public event EventHandler PostAuthorizeRequest;
440                 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
441                 {
442                         AddOnPostAuthorizeRequestAsync (bh, eh, null);
443                 }
444                 
445                 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
446                 {
447                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
448                         PostAuthorizeRequest += new EventHandler (invoker.Invoke);
449                 }
450
451                 public event EventHandler PostResolveRequestCache;
452                 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
453                 {
454                         AddOnPostResolveRequestCacheAsync (bh, eh, null);
455                 }
456                 
457                 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
458                 {
459                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
460                         PostResolveRequestCache += new EventHandler (invoker.Invoke);
461                 }
462
463                 public event EventHandler PostMapRequestHandler;
464                 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh)
465                 {
466                         AddOnPostMapRequestHandlerAsync (bh, eh, null);
467                 }
468                 
469                 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh, object data)
470                 {
471                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
472                         PostMapRequestHandler += new EventHandler (invoker.Invoke);
473                 }
474                 
475                 public event EventHandler PostAcquireRequestState;
476                 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
477                 {
478                         AddOnPostAcquireRequestStateAsync (bh, eh, null);
479                 }
480                 
481                 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
482                 {
483                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
484                         PostAcquireRequestState += new EventHandler (invoker.Invoke);
485                 }
486                 
487                 public event EventHandler PostReleaseRequestState;
488                 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
489                 {
490                         AddOnPostReleaseRequestStateAsync (bh, eh, null);
491                 }
492                 
493                 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
494                 {
495                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
496                         PostReleaseRequestState += new EventHandler (invoker.Invoke);
497                 }
498
499                 public event EventHandler PostUpdateRequestCache;
500                 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
501                 {
502                         AddOnPostUpdateRequestCacheAsync (bh, eh, null);
503                 }
504                 
505                 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
506                 {
507                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
508                         PostUpdateRequestCache += new EventHandler (invoker.Invoke);
509                 }
510
511                 //
512                 // The new overloads that take a data parameter
513                 //
514                 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
515                 {
516                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
517                         AcquireRequestState += new EventHandler (invoker.Invoke);
518                 }
519
520                 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
521                 {
522                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
523                         AuthenticateRequest += new EventHandler (invoker.Invoke);
524                 }
525
526                 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
527                 {
528                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
529                         AuthorizeRequest += new EventHandler (invoker.Invoke);
530                 }
531
532                 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
533                 {
534                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
535                         BeginRequest += new EventHandler (invoker.Invoke);
536                 }
537
538                 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
539                 {
540                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
541                         EndRequest += new EventHandler (invoker.Invoke);
542                 }
543                 
544                 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
545                 {
546                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
547                         PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
548                 }
549
550                 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
551                 {
552                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
553                         PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
554                 }
555
556                 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
557                 {
558                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
559                         ReleaseRequestState += new EventHandler (invoker.Invoke);
560                 }
561
562                 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
563                 {
564                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
565                         ResolveRequestCache += new EventHandler (invoker.Invoke);
566                 }
567
568                 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
569                 {
570                         AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
571                         UpdateRequestCache += new EventHandler (invoker.Invoke);
572                 }
573 #endif
574                 
575                 internal event EventHandler DefaultAuthentication;
576                 
577                 //
578                 // Bypass all the event on the Http pipeline and go directly to EndRequest
579                 //
580                 public void CompleteRequest ()
581                 {
582                         stop_processing = true;
583                 }
584
585                 internal bool RequestCompleted {
586                         set { stop_processing = value; }
587                 }
588
589                 public virtual void Dispose ()
590                 {
591                         if (modcoll != null) {
592                                 for (int i = modcoll.Count - 1; i >= 0; i--) {
593                                         modcoll.Get (i).Dispose ();
594                                 }
595                                 modcoll = null;
596                         }
597
598                         if (Disposed != null)
599                                 Disposed (this, EventArgs.Empty);
600                         
601                         done.Close ();
602                         done = null;
603                 }
604
605                 public virtual string GetVaryByCustomString (HttpContext context, string custom)
606                 {
607                         if (custom == null) // Sigh
608                                 throw new NullReferenceException ();
609
610                         if (0 == String.Compare (custom, "browser", true, CultureInfo.InvariantCulture))
611                                 return context.Request.Browser.Type;
612
613                         return null;
614                 }
615
616                 //
617                 // If we catch an error, queue this error
618                 //
619                 void ProcessError (Exception e)
620                 {
621                         if (context == null)
622                                 context = HttpContext.Current;
623
624                         bool first = context.Error == null;
625                         context.AddError (e);
626                         if (first){
627                                 if (Error != null){
628                                         try {
629                                                 Error (this, EventArgs.Empty);
630                                         } catch (ThreadAbortException){
631                                                 // This happens on Redirect() or End()
632                                                 Thread.ResetAbort ();
633                                         } catch (Exception ee){
634                                                 context.AddError (ee);
635                                         }
636                                 }
637                         }
638                         stop_processing = true;
639 #if NET_2_0
640                         // we want to remove configuration from the cache in case of 
641                         // invalid resource not exists to prevent DOS attack.
642                         HttpException httpEx = e as HttpException;
643                         if (httpEx != null && httpEx.GetHttpCode () == 404) {
644                                 removeConfigurationFromCache = true;
645                         }
646 #endif
647                 }
648                 
649                 //
650                 // Ticks the clock: next step on the pipeline.
651                 //
652                 void Tick ()
653                 {
654                         try {
655                                 if (pipeline != null && pipeline.MoveNext ()){
656                                         if (pipeline == null || (bool)pipeline.Current)
657                                                 PipelineDone ();
658                                 }
659                         } catch (ThreadAbortException taex) {
660                                 object obj = taex.ExceptionState;
661                                 Thread.ResetAbort ();
662                                 stop_processing = true;
663                                 if (obj is StepTimeout)
664                                         ProcessError (new HttpException ("The request timed out."));
665                                 else
666                                         PipelineDone ();
667                         } catch (Exception e) {
668                                 Console.WriteLine ("Tick caught an exception that has not been propagated:\n" + e);
669                         }
670                 }
671
672                 void Resume ()
673                 {
674                         if (in_begin)
675                                 must_yield = false;
676                         else
677                                 Tick ();
678                 }
679                 
680                 //
681                 // Invoked when our async callback called from RunHooks completes,
682                 // we restart the pipeline here.
683                 //
684                 void async_callback_completed_cb (IAsyncResult ar)
685                 {
686                         if (current_ai.end != null){
687                                 try {
688                                         current_ai.end (ar);
689                                 } catch (Exception e) {
690                                         ProcessError (e);
691                                 }
692                         }
693
694                         Resume ();
695                 }
696
697                 void async_handler_complete_cb (IAsyncResult ar)
698                 {
699                         IHttpAsyncHandler async_handler = ((IHttpAsyncHandler) ar.AsyncState);
700
701                         try {
702                                 async_handler.EndProcessRequest (ar);
703                         } catch (Exception e){
704                                 ProcessError (e);
705                         }
706                         
707                         Resume ();
708                 }
709                 
710                 //
711                 // This enumerator yields whether processing must be stopped:
712                 //    true:  processing of the pipeline must be stopped
713                 //    false: processing of the pipeline must not be stopped
714                 //
715                 IEnumerable RunHooks (Delegate list)
716                 {
717                         Delegate [] delegates = list.GetInvocationList ();
718
719                         foreach (EventHandler d in delegates){
720                                 if (d.Target != null && (d.Target is AsyncInvoker)){
721                                         current_ai = (AsyncInvoker) d.Target;
722
723                                         try {
724                                                 must_yield = true;
725                                                 in_begin = true;
726                                                 context.BeginTimeoutPossible ();
727                                                 current_ai.begin (this, EventArgs.Empty, async_callback_completed_cb, current_ai.data);
728                                         } catch (ThreadAbortException taex){
729                                                 object obj = taex.ExceptionState;
730                                                 Thread.ResetAbort ();
731                                                 stop_processing = true;
732                                                 if (obj is StepTimeout)
733                                                         ProcessError (new HttpException ("The request timed out."));
734                                         } catch (Exception e){
735                                                 ProcessError (e);
736                                         } finally {
737                                                 in_begin = false;
738                                                 context.EndTimeoutPossible ();
739                                         }
740
741                                         //
742                                         // If things are still moving forward, yield this
743                                         // thread now
744                                         //
745                                         if (must_yield)
746                                                 yield return stop_processing;
747                                         else if (stop_processing)
748                                                 yield return true;
749                                 } else {
750                                         try {
751                                                 context.BeginTimeoutPossible ();
752                                                 d (this, EventArgs.Empty);
753                                         } catch (ThreadAbortException taex){
754                                                 object obj = taex.ExceptionState;
755                                                 Thread.ResetAbort ();
756                                                 stop_processing = true;
757                                                 if (obj is StepTimeout)
758                                                         ProcessError (new HttpException ("The request timed out."));
759                                         } catch (Exception e){
760                                                 ProcessError (e);
761                                         } finally {
762                                                 context.EndTimeoutPossible ();
763                                         }
764                                         if (stop_processing)
765                                                 yield return true;
766                                 }
767                         }
768                 }
769
770                 static void FinalErrorWrite (HttpResponse response, string error)
771                 {
772                         try {
773                                 response.Write (error);
774                                 response.Flush (true);
775                         } catch {
776                                 response.Close ();
777                         }
778                 }
779
780                 void OutputPage ()
781                 {
782                         if (context.Error == null){
783                                 try {
784                                         context.Response.Flush (true);
785                                 } catch (Exception e){
786                                         context.AddError (e);
787                                 }
788                         }
789
790                         Exception error = context.Error;
791                         if (error != null){
792                                 HttpResponse response = context.Response;
793
794 #if TARGET_J2EE
795                                 IPortletActionRequest actionRequest = context.ServletRequest as IPortletActionRequest;
796                                 IPortletActionResponse actionResponse = context.ServletResponse as IPortletActionResponse;
797                                 if (actionRequest != null && actionResponse != null && actionRequest.processActionOnly ()) {
798                                         string exception = "Exception of type " + context.Error.GetType () + 
799                                                 " during processAction: " + context.Error.Message + Console.Out.NewLine + 
800                                                 context.Error.StackTrace;
801                                         actionResponse.setRenderParameter ("vmw.action.exception", exception);
802                                         return;
803                                 }
804 #endif
805                                 if (!response.HeadersSent){
806                                         response.ClearHeaders ();
807                                         response.ClearContent ();
808
809                                         if (error is HttpException){
810                                                 response.StatusCode = ((HttpException)error).GetHttpCode ();
811                                         } else {
812                                                 error = new HttpException ("", error);
813                                                 response.StatusCode = 500;
814                                         }
815                                         HttpException httpEx = (HttpException) error;
816                                         if (!RedirectCustomError (ref httpEx))
817                                                 FinalErrorWrite (response, httpEx.GetHtmlErrorMessage ());
818                                         else
819                                                 response.Flush (true);
820                                 } else {
821                                         if (!(error is HttpException))
822                                                 error = new HttpException ("", error);
823                                         FinalErrorWrite (response, ((HttpException) error).GetHtmlErrorMessage ());
824                                 }
825                         }
826                         
827                 }
828                 
829                 //
830                 // Invoked at the end of the pipeline execution
831                 //
832                 void PipelineDone ()
833                 {
834                         try {
835                                 if (EndRequest != null)
836                                         EndRequest (this, EventArgs.Empty);
837                         } catch (Exception e){
838                                 ProcessError (e);
839                         }
840
841                         try {
842                                 OutputPage ();
843                         } catch (Exception e) {
844                                 Console.WriteLine ("Internal error: OutputPage threw an exception " + e);
845                         } finally {
846                                 if (context == null)
847                                         context = HttpContext.Current;
848                                 context.WorkerRequest.EndOfRequest();
849                                 if (factory != null && context.Handler != null){
850                                         factory.ReleaseHandler (context.Handler);
851                                         context.Handler = null;
852                                         factory = null;
853                                 }
854 #if NET_2_0
855                                 context.PopHandler ();
856 #endif
857                                 if (begin_iar != null){
858                                         try {
859                                                 begin_iar.Complete ();
860                                         } catch {
861                                                 //
862                                                 // TODO: if this throws an error, we have no way of reporting it
863                                                 // Not really too bad, since the only failure might be
864                                                 // `HttpRuntime.request_processed'
865                                                 //
866                                         } finally {
867                                                 done.Set ();
868                                         }
869                                 } else {
870                                         done.Set ();
871                                 }
872                                 
873                                 // context = null; -> moved to PostDone
874                                 pipeline = null;
875                                 current_ai = null;
876                         }
877                         PostDone ();
878                 }
879
880                 //
881                 // Events fired as described in `Http Runtime Support, HttpModules,
882                 // Handling Public Events'
883                 //
884                 IEnumerator Pipeline ()
885                 {
886                         if (stop_processing)
887                                 yield return true;
888
889                         if (BeginRequest != null)
890                                 foreach (bool stop in RunHooks (BeginRequest))
891                                         yield return stop;
892
893                         if (AuthenticateRequest != null)
894                                 foreach (bool stop in RunHooks (AuthenticateRequest))
895                                         yield return stop;
896
897                         if (DefaultAuthentication != null)
898                                 foreach (bool stop in RunHooks (DefaultAuthentication))
899                                         yield return stop;
900
901 #if NET_2_0
902                         if (PostAuthenticateRequest != null)
903                                 foreach (bool stop in RunHooks (PostAuthenticateRequest))
904                                         yield return stop;
905 #endif
906                         if (AuthorizeRequest != null)
907                                 foreach (bool stop in RunHooks (AuthorizeRequest))
908                                         yield return stop;
909 #if NET_2_0
910                         if (PostAuthorizeRequest != null)
911                                 foreach (bool stop in RunHooks (PostAuthorizeRequest))
912                                         yield return stop;
913 #endif
914
915                         if (ResolveRequestCache != null)
916                                 foreach (bool stop in RunHooks (ResolveRequestCache))
917                                         yield return stop;
918
919 #if NET_2_0
920                         if (PostResolveRequestCache != null)
921                                 foreach (bool stop in RunHooks (PostResolveRequestCache))
922                                         yield return stop;
923 #endif
924
925                         // Obtain the handler for the request.
926                         IHttpHandler handler = null;
927                         try {
928                                 handler = GetHandler (context);
929                                 context.Handler = handler;
930 #if NET_2_0
931                                 context.PushHandler (handler);
932 #endif
933                         } catch (FileNotFoundException fnf){
934                                 if (context.Request.IsLocal)
935                                         ProcessError (new HttpException (404, String.Format ("File not found {0}", fnf.FileName), fnf));
936                                 else
937                                         ProcessError (new HttpException (404, "File not found: " + Path.GetFileName (fnf.FileName)));
938                         } catch (DirectoryNotFoundException dnf){
939                                 if (!context.Request.IsLocal)
940                                         dnf = null; // Do not "leak" real path information
941                                 ProcessError (new HttpException (404, "Directory not found", dnf));
942                         } catch (Exception e) {
943                                 ProcessError (e);
944                         }
945
946                         if (stop_processing)
947                                 yield return true;
948
949 #if NET_2_0
950                         if (PostMapRequestHandler != null)
951                                 foreach (bool stop in RunHooks (PostMapRequestHandler))
952                                         yield return stop;
953                         
954 #endif
955                         if (AcquireRequestState != null){
956                                 foreach (bool stop in RunHooks (AcquireRequestState))
957                                         yield return stop;
958                         }
959
960 #if NET_2_0
961                         if (PostAcquireRequestState != null){
962                                 foreach (bool stop in RunHooks (PostAcquireRequestState))
963                                         yield return stop;
964                         }
965 #endif
966                         
967                         //
968                         // From this point on, we need to ensure that we call
969                         // ReleaseRequestState, so the code below jumps to
970                         // `release:' to guarantee it rather than yielding.
971                         //
972                         if (PreRequestHandlerExecute != null)
973                                 foreach (bool stop in RunHooks (PreRequestHandlerExecute))
974                                         if (stop)
975                                                 goto release;
976                                 
977                         try {
978                                 context.BeginTimeoutPossible ();
979                                 if (handler != null){
980                                         IHttpAsyncHandler async_handler = handler as IHttpAsyncHandler;
981                                         
982                                         if (async_handler != null){
983                                                 must_yield = true;
984                                                 in_begin = true;
985                                                 async_handler.BeginProcessRequest (context, async_handler_complete_cb, handler);
986                                         } else {
987                                                 must_yield = false;
988                                                 handler.ProcessRequest (context);
989                                         }
990                                 }
991                         } catch (ThreadAbortException taex){
992                                 object obj = taex.ExceptionState;
993                                 Thread.ResetAbort ();
994                                 stop_processing = true;
995                                 if (obj is StepTimeout)
996                                         ProcessError (new HttpException ("The request timed out."));
997                         } catch (Exception e){
998                                 ProcessError (e);
999                         } finally {
1000                                 in_begin = false;
1001                                 context.EndTimeoutPossible ();
1002                         }
1003                         if (must_yield)
1004                                 yield return stop_processing;
1005                         else if (stop_processing)
1006                                 goto release;
1007                         
1008                         // These are executed after the application has returned
1009                         
1010                         if (PostRequestHandlerExecute != null)
1011                                 foreach (bool stop in RunHooks (PostRequestHandlerExecute))
1012                                         if (stop)
1013                                                 goto release;
1014                         
1015                 release:
1016                         if (ReleaseRequestState != null){
1017 #pragma warning disable 168
1018                                 foreach (bool stop in RunHooks (ReleaseRequestState)){
1019                                         //
1020                                         // Ignore the stop signal while release the state
1021                                         //
1022                                         
1023                                 }
1024 #pragma warning restore 168
1025                         }
1026                         
1027                         if (stop_processing)
1028                                 yield return true;
1029
1030 #if NET_2_0
1031                         if (PostReleaseRequestState != null)
1032                                 foreach (bool stop in RunHooks (PostReleaseRequestState))
1033                                         yield return stop;
1034 #endif
1035
1036                         if (context.Error == null)
1037                                 context.Response.DoFilter (true);
1038
1039                         if (UpdateRequestCache != null)
1040                                 foreach (bool stop in RunHooks (UpdateRequestCache))
1041                                         yield return stop;
1042
1043 #if NET_2_0
1044                         if (PostUpdateRequestCache != null)
1045                                 foreach (bool stop in RunHooks (PostUpdateRequestCache))
1046                                         yield return stop;
1047 #endif
1048                         PipelineDone ();
1049                 }
1050
1051
1052                 internal CultureInfo GetThreadCulture (HttpRequest request, CultureInfo culture, bool isAuto)
1053                 {
1054 #if NET_2_0
1055                         if (!isAuto)
1056                                 return culture;
1057                         CultureInfo ret = null;
1058                         string[] languages = request.UserLanguages;
1059                         try {
1060                                 if (languages != null && languages.Length > 0)
1061                                         ret = CultureInfo.CreateSpecificCulture (languages[0]);
1062                         } catch {
1063                         }
1064                         
1065                         if (ret == null)
1066                                 ret = culture;
1067                         
1068                         return ret;
1069 #else
1070                         return culture;
1071 #endif
1072                 }
1073
1074
1075                 void PreStart ()
1076                 {
1077 #if !TARGET_J2EE
1078                         HttpRuntime.TimeoutManager.Add (context);
1079 #endif
1080                         Thread th = Thread.CurrentThread;
1081                         if (app_culture != null) {
1082                                 prev_app_culture = th.CurrentCulture;
1083                                 CultureInfo new_app_culture = GetThreadCulture (Request, app_culture, autoCulture);
1084                                 if (!new_app_culture.Equals (CultureInfo.InvariantCulture))
1085                                         th.CurrentCulture = new_app_culture;
1086                         }
1087
1088                         if (appui_culture != null) {
1089                                 prev_appui_culture = th.CurrentUICulture;
1090                                 CultureInfo new_app_culture = GetThreadCulture (Request, appui_culture, autoUICulture);
1091                                 if (!new_app_culture.Equals (CultureInfo.InvariantCulture))
1092                                         th.CurrentUICulture = new_app_culture;
1093                         }
1094
1095 #if !TARGET_JVM
1096                         prev_user = Thread.CurrentPrincipal;
1097 #endif
1098                 }
1099
1100                 void PostDone ()
1101                 {
1102 #if NET_2_0
1103                         if (removeConfigurationFromCache) {
1104                                 WebConfigurationManager.RemoveConfigurationFromCache (context);
1105                                 removeConfigurationFromCache = false;
1106                         }
1107 #endif
1108                         Thread th = Thread.CurrentThread;
1109 #if !TARGET_JVM
1110                         if (Thread.CurrentPrincipal != prev_user)
1111                                 Thread.CurrentPrincipal = prev_user;
1112 #endif
1113                         if (prev_appui_culture != null && prev_appui_culture != th.CurrentUICulture)
1114                                 th.CurrentUICulture = prev_appui_culture;
1115                         if (prev_app_culture != null && prev_app_culture != th.CurrentCulture)
1116                                 th.CurrentCulture = prev_app_culture;
1117
1118 #if !TARGET_J2EE
1119                         if (context == null)
1120                                 context = HttpContext.Current;
1121                         HttpRuntime.TimeoutManager.Remove (context);
1122 #endif
1123                         context = null;
1124                         session = null;
1125                         HttpContext.Current = null;
1126                 }
1127
1128                 void Start (object x)
1129                 {
1130                         try {
1131                                 InitOnce (true);
1132                         } catch (Exception e) {
1133 #if NET_2_0
1134                                 initialization_exception = e;
1135 #endif
1136                                 FinalErrorWrite (context.Response, new HttpException ("", e).GetHtmlErrorMessage ());
1137                                 PipelineDone ();
1138                                 return;
1139                         }
1140
1141                         HttpContext.Current = Context;
1142                         PreStart ();
1143                         pipeline = Pipeline ();
1144                         Tick ();
1145                 }
1146         
1147                 // Used by HttpServerUtility.Execute
1148                 internal IHttpHandler GetHandler (HttpContext context)
1149                 {
1150                         HttpRequest request = context.Request;
1151                         string verb = request.RequestType;
1152                         string url = request.FilePath;
1153                         
1154                         IHttpHandler handler = null;
1155 #if NET_2_0
1156                         HttpHandlersSection     httpHandlersSection = (HttpHandlersSection) WebConfigurationManager.GetSection ("system.web/httpHandlers");
1157                         object o = httpHandlersSection.LocateHandler (verb, url);
1158 #else
1159                         HandlerFactoryConfiguration factory_config = (HandlerFactoryConfiguration) HttpContext.GetAppConfig ("system.web/httpHandlers");
1160                         object o = factory_config.LocateHandler (verb, url);
1161 #endif
1162
1163                         factory = o as IHttpHandlerFactory;
1164                         
1165                         if (factory == null) {
1166                                 handler = (IHttpHandler) o;
1167                         } else {
1168                                 handler = factory.GetHandler (context, verb, url, request.PhysicalPath);
1169                         }
1170
1171                         return handler;
1172                 }
1173                 
1174                 void IHttpHandler.ProcessRequest (HttpContext context)
1175                 {
1176                         begin_iar = null;
1177                         this.context = context;
1178                         done.Reset ();
1179
1180                         Start (null);
1181                         done.WaitOne ();
1182                 }
1183
1184                 //
1185                 // This is used by FireOnAppStart, when we init the application
1186                 // as the context is required to be set at that point (the user
1187                 // might call methods that require it on that hook).
1188                 //
1189                 internal void SetContext (HttpContext context)
1190                 {
1191                         this.context = context;
1192                 }
1193
1194                 internal void SetSession (HttpSessionState session)
1195                 {
1196                         this.session = session;
1197                 }
1198
1199                 IAsyncResult IHttpAsyncHandler.BeginProcessRequest (HttpContext context, AsyncCallback cb, object extraData)
1200                 {
1201                         this.context = context;
1202                         done.Reset ();
1203                         
1204                         begin_iar = new AsyncRequestState (done, cb, extraData);
1205
1206 #if TARGET_J2EE
1207                         IPortletRenderRequest renderRequest = context.ServletRequest as IPortletRenderRequest;
1208                         if (renderRequest != null) {
1209                                 string actionException = context.ServletRequest.getParameter ("vmw.action.exception");
1210                                 if (actionException != null && actionException.Length > 0) {
1211                                         FinalErrorWrite (context.Response, actionException.Replace("\n", "<br>"));
1212                                         begin_iar.Complete ();
1213                                         return begin_iar;
1214                                 }
1215                         }
1216 #endif
1217
1218 #if TARGET_JVM
1219                         if (true)
1220 #else
1221                         if (Thread.CurrentThread.IsThreadPoolThread)
1222 #endif
1223                                 Start (null);
1224                         else
1225                                 ThreadPool.QueueUserWorkItem (new WaitCallback (Start), null);
1226                         
1227                         return begin_iar;
1228                 }
1229
1230                 void IHttpAsyncHandler.EndProcessRequest (IAsyncResult result)
1231                 {
1232                         if (!result.IsCompleted)
1233                                 result.AsyncWaitHandle.WaitOne ();
1234                         begin_iar = null;
1235                 }
1236
1237                 public virtual void Init ()
1238                 {
1239                 }
1240
1241                 bool IHttpHandler.IsReusable {
1242                         get {
1243                                 return true;
1244                         }
1245                 }
1246                 
1247 #region internals
1248                 internal void ClearError ()
1249                 {
1250                         context.ClearError ();
1251                 }
1252
1253                 bool RedirectErrorPage (string error_page)
1254                 {
1255                         if (context.Request.QueryString ["aspxerrorpath"] != null)
1256                                 return false;
1257
1258                         Response.Redirect (error_page + "?aspxerrorpath=" + Request.Path, false);
1259                         return true;
1260                 }
1261                                                         
1262                 bool RedirectCustomError (ref HttpException httpEx)
1263                 {
1264                         try {
1265                         if (!context.IsCustomErrorEnabledUnsafe)
1266                                 return false;
1267                         
1268 #if NET_2_0
1269                         CustomErrorsSection config = (CustomErrorsSection)WebConfigurationManager.GetSection ("system.web/customErrors");
1270 #else
1271                         CustomErrorsConfig config = null;
1272                         try {
1273                                 config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
1274                         } catch { }
1275 #endif
1276                         
1277                         if (config == null) {
1278                                 if (context.ErrorPage != null)
1279                                         return RedirectErrorPage (context.ErrorPage);
1280                                 
1281                                 return false;
1282                         }
1283                         
1284 #if NET_2_0
1285                         CustomError err = config.Errors [context.Response.StatusCode.ToString()];
1286                         string redirect = err == null ? null : err.Redirect;
1287 #else
1288                         string redirect =  config [context.Response.StatusCode];
1289 #endif
1290                         if (redirect == null) {
1291                                 redirect = context.ErrorPage;
1292                                 if (redirect == null)
1293                                         redirect = config.DefaultRedirect;
1294                         }
1295                         
1296                         if (redirect == null)
1297                                 return false;
1298                         
1299                         return RedirectErrorPage (redirect);
1300                         }
1301                         catch (Exception ex) {
1302                                 httpEx = new HttpException (500, "", ex);
1303                                 return false;
1304                         }
1305                 }
1306 #endregion
1307         }
1308
1309         //
1310         // Based on Fritz' Onion's AsyncRequestState class for asynchronous IHttpAsyncHandlers
1311         // 
1312         class AsyncRequestState : IAsyncResult {
1313                 AsyncCallback cb;
1314                 object cb_data;
1315                 bool completed;
1316                 ManualResetEvent complete_event = null;
1317                 
1318                 internal AsyncRequestState (ManualResetEvent complete_event, AsyncCallback cb, object cb_data)
1319                 {
1320                         this.cb = cb;
1321                         this.cb_data = cb_data;
1322                         this.complete_event = complete_event;
1323                 }
1324
1325                 internal void Complete ()
1326                 {
1327                         completed = true;
1328                         if (cb != null)
1329                                 cb (this);
1330                         
1331                         complete_event.Set ();
1332                 }
1333
1334                 public object AsyncState {
1335                         get {
1336                                 return cb_data;
1337                         }
1338                 }
1339
1340                 public bool CompletedSynchronously {
1341                         get {
1342                                 return false;
1343                         }
1344                 }
1345
1346                 public bool IsCompleted {
1347                         get {
1348                                 return completed;
1349                         }
1350                 }
1351
1352                 public WaitHandle AsyncWaitHandle {
1353                         get {
1354                                 return complete_event;
1355                         }
1356                 }
1357         }
1358
1359 #region Helper classes
1360         
1361         //
1362         // A wrapper to keep track of begin/end pairs
1363         //
1364         class AsyncInvoker {
1365                 public BeginEventHandler begin;
1366                 public EndEventHandler end;
1367                 public object data;
1368                 
1369                 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, object d)
1370                 {
1371                         begin = bh;
1372                         end = eh;
1373                         data = d;
1374                 }
1375
1376                 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh)
1377                 {
1378                         begin = bh;
1379                         end = eh;
1380                 }
1381                 
1382                 public void Invoke (object sender, EventArgs e)
1383                 {
1384                         throw new Exception ("This is just a dummy");
1385                 }
1386         }
1387 #endregion
1388 }
1389