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