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