2 // System.Web.HttpApplication.cs
5 // Miguel de Icaza (miguel@novell.com)
6 // Gonzalo Paniagua (gonzalo@ximian.com)
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
30 // The Application Processing Pipeline.
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.
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.
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
50 // By yielding values we can suspend execution of RunHooks.
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.
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.
66 using System.Collections;
67 using System.ComponentModel;
68 using System.Globalization;
69 using System.Security.Permissions;
70 using System.Security.Principal;
71 using System.Threading;
72 using System.Web.Configuration;
73 using System.Web.SessionState;
76 namespace System.Web {
79 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
80 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
83 public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable {
85 HttpSessionState session;
88 // The source, and the exposed API (cache).
89 HttpModuleCollection modcoll;
91 string assemblyLocation;
94 // The factory for the handler currently running.
96 IHttpHandlerFactory factory;
99 // Whether the pipeline should be stopped
101 bool stop_processing;
106 IEnumerator pipeline;
108 // To flag when we are done processing a request from BeginProcessRequest.
109 ManualResetEvent done;
111 // The current IAsyncResult for the running async request handler in the pipeline
112 AsyncRequestState begin_iar;
114 // Tracks the current AsyncInvocation being dispatched
115 AsyncInvoker current_ai;
117 // We don't use the EventHandlerList here, but derived classes might do
118 EventHandlerList events;
120 // Culture and IPrincipal
121 CultureInfo app_culture;
122 CultureInfo appui_culture;
123 CultureInfo prev_app_culture;
124 CultureInfo prev_appui_culture;
125 IPrincipal prev_user;
128 // These are used to detect the case where the EndXXX method is invoked
129 // from within the BeginXXXX delegate, so we detect whether we kick the
130 // pipeline from here, or from the the RunHook routine
135 public HttpApplication ()
137 done = new ManualResetEvent (false);
140 internal void InitOnce (bool full_init)
146 #if CONFIGURATION_2_0
147 HttpModulesSection modules;
148 modules = (HttpModulesSection) WebConfigurationManager.GetWebApplicationSection ("system.web/httpModules");
150 ModulesConfiguration modules;
152 modules = (ModulesConfiguration) HttpContext.GetAppConfig ("system.web/httpModules");
155 modcoll = modules.LoadModules (this);
158 HttpApplicationFactory.AttachEvents (this);
160 #if CONFIGURATION_2_0
161 GlobalizationSection cfg;
162 cfg = (GlobalizationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/globalization");
163 app_culture = cfg.GetCulture();
164 appui_culture = cfg.GetUICulture();
166 GlobalizationConfiguration cfg;
167 cfg = GlobalizationConfiguration.GetInstance (null);
169 app_culture = cfg.Culture;
170 appui_culture = cfg.UICulture;
176 internal string AssemblyLocation {
178 if (assemblyLocation == null)
179 assemblyLocation = GetType ().Assembly.Location;
180 return assemblyLocation;
185 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
186 public HttpApplicationState Application {
188 return HttpApplicationFactory.ApplicationState;
193 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
194 public HttpContext Context {
200 protected EventHandlerList Events {
203 events = new EventHandlerList ();
210 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
211 public HttpModuleCollection Modules {
212 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
215 modcoll = new HttpModuleCollection ();
222 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
223 public HttpRequest Request {
226 throw new HttpException (Locale.GetText ("No context is available."));
228 if (false == HttpApplicationFactory.ContextAvailable)
229 throw new HttpException (Locale.GetText ("Request is not available in this context."));
231 return context.Request;
236 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
237 public HttpResponse Response {
240 throw new HttpException (Locale.GetText ("No context is available."));
242 if (false == HttpApplicationFactory.ContextAvailable)
243 throw new HttpException (Locale.GetText ("Response is not available in this context."));
245 return context.Response;
250 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
251 public HttpServerUtility Server {
254 return context.Server;
257 // This is so we can get the Server and call a few methods
258 // which are not context sensitive, see HttpServerUtilityTest
260 return new HttpServerUtility ((HttpContext) null);
265 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
266 public HttpSessionState Session {
268 // Only used for Session_End
273 throw new HttpException (Locale.GetText ("No context is available."));
274 return context.Session;
279 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
280 public virtual ISite Site {
291 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
292 public IPrincipal User {
295 throw new HttpException (Locale.GetText ("No context is available."));
296 if (context.User == null)
297 throw new HttpException (Locale.GetText ("No currently authenticated user."));
303 public virtual event EventHandler Disposed;
304 public virtual event EventHandler Error;
306 public event EventHandler PreSendRequestHeaders;
307 internal void TriggerPreSendRequestHeaders ()
309 if (PreSendRequestHeaders != null)
310 PreSendRequestHeaders (this, EventArgs.Empty);
313 public event EventHandler PreSendRequestContent;
314 internal void TriggerPreSendRequestContent ()
316 if (PreSendRequestContent != null)
317 PreSendRequestContent (this, EventArgs.Empty);
320 public event EventHandler AcquireRequestState;
321 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
323 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
324 AcquireRequestState += new EventHandler (invoker.Invoke);
327 public event EventHandler AuthenticateRequest;
328 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
330 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
331 AuthenticateRequest += new EventHandler (invoker.Invoke);
334 public event EventHandler AuthorizeRequest;
335 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
337 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
338 AuthorizeRequest += new EventHandler (invoker.Invoke);
341 public event EventHandler BeginRequest;
342 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh)
344 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
345 BeginRequest += new EventHandler (invoker.Invoke);
348 public event EventHandler EndRequest;
349 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh)
351 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
352 EndRequest += new EventHandler (invoker.Invoke);
355 public event EventHandler PostRequestHandlerExecute;
356 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
358 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
359 PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
362 public event EventHandler PreRequestHandlerExecute;
363 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
365 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
366 PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
369 public event EventHandler ReleaseRequestState;
370 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
372 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
373 ReleaseRequestState += new EventHandler (invoker.Invoke);
376 public event EventHandler ResolveRequestCache;
377 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
379 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
380 ResolveRequestCache += new EventHandler (invoker.Invoke);
383 public event EventHandler UpdateRequestCache;
384 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
386 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
387 UpdateRequestCache += new EventHandler (invoker.Invoke);
391 public event EventHandler PostAuthenticateRequest;
392 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
394 AddOnPostAuthenticateRequestAsync (bh, eh, null);
397 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
399 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
400 PostAuthenticateRequest += new EventHandler (invoker.Invoke);
403 public event EventHandler PostAuthorizeRequest;
404 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
406 AddOnPostAuthorizeRequestAsync (bh, eh, null);
409 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
411 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
412 PostAuthorizeRequest += new EventHandler (invoker.Invoke);
415 public event EventHandler PostResolveRequestCache;
416 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
418 AddOnPostResolveRequestCacheAsync (bh, eh, null);
421 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
423 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
424 PostResolveRequestCache += new EventHandler (invoker.Invoke);
427 public event EventHandler PostMapRequestHandler;
428 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh)
430 AddOnPostMapRequestHandlerAsync (bh, eh, null);
433 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh, object data)
435 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
436 PostMapRequestHandler += new EventHandler (invoker.Invoke);
439 public event EventHandler PostAcquireRequestState;
440 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
442 AddOnPostAcquireRequestStateAsync (bh, eh, null);
445 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
447 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
448 PostAcquireRequestState += new EventHandler (invoker.Invoke);
451 public event EventHandler PostReleaseRequestState;
452 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
454 AddOnPostReleaseRequestStateAsync (bh, eh, null);
457 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
459 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
460 PostReleaseRequestState += new EventHandler (invoker.Invoke);
463 public event EventHandler PostUpdateRequestCache;
464 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
466 AddOnPostUpdateRequestCacheAsync (bh, eh, null);
469 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
471 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
472 PostUpdateRequestCache += new EventHandler (invoker.Invoke);
476 // The new overloads that take a data parameter
478 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
480 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
481 AcquireRequestState += new EventHandler (invoker.Invoke);
484 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
486 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
487 AuthenticateRequest += new EventHandler (invoker.Invoke);
490 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
492 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
493 AuthorizeRequest += new EventHandler (invoker.Invoke);
496 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
498 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
499 BeginRequest += new EventHandler (invoker.Invoke);
502 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
504 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
505 EndRequest += new EventHandler (invoker.Invoke);
508 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
510 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
511 PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
514 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
516 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
517 PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
520 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
522 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
523 ReleaseRequestState += new EventHandler (invoker.Invoke);
526 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
528 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
529 ResolveRequestCache += new EventHandler (invoker.Invoke);
532 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
534 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
535 UpdateRequestCache += new EventHandler (invoker.Invoke);
539 internal event EventHandler DefaultAuthentication;
542 // Bypass all the event on the Http pipeline and go directly to EndRequest
544 public void CompleteRequest ()
546 stop_processing = true;
549 internal bool RequestCompleted {
550 set { stop_processing = value; }
553 public virtual void Dispose ()
555 if (modcoll != null) {
556 for (int i = modcoll.Count; i >= 0; i--) {
557 modcoll.Get (i).Dispose ();
562 if (Disposed != null)
563 Disposed (this, EventArgs.Empty);
569 public virtual string GetVaryByCustomString (HttpContext context, string custom)
571 if (custom == null) // Sigh
572 throw new NullReferenceException ();
574 if (0 == String.Compare (custom, "browser", true, CultureInfo.InvariantCulture))
575 return context.Request.Browser.Type;
581 // If we catch an error, queue this error
583 void ProcessError (Exception e)
585 bool first = context.Error == null;
587 context.AddError (e);
591 Error (this, EventArgs.Empty);
592 } catch (ThreadAbortException ee){
593 // This happens on Redirect() or End()
594 Thread.ResetAbort ();
595 } catch (Exception ee){
596 context.AddError (ee);
600 stop_processing = true;
604 // Ticks the clock: next step on the pipeline.
609 if (pipeline.MoveNext ()){
610 if ((bool)pipeline.Current)
613 } catch (Exception e) {
614 Console.WriteLine ("Tick caught an exception that has not been propagated:\n" + e);
627 // Invoked when our async callback called from RunHooks completes,
628 // we restart the pipeline here.
630 void async_callback_completed_cb (IAsyncResult ar)
632 if (current_ai.end != null){
635 } catch (Exception e) {
643 void async_handler_complete_cb (IAsyncResult ar)
645 IHttpAsyncHandler async_handler = ((IHttpAsyncHandler) ar.AsyncState);
648 async_handler.EndProcessRequest (ar);
649 } catch (Exception e){
657 // This enumerator yields whether processing must be stopped:
658 // true: processing of the pipeline must be stopped
659 // false: processing of the pipeline must not be stopped
661 #if TARGET_JVM && !NET_2_0
662 class RunHooksEnumerator : IEnumerable, IEnumerator
664 Delegate [] delegates;
668 internal RunHooksEnumerator(HttpApplication app, Delegate list)
671 delegates = list.GetInvocationList ();
674 public virtual IEnumerator GetEnumerator() { return this; }
675 public virtual object Current { get{ return app.stop_processing; } }
676 public virtual void Reset()
678 throw new NotImplementedException("HttpApplication.RunHooksEnumerator.Reset called.");
680 public virtual bool MoveNext ()
682 while (currentStep < delegates.Length) {
683 if (ProcessDelegate((EventHandler)delegates[currentStep++]))
689 bool ProcessDelegate(EventHandler d)
691 if (d.Target != null && (d.Target is AsyncInvoker)){
692 app.current_ai = (AsyncInvoker) d.Target;
695 app.must_yield = true;
697 app.context.BeginTimeoutPossible ();
698 app.current_ai.begin (app, EventArgs.Empty, new AsyncCallback(app.async_callback_completed_cb), app.current_ai.data);
700 catch (ThreadAbortException taex){
701 object obj = taex.ExceptionState;
702 Thread.ResetAbort ();
703 app.stop_processing = true;
704 if (obj is StepTimeout)
705 app.ProcessError (new HttpException ("The request timed out."));
708 app.ProcessError (e);
711 app.in_begin = false;
712 app.context.EndTimeoutPossible ();
716 // If things are still moving forward, yield this
721 else if (app.stop_processing)
726 app.context.BeginTimeoutPossible ();
727 d (app, EventArgs.Empty);
728 } catch (ThreadAbortException taex){
729 object obj = taex.ExceptionState;
730 Thread.ResetAbort ();
731 app.stop_processing = true;
732 if (obj is StepTimeout)
733 app.ProcessError (new HttpException ("The request timed out."));
736 app.ProcessError (e);
739 app.context.EndTimeoutPossible ();
741 if (app.stop_processing)
748 IEnumerable RunHooks (Delegate list)
750 return new RunHooksEnumerator(this, list);
753 IEnumerable RunHooks (Delegate list)
755 Delegate [] delegates = list.GetInvocationList ();
757 foreach (EventHandler d in delegates){
758 if (d.Target != null && (d.Target is AsyncInvoker)){
759 current_ai = (AsyncInvoker) d.Target;
764 context.BeginTimeoutPossible ();
765 current_ai.begin (this, EventArgs.Empty, async_callback_completed_cb, current_ai.data);
766 } catch (ThreadAbortException taex){
767 object obj = taex.ExceptionState;
768 Thread.ResetAbort ();
769 stop_processing = true;
770 if (obj is StepTimeout)
771 ProcessError (new HttpException ("The request timed out."));
772 } catch (Exception e){
776 context.EndTimeoutPossible ();
780 // If things are still moving forward, yield this
784 yield return stop_processing;
785 else if (stop_processing)
789 context.BeginTimeoutPossible ();
790 d (this, EventArgs.Empty);
791 } catch (ThreadAbortException taex){
792 object obj = taex.ExceptionState;
793 Thread.ResetAbort ();
794 stop_processing = true;
795 if (obj is StepTimeout)
796 ProcessError (new HttpException ("The request timed out."));
797 } catch (Exception e){
800 context.EndTimeoutPossible ();
809 static void FinalErrorWrite (HttpResponse response, string error)
812 response.Write (error);
813 response.Flush (true);
821 if (context.Error == null){
823 context.Response.Flush (true);
824 } catch (Exception e){
825 context.AddError (e);
829 Exception error = context.Error;
831 HttpResponse response = context.Response;
833 if (!response.HeadersSent){
834 response.ClearHeaders ();
835 response.ClearContent ();
837 if (error is HttpException){
838 response.StatusCode = ((HttpException)error).GetHttpCode ();
840 error = new HttpException ("", error);
841 response.StatusCode = 500;
843 if (!RedirectCustomError ())
844 FinalErrorWrite (response, ((HttpException) error).GetHtmlErrorMessage ());
846 response.Flush (true);
848 if (!(error is HttpException))
849 error = new HttpException ("", error);
850 FinalErrorWrite (response, ((HttpException) error).GetHtmlErrorMessage ());
857 // Invoked at the end of the pipeline execution
862 if (EndRequest != null)
863 EndRequest (this, EventArgs.Empty);
864 } catch (Exception e){
871 } catch (Exception e) {
872 Console.WriteLine ("Internal error: OutputPage threw an exception " + e);
874 context.WorkerRequest.EndOfRequest();
875 if (begin_iar != null){
877 begin_iar.Complete ();
880 // TODO: if this throws an error, we have no way of reporting it
881 // Not really too bad, since the only failure might be
882 // `HttpRuntime.request_processed'
889 if (factory != null && context.Handler != null){
890 factory.ReleaseHandler (context.Handler);
894 context.Handler = null;
895 // context = null; -> moved to PostDone
903 // Events fired as described in `Http Runtime Support, HttpModules,
904 // Handling Public Events'
906 #if TARGET_JVM && !NET_2_0
907 class PipeLineEnumerator : IEnumerator {
909 IEnumerator currentEnumerator = null;
911 bool pipelineFinished = false;
912 IHttpHandler handler = null;
914 InternalStepDelegate AllocateHandlerDel;
915 InternalStepDelegate ProcessHandlerDel;
916 InternalStepDelegate ReleaseHandlerDel;
918 // true means that we need to yield and return the current value;
919 // false means that we need to go on to the next delegate and return
920 // values from there.
921 delegate bool InternalStepDelegate();
923 internal PipeLineEnumerator(HttpApplication app)
926 AllocateHandlerDel = new InternalStepDelegate(AllocateHandler);
927 ProcessHandlerDel = new InternalStepDelegate(ProcessHandler);
928 ReleaseHandlerDel = new InternalStepDelegate(ReleaseHandler);
931 public virtual object Current
935 if (currentEnumerator != null)
936 return currentEnumerator.Current;
941 // See InternalStepDelegate for meaning of true/false return value
942 bool AllocateHandler()
944 // Obtain the handler for the request.
946 handler = app.GetHandler (app.context);
948 catch (FileNotFoundException fnf){
949 if (app.context.Request.IsLocal)
950 app.ProcessError (new HttpException (404, String.Format ("File not found {0}", fnf.FileName), fnf));
952 app.ProcessError (new HttpException (404, "File not found", fnf));
953 } catch (DirectoryNotFoundException dnf){
954 app.ProcessError (new HttpException (404, "Directory not found", dnf));
955 } catch (Exception e) {
956 app.ProcessError (e);
959 if (app.stop_processing) {
966 // See InternalStepDelegate for meaning of true/false return value
967 bool ProcessHandler()
970 // From this point on, we need to ensure that we call
971 // ReleaseRequestState, so the code below jumps to
972 // `release:' to guarantee it rather than yielding.
974 if (app.PreRequestHandlerExecute != null)
975 foreach (bool stop in app.RunHooks (app.PreRequestHandlerExecute))
980 app.context.BeginTimeoutPossible ();
981 if (handler != null){
982 IHttpAsyncHandler async_handler = handler as IHttpAsyncHandler;
984 if (async_handler != null){
985 app.must_yield = true;
987 async_handler.BeginProcessRequest (app.context, new AsyncCallback(app.async_handler_complete_cb), handler);
989 app.must_yield = false;
990 handler.ProcessRequest (app.context);
994 catch (ThreadAbortException taex){
995 object obj = taex.ExceptionState;
996 Thread.ResetAbort ();
997 app.stop_processing = true;
998 if (obj is StepTimeout)
999 app.ProcessError (new HttpException ("The request timed out."));
1001 catch (Exception e){
1002 app.ProcessError (e);
1005 app.in_begin = false;
1006 app.context.EndTimeoutPossible ();
1008 if (app.must_yield) {
1009 currentVal = app.stop_processing;
1012 else if (app.stop_processing)
1015 // These are executed after the application has returned
1016 if (app.PostRequestHandlerExecute != null)
1017 foreach (bool stop in app.RunHooks (app.PostRequestHandlerExecute))
1024 // See InternalStepDelegate for meaning of true/false return value
1025 bool ReleaseHandler()
1027 if (app.ReleaseRequestState != null){
1028 foreach (bool stop in app.RunHooks (app.ReleaseRequestState)){
1030 // Ignore the stop signal while release the state
1035 if (app.stop_processing) {
1042 Delegate FindNextDelegate ()
1044 switch(currentStep++) {
1045 case 1: return app.BeginRequest;
1046 case 2: return app.AuthenticateRequest;
1047 case 3: return app.DefaultAuthentication;
1049 case 4: return app.PostAuthenticateRequest;
1051 case 5: return app.AuthorizeRequest;
1053 case 6: return app.PostAuthorizeRequest;
1055 case 7: return app.ResolveRequestCache;
1056 case 8: return AllocateHandlerDel;
1058 case 9: return app.PostResolveRequestCache;
1061 case 10: return app.PostMapRequestHandler;
1063 case 11: return app.AcquireRequestState;
1065 case 12: return app.PostAcquireRequestState;
1067 case 13: return app.ResolveRequestCache;
1068 case 14: return ProcessHandlerDel;
1069 case 15: return ReleaseHandlerDel;
1071 case 16: return app.PostReleaseRequestState;
1073 case 17: return app.UpdateRequestCache;
1075 case 18: return app.PostUpdateRequestCache;
1077 case 19: pipelineFinished = true; return null;
1082 public virtual bool MoveNext ()
1084 while (!pipelineFinished) {
1085 if (currentEnumerator != null && currentEnumerator.MoveNext())
1087 currentEnumerator = null;
1089 Delegate d = FindNextDelegate();
1090 InternalStepDelegate d1 = d as InternalStepDelegate;
1096 currentEnumerator = app.RunHooks(d).GetEnumerator();
1099 app.PipelineDone ();
1103 public virtual void Reset()
1105 throw new NotImplementedException("HttpApplication.PipelineEnumerator.Reset called.");
1109 IEnumerator Pipeline ()
1111 return new PipeLineEnumerator(this);
1114 IEnumerator Pipeline ()
1116 if (stop_processing)
1119 if (BeginRequest != null)
1120 foreach (bool stop in RunHooks (BeginRequest))
1123 if (AuthenticateRequest != null)
1124 foreach (bool stop in RunHooks (AuthenticateRequest))
1127 if (DefaultAuthentication != null)
1128 foreach (bool stop in RunHooks (DefaultAuthentication))
1132 if (PostAuthenticateRequest != null)
1133 foreach (bool stop in RunHooks (AuthenticateRequest))
1136 if (AuthorizeRequest != null)
1137 foreach (bool stop in RunHooks (AuthorizeRequest))
1140 if (PostAuthorizeRequest != null)
1141 foreach (bool stop in RunHooks (PostAuthorizeRequest))
1145 if (ResolveRequestCache != null)
1146 foreach (bool stop in RunHooks (ResolveRequestCache))
1149 // Obtain the handler for the request.
1150 IHttpHandler handler = null;
1152 handler = GetHandler (context);
1153 } catch (FileNotFoundException fnf){
1154 if (context.Request.IsLocal)
1155 ProcessError (new HttpException (404, String.Format ("File not found {0}", fnf.FileName), fnf));
1157 ProcessError (new HttpException (404, "File not found", fnf));
1158 } catch (DirectoryNotFoundException dnf){
1159 ProcessError (new HttpException (404, "Directory not found", dnf));
1160 } catch (Exception e) {
1164 if (stop_processing)
1168 if (PostResolveRequestCache != null)
1169 foreach (bool stop in RunHooks (PostResolveRequestCache))
1172 if (PostMapRequestHandler != null)
1173 foreach (bool stop in RunHooks (PostMapRequestHandler))
1177 if (AcquireRequestState != null){
1178 foreach (bool stop in RunHooks (AcquireRequestState))
1183 if (PostAcquireRequestState != null){
1184 foreach (bool stop in RunHooks (PostAcquireRequestState))
1190 // From this point on, we need to ensure that we call
1191 // ReleaseRequestState, so the code below jumps to
1192 // `release:' to guarantee it rather than yielding.
1194 if (PreRequestHandlerExecute != null)
1195 foreach (bool stop in RunHooks (PreRequestHandlerExecute))
1200 context.BeginTimeoutPossible ();
1201 if (handler != null){
1202 IHttpAsyncHandler async_handler = handler as IHttpAsyncHandler;
1204 if (async_handler != null){
1207 async_handler.BeginProcessRequest (context, async_handler_complete_cb, handler);
1210 handler.ProcessRequest (context);
1213 } catch (ThreadAbortException taex){
1214 object obj = taex.ExceptionState;
1215 Thread.ResetAbort ();
1216 stop_processing = true;
1217 if (obj is StepTimeout)
1218 ProcessError (new HttpException ("The request timed out."));
1219 } catch (Exception e){
1223 context.EndTimeoutPossible ();
1226 yield return stop_processing;
1227 else if (stop_processing)
1230 // These are executed after the application has returned
1232 if (PostRequestHandlerExecute != null)
1233 foreach (bool stop in RunHooks (PostRequestHandlerExecute))
1238 if (ReleaseRequestState != null){
1239 foreach (bool stop in RunHooks (ReleaseRequestState)){
1241 // Ignore the stop signal while release the state
1247 if (stop_processing)
1251 if (PostReleaseRequestState != null)
1252 foreach (bool stop in RunHooks (PostReleaseRequestState))
1256 if (context.Error == null)
1257 context.Response.DoFilter (true);
1259 if (UpdateRequestCache != null)
1260 foreach (bool stop in RunHooks (UpdateRequestCache))
1264 if (PostUpdateRequestCache != null)
1265 foreach (bool stop in RunHooks (PostUpdateRequestCache))
1275 HttpRuntime.TimeoutManager.Add (context);
1277 Thread th = Thread.CurrentThread;
1278 if (app_culture != null) {
1279 prev_app_culture = th.CurrentCulture;
1280 th.CurrentCulture = app_culture;
1283 if (appui_culture != null) {
1284 prev_appui_culture = th.CurrentUICulture;
1285 th.CurrentUICulture = appui_culture;
1289 prev_user = Thread.CurrentPrincipal;
1295 Thread th = Thread.CurrentThread;
1297 if (Thread.CurrentPrincipal != prev_user)
1298 Thread.CurrentPrincipal = prev_user;
1300 if (prev_appui_culture != null && prev_appui_culture != th.CurrentUICulture)
1301 th.CurrentUICulture = prev_appui_culture;
1302 if (prev_app_culture != null && prev_app_culture != th.CurrentCulture)
1303 th.CurrentCulture = prev_app_culture;
1306 HttpRuntime.TimeoutManager.Remove (context);
1310 HttpContext.Current = null;
1313 void Start (object x)
1317 pipeline = Pipeline ();
1321 // Used by HttpServerUtility.Execute
1322 internal IHttpHandler GetHandler (HttpContext context)
1324 HttpRequest request = context.Request;
1325 string verb = request.RequestType;
1326 string url = request.FilePath;
1328 IHttpHandler handler = null;
1329 #if CONFIGURATION_2_0
1330 HttpHandlersSection section = (HttpHandlersSection) WebConfigurationManager.GetWebApplicationSection ("system.web/httpHandlers");
1331 object o = section.LocateHandler (verb, url);
1333 HandlerFactoryConfiguration factory_config = (HandlerFactoryConfiguration) HttpContext.GetAppConfig ("system.web/httpHandlers");
1334 object o = factory_config.LocateHandler (verb, url);
1337 factory = o as IHttpHandlerFactory;
1339 if (factory == null) {
1340 handler = (IHttpHandler) o;
1342 handler = factory.GetHandler (context, verb, url, request.PhysicalPath);
1344 context.Handler = handler;
1349 void IHttpHandler.ProcessRequest (HttpContext context)
1352 this.context = context;
1360 // This is used by FireOnAppStart, when we init the application
1361 // as the context is required to be set at that point (the user
1362 // might call methods that require it on that hook).
1364 internal void SetContext (HttpContext context)
1366 this.context = context;
1369 internal void SetSession (HttpSessionState session)
1371 this.session = session;
1374 IAsyncResult IHttpAsyncHandler.BeginProcessRequest (HttpContext context, AsyncCallback cb, object extraData)
1376 this.context = context;
1379 begin_iar = new AsyncRequestState (done, cb, extraData);
1383 if (Thread.CurrentThread.IsThreadPoolThread)
1387 ThreadPool.QueueUserWorkItem (new WaitCallback (Start), null);
1392 void IHttpAsyncHandler.EndProcessRequest (IAsyncResult result)
1394 if (!result.IsCompleted)
1395 result.AsyncWaitHandle.WaitOne ();
1399 public virtual void Init ()
1403 bool IHttpHandler.IsReusable {
1410 internal void ClearError ()
1412 context.ClearError ();
1415 bool RedirectErrorPage (string error_page)
1417 if (context.Request.QueryString ["aspxerrorpath"] != null)
1420 Response.Redirect (error_page + "?aspxerrorpath=" + Request.Path, false);
1424 bool RedirectCustomError ()
1426 if (!context.IsCustomErrorEnabled)
1429 #if CONFIGURATION_2_0
1430 CustomErrorsSection config = (CustomErrorsSection)WebConfigurationManager.GetWebApplicationSection ("system.web/customErrors");
1432 CustomErrorsConfig config = null;
1434 config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
1438 if (config == null) {
1439 if (context.ErrorPage != null)
1440 return RedirectErrorPage (context.ErrorPage);
1445 #if CONFIGURATION_2_0
1446 CustomError err = config.Errors [context.Response.StatusCode.ToString()];
1447 string redirect = err == null ? null : err.Redirect;
1449 string redirect = config [context.Response.StatusCode];
1451 if (redirect == null) {
1452 redirect = context.ErrorPage;
1453 if (redirect == null)
1454 redirect = config.DefaultRedirect;
1457 if (redirect == null)
1460 return RedirectErrorPage (redirect);
1466 // Based on Fritz' Onion's AsyncRequestState class for asynchronous IHttpAsyncHandlers
1468 class AsyncRequestState : IAsyncResult {
1472 ManualResetEvent complete_event = null;
1474 internal AsyncRequestState (ManualResetEvent complete_event, AsyncCallback cb, object cb_data)
1477 this.cb_data = cb_data;
1478 this.complete_event = complete_event;
1481 internal void Complete ()
1487 complete_event.Set ();
1490 public object AsyncState {
1496 public bool CompletedSynchronously {
1502 public bool IsCompleted {
1508 public WaitHandle AsyncWaitHandle {
1510 return complete_event;
1515 #region Helper classes
1518 // A wrapper to keep track of begin/end pairs
1520 class AsyncInvoker {
1521 public BeginEventHandler begin;
1522 public EndEventHandler end;
1525 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, object d)
1532 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh)
1538 public void Invoke (object sender, EventArgs e)
1540 throw new Exception ("This is just a dummy");