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.Principal;
70 using System.Threading;
71 using System.Web.Configuration;
72 using System.Web.SessionState;
75 namespace System.Web {
78 public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable {
82 // The source, and the exposed API (cache).
83 HttpModuleCollection modcoll;
85 internal string AssemblyLocation;
88 // The factory for the handler currently running.
90 IHttpHandlerFactory factory;
93 // Whether the pipeline should be stopped
100 IEnumerator pipeline;
102 // To flag when we are done processing a request from BeginProcessRequest.
103 ManualResetEvent done;
105 // The current IAsyncResult for the running async request handler in the pipeline
106 AsyncRequestState begin_iar;
108 // Tracks the current AsyncInvocation being dispatched
109 AsyncInvoker current_ai;
111 // We don't use the EventHandlerList here, but derived classes might do
112 EventHandlerList events;
114 // Culture and IPrincipal
115 CultureInfo app_culture;
116 CultureInfo appui_culture;
117 CultureInfo prev_app_culture;
118 CultureInfo prev_appui_culture;
119 IPrincipal prev_user;
122 // These are used to detect the case where the EndXXX method is invoked
123 // from within the BeginXXXX delegate, so we detect whether we kick the
124 // pipeline from here, or from the the RunHook routine
129 public HttpApplication ()
131 AssemblyLocation = GetType ().Assembly.Location;
132 done = new ManualResetEvent (false);
141 ModulesConfiguration modules;
142 modules = (ModulesConfiguration) HttpContext.GetAppConfig ("system.web/httpModules");
144 modcoll = modules.LoadModules (this);
146 HttpApplicationFactory.AttachEvents (this);
148 GlobalizationConfiguration cfg = GlobalizationConfiguration.GetInstance (null);
150 app_culture = cfg.Culture;
151 appui_culture = cfg.UICulture;
157 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
158 public HttpApplicationState Application {
160 return HttpApplicationFactory.ApplicationState;
165 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
166 public HttpContext Context {
172 protected EventHandlerList Events {
175 events = new EventHandlerList ();
182 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
183 public HttpModuleCollection Modules {
186 modcoll = new HttpModuleCollection ();
193 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
194 public HttpRequest Request {
196 return context.Request;
201 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
202 public HttpResponse Response {
204 return context.Response;
209 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
210 public HttpServerUtility Server {
213 return context.Server;
216 // This is so we can get the Server and call a few methods
217 // which are not context sensitive, see HttpServerUtilityTest
219 return new HttpServerUtility ((HttpContext) null);
224 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
225 public HttpSessionState Session {
227 return context.Session;
232 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
233 public virtual ISite Site {
244 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
245 public IPrincipal User {
247 if (context.User == null)
248 throw new HttpException ("Currently authenticated user");
254 public virtual event EventHandler Disposed;
255 public virtual event EventHandler Error;
257 public event EventHandler PreSendRequestHeaders;
258 internal void TriggerPreSendRequestHeaders ()
260 if (PreSendRequestHeaders != null)
261 PreSendRequestHeaders (this, EventArgs.Empty);
264 public event EventHandler PreSendRequestContent;
265 internal void TriggerPreSendRequestContent ()
267 if (PreSendRequestContent != null)
268 PreSendRequestContent (this, EventArgs.Empty);
271 public event EventHandler AcquireRequestState;
272 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
274 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
275 AcquireRequestState += new EventHandler (invoker.Invoke);
278 public event EventHandler AuthenticateRequest;
279 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
281 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
282 AuthenticateRequest += new EventHandler (invoker.Invoke);
285 public event EventHandler AuthorizeRequest;
286 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
288 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
289 AuthorizeRequest += new EventHandler (invoker.Invoke);
292 public event EventHandler BeginRequest;
293 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh)
295 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
296 BeginRequest += new EventHandler (invoker.Invoke);
299 public event EventHandler EndRequest;
300 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh)
302 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
303 EndRequest += new EventHandler (invoker.Invoke);
306 public event EventHandler PostRequestHandlerExecute;
307 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
309 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
310 PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
313 public event EventHandler PreRequestHandlerExecute;
314 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
316 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
317 PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
320 public event EventHandler ReleaseRequestState;
321 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
323 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
324 ReleaseRequestState += new EventHandler (invoker.Invoke);
327 public event EventHandler ResolveRequestCache;
328 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
330 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
331 ResolveRequestCache += new EventHandler (invoker.Invoke);
334 public event EventHandler UpdateRequestCache;
335 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
337 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
338 UpdateRequestCache += new EventHandler (invoker.Invoke);
342 public event EventHandler PostAuthenticateRequest;
343 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
345 AddOnPostAuthenticateRequestAsync (bh, eh, null);
348 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
350 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
351 PostAuthenticateRequest += new EventHandler (invoker.Invoke);
354 public event EventHandler PostAuthorizeRequest;
355 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
357 AddOnPostAuthorizeRequestAsync (bh, eh, null);
360 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
362 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
363 PostAuthorizeRequest += new EventHandler (invoker.Invoke);
366 public event EventHandler PostResolveRequestCache;
367 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
369 AddOnPostResolveRequestCacheAsync (bh, eh, null);
372 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
374 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
375 PostResolveRequestCache += new EventHandler (invoker.Invoke);
378 public event EventHandler PostMapRequestHandler;
379 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh)
381 AddOnPostMapRequestHandlerAsync (bh, eh, null);
384 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh, object data)
386 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
387 PostMapRequestHandler += new EventHandler (invoker.Invoke);
390 public event EventHandler PostAcquireRequestState;
391 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
393 AddOnPostAcquireRequestStateAsync (bh, eh, null);
396 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
398 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
399 PostAcquireRequestState += new EventHandler (invoker.Invoke);
402 public event EventHandler PostReleaseRequestState;
403 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
405 AddOnPostReleaseRequestStateAsync (bh, eh, null);
408 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
410 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
411 PostReleaseRequestState += new EventHandler (invoker.Invoke);
414 public event EventHandler PostUpdateRequestCache;
415 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
417 AddOnPostUpdateRequestCacheAsync (bh, eh, null);
420 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
422 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
423 PostUpdateRequestCache += new EventHandler (invoker.Invoke);
427 // The new overloads that take a data parameter
429 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
431 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
432 AcquireRequestState += new EventHandler (invoker.Invoke);
435 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
437 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
438 AuthenticateRequest += new EventHandler (invoker.Invoke);
441 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
443 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
444 AuthorizeRequest += new EventHandler (invoker.Invoke);
447 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
449 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
450 BeginRequest += new EventHandler (invoker.Invoke);
453 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
455 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
456 EndRequest += new EventHandler (invoker.Invoke);
459 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
461 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
462 PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
465 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
467 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
468 PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
471 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
473 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
474 ReleaseRequestState += new EventHandler (invoker.Invoke);
477 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
479 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
480 ResolveRequestCache += new EventHandler (invoker.Invoke);
483 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
485 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
486 UpdateRequestCache += new EventHandler (invoker.Invoke);
490 internal event EventHandler DefaultAuthentication;
493 // Bypass all the event on the Http pipeline and go directly to EndRequest
495 public void CompleteRequest ()
497 stop_processing = true;
500 public virtual void Dispose ()
502 if (modcoll != null) {
503 for (int i = modcoll.Count; i >= 0; i--) {
504 modcoll.Get (i).Dispose ();
509 if (Disposed != null)
510 Disposed (this, EventArgs.Empty);
516 public virtual string GetVaryByCustomString (HttpContext context, string custom)
518 if (custom == null) // Sigh
519 throw new NullReferenceException ();
521 if (0 == String.Compare (custom, "browser", true, CultureInfo.InvariantCulture))
522 return context.Request.Browser.Type;
528 // If we catch an error, queue this error
530 void ProcessError (Exception e)
532 bool first = context.Error == null;
534 context.AddError (e);
538 Error (this, EventArgs.Empty);
539 } catch (Exception ee){
540 context.AddError (ee);
544 stop_processing = true;
548 // Ticks the clock: next step on the pipeline.
553 if (pipeline.MoveNext ()){
554 if ((bool)pipeline.Current)
557 } catch (Exception e) {
558 Console.WriteLine ("Tick caught an exception that has not been propagated:\n" + e);
571 // Invoked when our async callback called from RunHooks completes,
572 // we restart the pipeline here.
574 void async_callback_completed_cb (IAsyncResult ar)
576 if (current_ai.end != null){
579 } catch (Exception e) {
587 void async_handler_complete_cb (IAsyncResult ar)
589 IHttpAsyncHandler async_handler = ((IHttpAsyncHandler) ar.AsyncState);
592 async_handler.EndProcessRequest (ar);
593 } catch (Exception e){
601 // This enumerator yields whether processing must be stopped:
602 // true: processing of the pipeline must be stopped
603 // false: processing of the pipeline must not be stopped
605 IEnumerable RunHooks (Delegate list)
607 Delegate [] delegates = list.GetInvocationList ();
609 foreach (EventHandler d in delegates){
610 if (d.Target != null && (d.Target is AsyncInvoker)){
611 current_ai = (AsyncInvoker) d.Target;
616 context.BeginTimeoutPossible ();
617 current_ai.begin (this, EventArgs.Empty, async_callback_completed_cb, current_ai.data);
618 } catch (ThreadAbortException taex){
619 object obj = taex.ExceptionState;
620 Thread.ResetAbort ();
621 stop_processing = true;
622 if (obj is StepTimeout)
623 ProcessError (new HttpException ("The request timed out."));
624 } catch (Exception e){
628 context.EndTimeoutPossible ();
632 // If things are still moving forward, yield this
636 yield return stop_processing;
637 else if (stop_processing)
641 context.BeginTimeoutPossible ();
642 d (this, EventArgs.Empty);
643 } catch (ThreadAbortException taex){
644 object obj = taex.ExceptionState;
645 Thread.ResetAbort ();
646 stop_processing = true;
647 if (obj is StepTimeout)
648 ProcessError (new HttpException ("The request timed out."));
649 } catch (Exception e){
652 context.EndTimeoutPossible ();
662 if (context.Error == null){
664 context.Response.Flush (true);
665 } catch (Exception e){
666 context.AddError (e);
670 Exception error = context.Error;
672 HttpResponse response = context.Response;
674 if (!response.HeadersSent){
675 response.ClearHeaders ();
676 response.ClearContent ();
678 if (error is HttpException){
679 response.StatusCode = ((HttpException)error).GetHttpCode ();
681 error = new HttpException ("", error);
682 response.StatusCode = 500;
684 if (!RedirectCustomError ())
685 response.Write (((HttpException) error).GetHtmlErrorMessage ());
687 response.Write (((HttpException) error).GetHtmlErrorMessage ());
688 response.Flush (true);
694 // Invoked at the end of the pipeline execution
701 if (EndRequest != null)
702 EndRequest (this, EventArgs.Empty);
704 } catch (Exception e) {
705 Console.WriteLine ("Internal error: we are missing one catch " + e);
707 context.WorkerRequest.EndOfRequest();
708 if (begin_iar != null){
710 begin_iar.Complete ();
713 // TODO: if this throws an error, we have no way of reporting it
714 // Not really too bad, since the only failure might be
715 // `HttpRuntime.request_processed'
722 if (factory != null && context.Handler != null){
723 factory.ReleaseHandler (context.Handler);
727 context.Handler = null;
728 // context = null; -> moved to PostDone
736 // Events fired as described in `Http Runtime Support, HttpModules,
737 // Handling Public Events'
739 IEnumerator Pipeline ()
741 if (BeginRequest != null)
742 foreach (bool stop in RunHooks (BeginRequest))
745 if (AuthenticateRequest != null)
746 foreach (bool stop in RunHooks (AuthenticateRequest))
749 if (DefaultAuthentication != null)
750 foreach (bool stop in RunHooks (DefaultAuthentication))
754 if (PostAuthenticateRequest != null)
755 foreach (bool stop in RunHooks (AuthenticateRequest))
758 if (AuthorizeRequest != null)
759 foreach (bool stop in RunHooks (AuthorizeRequest))
762 if (PostAuthorizeRequest != null)
763 foreach (bool stop in RunHooks (PostAuthorizeRequest))
767 if (ResolveRequestCache != null)
768 foreach (bool stop in RunHooks (ResolveRequestCache))
771 // Obtain the handler for the request.
772 IHttpHandler handler = null;
774 handler = GetHandler (context);
775 } catch (FileNotFoundException fnf){
776 if (context.Request.IsLocal)
777 ProcessError (new HttpException (404, String.Format ("File not found {0}", fnf.FileName), fnf));
779 ProcessError (new HttpException (404, "File not found", fnf));
780 } catch (DirectoryNotFoundException dnf){
781 ProcessError (new HttpException (404, "Directory not found", dnf));
782 } catch (Exception e) {
790 if (PostResolveRequestCache != null)
791 foreach (bool stop in RunHooks (PostResolveRequestCache))
794 if (PostMapRequestHandler != null)
795 foreach (bool stop in RunHooks (PostMapRequestHandler))
799 if (AcquireRequestState != null){
800 foreach (bool stop in RunHooks (AcquireRequestState))
805 if (PostAcquireRequestState != null){
806 foreach (bool stop in RunHooks (PostAcquireRequestState))
812 // From this point on, we need to ensure that we call
813 // ReleaseRequestState, so the code below jumps to
814 // `release:' to guarantee it rather than yielding.
816 if (PreRequestHandlerExecute != null)
817 foreach (bool stop in RunHooks (PreRequestHandlerExecute))
822 context.BeginTimeoutPossible ();
823 if (handler != null){
824 IHttpAsyncHandler async_handler = handler as IHttpAsyncHandler;
826 if (async_handler != null){
829 async_handler.BeginProcessRequest (context, async_handler_complete_cb, handler);
832 handler.ProcessRequest (context);
835 } catch (ThreadAbortException taex){
836 object obj = taex.ExceptionState;
837 Thread.ResetAbort ();
838 stop_processing = true;
839 if (obj is StepTimeout)
840 ProcessError (new HttpException ("The request timed out."));
841 } catch (Exception e){
845 context.EndTimeoutPossible ();
848 yield return stop_processing;
849 else if (stop_processing)
852 // These are executed after the application has returned
854 if (PostRequestHandlerExecute != null)
855 foreach (bool stop in RunHooks (PostRequestHandlerExecute))
860 if (ReleaseRequestState != null){
861 #pragma warning disable 168
862 foreach (bool stop in RunHooks (ReleaseRequestState)){
864 // Ignore the stop signal while release the state
868 #pragma warning restore 168
875 if (PostReleaseRequestState != null)
876 foreach (bool stop in RunHooks (PostReleaseRequestState))
880 if (UpdateRequestCache != null)
881 foreach (bool stop in RunHooks (UpdateRequestCache))
885 if (PostUpdateRequestCache != null)
886 foreach (bool stop in RunHooks (PostUpdateRequestCache))
894 HttpRuntime.TimeoutManager.Add (context);
895 Thread th = Thread.CurrentThread;
896 if (app_culture != null) {
897 prev_app_culture = th.CurrentCulture;
898 th.CurrentCulture = app_culture;
901 if (appui_culture != null) {
902 prev_appui_culture = th.CurrentUICulture;
903 th.CurrentCulture = appui_culture;
906 prev_user = Thread.CurrentPrincipal;
911 Thread th = Thread.CurrentThread;
912 if (Thread.CurrentPrincipal != prev_user)
913 Thread.CurrentPrincipal = prev_user;
914 if (prev_appui_culture != null && prev_appui_culture != th.CurrentUICulture)
915 th.CurrentUICulture = prev_appui_culture;
916 if (prev_app_culture != null && prev_app_culture != th.CurrentCulture)
917 th.CurrentCulture = prev_app_culture;
919 HttpRuntime.TimeoutManager.Remove (context);
921 HttpContext.Current = null;
924 void Start (object x)
928 stop_processing = false;
929 pipeline = Pipeline ();
933 // Used by HttpServerUtility.Execute
934 internal IHttpHandler GetHandler (HttpContext context)
936 HttpRequest request = context.Request;
937 string verb = request.RequestType;
938 string url = request.FilePath;
940 HandlerFactoryConfiguration factory_config = (HandlerFactoryConfiguration) HttpContext.GetAppConfig ("system.web/httpHandlers");
941 object o = factory_config.LocateHandler (verb, url);
942 factory = o as IHttpHandlerFactory;
943 IHttpHandler handler;
945 if (factory == null) {
946 handler = (IHttpHandler) o;
948 handler = factory.GetHandler (context, verb, url, request.PhysicalPath);
950 context.Handler = handler;
955 void IHttpHandler.ProcessRequest (HttpContext context)
958 this.context = context;
966 // This is used by FireOnAppStart, when we init the application
967 // as the context is required to be set at that point (the user
968 // might call methods that require it on that hook).
970 internal void SetContext (HttpContext context)
972 this.context = context;
975 IAsyncResult IHttpAsyncHandler.BeginProcessRequest (HttpContext context, AsyncCallback cb, object extraData)
977 this.context = context;
980 begin_iar = new AsyncRequestState (done, cb, extraData);
982 if (Thread.CurrentThread.IsThreadPoolThread)
985 ThreadPool.QueueUserWorkItem (new WaitCallback (Start), null);
990 void IHttpAsyncHandler.EndProcessRequest (IAsyncResult result)
992 if (!result.IsCompleted)
993 result.AsyncWaitHandle.WaitOne ();
997 public virtual void Init ()
1001 bool IHttpHandler.IsReusable {
1008 internal void ClearError ()
1010 context.ClearError ();
1013 bool RedirectErrorPage (string error_page)
1015 if (context.Request.QueryString ["aspxerrorpath"] != null)
1018 Response.Redirect (error_page + "?aspxerrorpath=" + Request.Path, false);
1022 bool RedirectCustomError ()
1024 if (!context.IsCustomErrorEnabled)
1027 CustomErrorsConfig config = null;
1029 config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
1032 if (config == null) {
1033 if (context.ErrorPage != null)
1034 return RedirectErrorPage (context.ErrorPage);
1039 string redirect = config [context.Response.StatusCode];
1040 if (redirect == null) {
1041 redirect = context.ErrorPage;
1042 if (redirect == null)
1043 redirect = config.DefaultRedirect;
1046 if (redirect == null)
1049 return RedirectErrorPage (redirect);
1055 // Based on Fritz' Onion's AsyncRequestState class for asynchronous IHttpAsyncHandlers
1057 class AsyncRequestState : IAsyncResult {
1061 ManualResetEvent complete_event = null;
1063 internal AsyncRequestState (ManualResetEvent complete_event, AsyncCallback cb, object cb_data)
1066 this.cb_data = cb_data;
1067 this.complete_event = complete_event;
1070 internal void Complete ()
1076 complete_event.Set ();
1079 public object AsyncState {
1085 public bool CompletedSynchronously {
1091 public bool IsCompleted {
1097 public WaitHandle AsyncWaitHandle {
1099 return complete_event;
1104 #region Helper classes
1107 // A wrapper to keep track of begin/end pairs
1109 class AsyncInvoker {
1110 public BeginEventHandler begin;
1111 public EndEventHandler end;
1114 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, object d)
1121 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh)
1127 public void Invoke (object sender, EventArgs e)
1129 throw new Exception ("This is just a dummy");