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