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