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