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