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.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.Caching;
75 using System.Web.Configuration;
76 using System.Web.SessionState;
83 namespace System.Web {
86 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
87 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
90 public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable {
91 object this_lock = new object();
93 internal static readonly string [] BinDirs = {"Bin", "bin"};
96 HttpSessionState session;
99 // The source, and the exposed API (cache).
100 HttpModuleCollection modcoll;
102 string assemblyLocation;
105 // The factory for the handler currently running.
107 IHttpHandlerFactory factory;
110 // Whether the thread culture is to be auto-set.
111 // Used only in the 2.0 profile, always false for 1.x
117 // Whether the pipeline should be stopped
119 bool stop_processing;
124 IEnumerator pipeline;
126 // To flag when we are done processing a request from BeginProcessRequest.
127 ManualResetEvent done;
129 // The current IAsyncResult for the running async request handler in the pipeline
130 AsyncRequestState begin_iar;
132 // Tracks the current AsyncInvocation being dispatched
133 AsyncInvoker current_ai;
135 // We don't use the EventHandlerList here, but derived classes might do
136 EventHandlerList events;
138 // Culture and IPrincipal
139 CultureInfo app_culture;
140 CultureInfo appui_culture;
141 CultureInfo prev_app_culture;
142 CultureInfo prev_appui_culture;
143 IPrincipal prev_user;
145 static string binDirectory;
149 const string initialization_exception_key = "System.Web.HttpApplication.initialization_exception";
150 static Exception initialization_exception {
151 get { return (Exception) AppDomain.CurrentDomain.GetData (initialization_exception_key); }
152 set { AppDomain.CurrentDomain.SetData (initialization_exception_key, value); }
155 static Exception initialization_exception;
157 bool removeConfigurationFromCache;
161 // These are used to detect the case where the EndXXX method is invoked
162 // from within the BeginXXXX delegate, so we detect whether we kick the
163 // pipeline from here, or from the the RunHook routine
168 public HttpApplication ()
170 done = new ManualResetEvent (false);
173 internal void InitOnce (bool full_init)
180 HttpModulesSection modules;
181 modules = (HttpModulesSection) WebConfigurationManager.GetSection ("system.web/httpModules", HttpRuntime.AppDomainAppVirtualPath);
183 ModulesConfiguration modules;
185 modules = (ModulesConfiguration) HttpContext.GetAppConfig ("system.web/httpModules");
188 modcoll = modules.LoadModules (this);
191 HttpApplicationFactory.AttachEvents (this);
197 internal string AssemblyLocation {
199 if (assemblyLocation == null)
200 assemblyLocation = GetType ().Assembly.Location;
201 return assemblyLocation;
206 internal static Exception InitializationException {
207 get { return initialization_exception; }
212 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
213 public HttpApplicationState Application {
215 return HttpApplicationFactory.ApplicationState;
220 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
221 public HttpContext Context {
227 protected EventHandlerList Events {
230 events = new EventHandlerList ();
237 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
238 public HttpModuleCollection Modules {
239 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
243 modcoll = new HttpModuleCollection ();
251 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
252 public HttpRequest Request {
255 throw new HttpException (Locale.GetText ("No context is available."));
257 if (false == HttpApplicationFactory.ContextAvailable)
258 throw new HttpException (Locale.GetText ("Request is not available in this context."));
260 return context.Request;
265 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
266 public HttpResponse Response {
269 throw new HttpException (Locale.GetText ("No context is available."));
271 if (false == HttpApplicationFactory.ContextAvailable)
272 throw new HttpException (Locale.GetText ("Response is not available in this context."));
274 return context.Response;
279 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
280 public HttpServerUtility Server {
283 return context.Server;
286 // This is so we can get the Server and call a few methods
287 // which are not context sensitive, see HttpServerUtilityTest
289 return new HttpServerUtility ((HttpContext) null);
294 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
295 public HttpSessionState Session {
297 // Only used for Session_End
302 throw new HttpException (Locale.GetText ("No context is available."));
303 return context.Session;
308 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
312 public virtual ISite Site {
324 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
325 public IPrincipal User {
328 throw new HttpException (Locale.GetText ("No context is available."));
329 if (context.User == null)
330 throw new HttpException (Locale.GetText ("No currently authenticated user."));
336 public virtual event EventHandler Disposed;
337 public virtual event EventHandler Error;
339 public event EventHandler PreSendRequestHeaders;
340 internal void TriggerPreSendRequestHeaders ()
342 if (PreSendRequestHeaders != null)
343 PreSendRequestHeaders (this, EventArgs.Empty);
346 public event EventHandler PreSendRequestContent;
347 internal void TriggerPreSendRequestContent ()
349 if (PreSendRequestContent != null)
350 PreSendRequestContent (this, EventArgs.Empty);
353 public event EventHandler AcquireRequestState;
354 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
356 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
357 AcquireRequestState += new EventHandler (invoker.Invoke);
360 public event EventHandler AuthenticateRequest;
361 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
363 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
364 AuthenticateRequest += new EventHandler (invoker.Invoke);
367 public event EventHandler AuthorizeRequest;
368 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
370 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
371 AuthorizeRequest += new EventHandler (invoker.Invoke);
374 public event EventHandler BeginRequest;
375 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh)
377 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
378 BeginRequest += new EventHandler (invoker.Invoke);
381 public event EventHandler EndRequest;
382 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh)
384 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
385 EndRequest += new EventHandler (invoker.Invoke);
388 public event EventHandler PostRequestHandlerExecute;
389 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
391 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
392 PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
395 public event EventHandler PreRequestHandlerExecute;
396 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
398 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
399 PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
402 public event EventHandler ReleaseRequestState;
403 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
405 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
406 ReleaseRequestState += new EventHandler (invoker.Invoke);
409 public event EventHandler ResolveRequestCache;
410 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
412 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
413 ResolveRequestCache += new EventHandler (invoker.Invoke);
416 public event EventHandler UpdateRequestCache;
417 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
419 AsyncInvoker invoker = new AsyncInvoker (bh, eh);
420 UpdateRequestCache += new EventHandler (invoker.Invoke);
424 public event EventHandler PostAuthenticateRequest;
425 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
427 AddOnPostAuthenticateRequestAsync (bh, eh, null);
430 public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
432 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
433 PostAuthenticateRequest += new EventHandler (invoker.Invoke);
436 public event EventHandler PostAuthorizeRequest;
437 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
439 AddOnPostAuthorizeRequestAsync (bh, eh, null);
442 public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
444 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
445 PostAuthorizeRequest += new EventHandler (invoker.Invoke);
448 public event EventHandler PostResolveRequestCache;
449 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
451 AddOnPostResolveRequestCacheAsync (bh, eh, null);
454 public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
456 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
457 PostResolveRequestCache += new EventHandler (invoker.Invoke);
460 public event EventHandler PostMapRequestHandler;
461 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh)
463 AddOnPostMapRequestHandlerAsync (bh, eh, null);
466 public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh, object data)
468 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
469 PostMapRequestHandler += new EventHandler (invoker.Invoke);
472 public event EventHandler PostAcquireRequestState;
473 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
475 AddOnPostAcquireRequestStateAsync (bh, eh, null);
478 public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
480 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
481 PostAcquireRequestState += new EventHandler (invoker.Invoke);
484 public event EventHandler PostReleaseRequestState;
485 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
487 AddOnPostReleaseRequestStateAsync (bh, eh, null);
490 public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
492 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
493 PostReleaseRequestState += new EventHandler (invoker.Invoke);
496 public event EventHandler PostUpdateRequestCache;
497 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
499 AddOnPostUpdateRequestCacheAsync (bh, eh, null);
502 public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
504 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
505 PostUpdateRequestCache += new EventHandler (invoker.Invoke);
509 // The new overloads that take a data parameter
511 public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
513 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
514 AcquireRequestState += new EventHandler (invoker.Invoke);
517 public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
519 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
520 AuthenticateRequest += new EventHandler (invoker.Invoke);
523 public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
525 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
526 AuthorizeRequest += new EventHandler (invoker.Invoke);
529 public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
531 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
532 BeginRequest += new EventHandler (invoker.Invoke);
535 public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
537 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
538 EndRequest += new EventHandler (invoker.Invoke);
541 public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
543 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
544 PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
547 public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
549 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
550 PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
553 public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
555 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
556 ReleaseRequestState += new EventHandler (invoker.Invoke);
559 public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
561 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
562 ResolveRequestCache += new EventHandler (invoker.Invoke);
565 public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
567 AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
568 UpdateRequestCache += new EventHandler (invoker.Invoke);
572 // They are for use with the IIS7 integrated mode, but have been added for compatibility
573 public event EventHandler LogRequest;
574 public void AddOnLogRequestAsync (BeginEventHandler bh, EndEventHandler eh)
576 AddOnLogRequestAsync (bh, eh, null);
579 public void AddOnLogRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state)
581 AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, state);
582 LogRequest += new EventHandler (invoker.Invoke);
585 public event EventHandler MapRequestHandler;
586 public void AddOnMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh)
588 AddOnMapRequestHandlerAsync (bh, eh, null);
591 public void AddOnMapRequestHandlerAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state)
593 AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, state);
594 MapRequestHandler += new EventHandler (invoker.Invoke);
597 public event EventHandler PostLogRequest;
598 public void AddOnPostLogRequestAsync (BeginEventHandler bh, EndEventHandler eh)
600 AddOnPostLogRequestAsync (bh, eh, null);
603 public void AddOnPostLogRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state)
605 AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, state);
606 PostLogRequest += new EventHandler (invoker.Invoke);
611 internal event EventHandler DefaultAuthentication;
614 // Bypass all the event on the Http pipeline and go directly to EndRequest
616 public void CompleteRequest ()
618 stop_processing = true;
621 internal bool RequestCompleted {
622 set { stop_processing = value; }
625 public virtual void Dispose ()
628 if (modcoll != null) {
629 for (int i = modcoll.Count - 1; i >= 0; i--) {
630 modcoll.Get (i).Dispose ();
636 if (Disposed != null)
637 Disposed (this, EventArgs.Empty);
643 public virtual string GetVaryByCustomString (HttpContext context, string custom)
645 if (custom == null) // Sigh
646 throw new NullReferenceException ();
648 if (0 == String.Compare (custom, "browser", true, CultureInfo.InvariantCulture))
649 return context.Request.Browser.Type;
655 // If we catch an error, queue this error
657 void ProcessError (Exception e)
659 bool first = context.Error == null;
660 context.AddError (e);
664 Error (this, EventArgs.Empty);
665 } catch (ThreadAbortException taex){
666 context.ClearError ();
667 if (FlagEnd.Value == taex.ExceptionState)
668 // This happens on Redirect() or End()
669 Thread.ResetAbort ();
671 // This happens on Thread.Abort()
672 context.AddError (taex);
673 } catch (Exception ee){
674 context.AddError (ee);
678 stop_processing = true;
680 // we want to remove configuration from the cache in case of
681 // invalid resource not exists to prevent DOS attack.
682 HttpException httpEx = e as HttpException;
683 if (httpEx != null && httpEx.GetHttpCode () == 404) {
684 removeConfigurationFromCache = true;
690 // Ticks the clock: next step on the pipeline.
692 internal void Tick ()
696 if (context.Error is UnifyRequestException) {
697 Exception ex = context.Error.InnerException;
698 context.ClearError ();
699 vmw.common.TypeUtils.Throw (ex);
703 if (pipeline.MoveNext ()){
704 if ((bool)pipeline.Current)
709 catch (Exception ex) {
710 if (ex is ThreadAbortException &&
711 ((ThreadAbortException) ex).ExceptionState == FlagEnd.Value)
713 if (context.WorkerRequest is IHttpUnifyWorkerRequest) {
714 context.ClearError ();
715 context.AddError (new UnifyRequestException (ex));
722 } catch (ThreadAbortException taex) {
723 object obj = taex.ExceptionState;
724 Thread.ResetAbort ();
725 stop_processing = true;
726 if (obj is StepTimeout)
727 ProcessError (new HttpException ("The request timed out."));
729 context.ClearError ();
730 if (FlagEnd.Value != obj)
731 context.AddError (taex);
735 } catch (Exception e) {
736 stop_processing = true;
751 // Invoked when our async callback called from RunHooks completes,
752 // we restart the pipeline here.
754 void async_callback_completed_cb (IAsyncResult ar)
756 if (current_ai.end != null){
759 } catch (Exception e) {
767 void async_handler_complete_cb (IAsyncResult ar)
769 IHttpAsyncHandler async_handler = ((IHttpAsyncHandler) ar.AsyncState);
772 async_handler.EndProcessRequest (ar);
773 } catch (Exception e){
781 // This enumerator yields whether processing must be stopped:
782 // true: processing of the pipeline must be stopped
783 // false: processing of the pipeline must not be stopped
785 IEnumerable RunHooks (Delegate list)
787 Delegate [] delegates = list.GetInvocationList ();
789 foreach (EventHandler d in delegates){
790 if (d.Target != null && (d.Target is AsyncInvoker)){
791 current_ai = (AsyncInvoker) d.Target;
796 context.BeginTimeoutPossible ();
797 current_ai.begin (this, EventArgs.Empty, async_callback_completed_cb, current_ai.data);
800 context.EndTimeoutPossible ();
804 // If things are still moving forward, yield this
808 yield return stop_processing;
809 else if (stop_processing)
813 context.BeginTimeoutPossible ();
814 d (this, EventArgs.Empty);
816 context.EndTimeoutPossible ();
824 static void FinalErrorWrite (HttpResponse response, string error)
827 response.Write (error);
828 response.Flush (true);
836 if (context.Error == null){
838 context.Response.Flush (true);
839 } catch (Exception e){
840 context.AddError (e);
844 Exception error = context.Error;
846 HttpResponse response = context.Response;
848 if (!response.HeadersSent){
849 response.ClearHeaders ();
850 response.ClearContent ();
852 if (error is HttpException){
853 response.StatusCode = ((HttpException)error).GetHttpCode ();
855 error = new HttpException ("", error);
856 response.StatusCode = 500;
858 HttpException httpEx = (HttpException) error;
859 if (!RedirectCustomError (ref httpEx))
860 FinalErrorWrite (response, httpEx.GetHtmlErrorMessage ());
862 response.Flush (true);
864 if (!(error is HttpException))
865 error = new HttpException ("", error);
866 FinalErrorWrite (response, ((HttpException) error).GetHtmlErrorMessage ());
873 // Invoked at the end of the pipeline execution
878 if (EndRequest != null)
879 EndRequest (this, EventArgs.Empty);
880 } catch (Exception e){
886 } catch (Exception e) {
887 Console.WriteLine ("Internal error: OutputPage threw an exception " + e);
889 context.WorkerRequest.EndOfRequest();
890 if (factory != null && context.Handler != null){
891 factory.ReleaseHandler (context.Handler);
892 context.Handler = null;
896 context.PopHandler ();
898 // context = null; -> moved to PostDone
904 if (begin_iar != null)
905 begin_iar.Complete ();
911 // Events fired as described in `Http Runtime Support, HttpModules,
912 // Handling Public Events'
914 IEnumerator Pipeline ()
919 if (BeginRequest != null)
920 foreach (bool stop in RunHooks (BeginRequest))
923 if (AuthenticateRequest != null)
924 foreach (bool stop in RunHooks (AuthenticateRequest))
927 if (DefaultAuthentication != null)
928 foreach (bool stop in RunHooks (DefaultAuthentication))
932 if (PostAuthenticateRequest != null)
933 foreach (bool stop in RunHooks (PostAuthenticateRequest))
936 if (AuthorizeRequest != null)
937 foreach (bool stop in RunHooks (AuthorizeRequest))
940 if (PostAuthorizeRequest != null)
941 foreach (bool stop in RunHooks (PostAuthorizeRequest))
945 if (ResolveRequestCache != null)
946 foreach (bool stop in RunHooks (ResolveRequestCache))
950 if (PostResolveRequestCache != null)
951 foreach (bool stop in RunHooks (PostResolveRequestCache))
954 // As per http://msdn2.microsoft.com/en-us/library/bb470252(VS.90).aspx
955 if (MapRequestHandler != null)
956 foreach (bool stop in RunHooks (MapRequestHandler))
960 // Obtain the handler for the request.
961 IHttpHandler handler = null;
963 handler = GetHandler (context, context.Request.FilePath);
965 context.PushHandler (handler);
967 } catch (FileNotFoundException fnf){
969 Console.WriteLine ("$$$$$$$$$$:Sys.Web Pipeline");
970 Console.WriteLine (fnf.ToString ());
972 if (context.Request.IsLocal)
973 ProcessError (new HttpException (404, String.Format ("File not found {0}", fnf.FileName), fnf, context.Request.FilePath));
975 ProcessError (new HttpException (404, "File not found: " + Path.GetFileName (fnf.FileName), context.Request.FilePath));
976 } catch (DirectoryNotFoundException dnf){
977 if (!context.Request.IsLocal)
978 dnf = null; // Do not "leak" real path information
979 ProcessError (new HttpException (404, "Directory not found", dnf));
980 } catch (Exception e) {
988 if (PostMapRequestHandler != null)
989 foreach (bool stop in RunHooks (PostMapRequestHandler))
993 if (AcquireRequestState != null){
994 foreach (bool stop in RunHooks (AcquireRequestState))
999 if (PostAcquireRequestState != null){
1000 foreach (bool stop in RunHooks (PostAcquireRequestState))
1006 // From this point on, we need to ensure that we call
1007 // ReleaseRequestState, so the code below jumps to
1008 // `release:' to guarantee it rather than yielding.
1010 if (PreRequestHandlerExecute != null)
1011 foreach (bool stop in RunHooks (PreRequestHandlerExecute))
1018 bool doProcessHandler = false;
1022 IHttpHandler ctxHandler = context.Handler;
1023 if (ctxHandler != null && handler != ctxHandler) {
1024 context.PopHandler ();
1025 handler = ctxHandler;
1026 context.PushHandler (handler);
1031 context.BeginTimeoutPossible ();
1032 if (handler != null){
1033 IHttpAsyncHandler async_handler = handler as IHttpAsyncHandler;
1035 if (async_handler != null){
1038 async_handler.BeginProcessRequest (context, async_handler_complete_cb, handler);
1041 handler.ProcessRequest (context);
1043 IHttpExtendedHandler extHandler=handler as IHttpExtendedHandler;
1044 doProcessHandler = extHandler != null && !extHandler.IsCompleted;
1048 if (context.Error != null)
1049 throw new TargetInvocationException(context.Error);
1052 context.EndTimeoutPossible ();
1055 if (doProcessHandler) {
1057 goto processHandler;
1061 yield return stop_processing;
1062 else if (stop_processing)
1065 // These are executed after the application has returned
1067 if (PostRequestHandlerExecute != null)
1068 foreach (bool stop in RunHooks (PostRequestHandlerExecute))
1073 if (ReleaseRequestState != null){
1074 #pragma warning disable 168
1075 foreach (bool stop in RunHooks (ReleaseRequestState)){
1077 // Ignore the stop signal while release the state
1081 #pragma warning restore 168
1084 if (stop_processing)
1088 if (PostReleaseRequestState != null)
1089 foreach (bool stop in RunHooks (PostReleaseRequestState))
1093 if (context.Error == null)
1094 context.Response.DoFilter (true);
1096 if (UpdateRequestCache != null)
1097 foreach (bool stop in RunHooks (UpdateRequestCache))
1101 if (PostUpdateRequestCache != null)
1102 foreach (bool stop in RunHooks (PostUpdateRequestCache))
1105 if (LogRequest != null)
1106 foreach (bool stop in RunHooks (LogRequest))
1109 if (PostLogRequest != null)
1110 foreach (bool stop in RunHooks (PostLogRequest))
1117 internal CultureInfo GetThreadCulture (HttpRequest request, CultureInfo culture, bool isAuto)
1122 CultureInfo ret = null;
1123 string[] languages = request.UserLanguages;
1125 if (languages != null && languages.Length > 0)
1126 ret = CultureInfo.CreateSpecificCulture (languages[0]);
1143 GlobalizationSection cfg;
1144 cfg = (GlobalizationSection) WebConfigurationManager.GetSection ("system.web/globalization");
1145 app_culture = cfg.GetCulture ();
1146 autoCulture = cfg.IsAutoCulture;
1147 appui_culture = cfg.GetUICulture ();
1148 autoUICulture = cfg.IsAutoUICulture;
1150 GlobalizationConfiguration cfg;
1151 cfg = GlobalizationConfiguration.GetInstance (null);
1153 app_culture = cfg.Culture;
1154 appui_culture = cfg.UICulture;
1159 context.StartTimeoutTimer ();
1161 Thread th = Thread.CurrentThread;
1162 if (app_culture != null) {
1163 prev_app_culture = th.CurrentCulture;
1164 CultureInfo new_app_culture = GetThreadCulture (Request, app_culture, autoCulture);
1165 if (!new_app_culture.Equals (CultureInfo.InvariantCulture))
1166 th.CurrentCulture = new_app_culture;
1169 if (appui_culture != null) {
1170 prev_appui_culture = th.CurrentUICulture;
1171 CultureInfo new_app_culture = GetThreadCulture (Request, appui_culture, autoUICulture);
1172 if (!new_app_culture.Equals (CultureInfo.InvariantCulture))
1173 th.CurrentUICulture = new_app_culture;
1177 prev_user = Thread.CurrentPrincipal;
1184 if (removeConfigurationFromCache) {
1185 WebConfigurationManager.RemoveConfigurationFromCache (context);
1186 removeConfigurationFromCache = false;
1189 Thread th = Thread.CurrentThread;
1191 if (Thread.CurrentPrincipal != prev_user)
1192 Thread.CurrentPrincipal = prev_user;
1194 if (prev_appui_culture != null && prev_appui_culture != th.CurrentUICulture)
1195 th.CurrentUICulture = prev_appui_culture;
1196 if (prev_app_culture != null && prev_app_culture != th.CurrentCulture)
1197 th.CurrentCulture = prev_app_culture;
1200 if (context == null)
1201 context = HttpContext.Current;
1202 context.StopTimeoutTimer ();
1206 HttpContext.Current = null;
1209 void Start (object x)
1213 } catch (Exception e) {
1215 initialization_exception = e;
1217 FinalErrorWrite (context.Response, new HttpException ("", e).GetHtmlErrorMessage ());
1222 HttpContext.Current = Context;
1224 pipeline = Pipeline ();
1228 const string HANDLER_CACHE = "@@HttpHandlerCache@@";
1230 internal static Hashtable GetHandlerCache ()
1232 Cache cache = HttpRuntime.InternalCache;
1233 Hashtable ret = cache [HANDLER_CACHE] as Hashtable;
1236 ret = new Hashtable ();
1237 cache.Insert (HANDLER_CACHE, ret);
1243 internal static void ClearHandlerCache ()
1245 Hashtable cache = GetHandlerCache ();
1249 internal object LocateHandler (string verb, string url)
1251 Hashtable cache = GetHandlerCache ();
1252 string id = String.Concat (verb, url);
1253 object ret = cache [id];
1259 HttpHandlersSection httpHandlersSection = (HttpHandlersSection) WebConfigurationManager.GetSection ("system.web/httpHandlers");
1260 ret = httpHandlersSection.LocateHandler (verb, url);
1262 HandlerFactoryConfiguration factory_config = (HandlerFactoryConfiguration) HttpContext.GetAppConfig ("system.web/httpHandlers");
1263 ret = factory_config.LocateHandler (verb, url);
1270 // Used by HttpServerUtility.Execute
1271 internal IHttpHandler GetHandler (HttpContext context, string url)
1273 if (context.Handler != null)
1274 return context.Handler;
1276 HttpRequest request = context.Request;
1277 string verb = request.RequestType;
1279 IHttpHandler handler = null;
1280 object o = LocateHandler (verb, url);
1282 factory = o as IHttpHandlerFactory;
1284 if (factory == null) {
1285 handler = (IHttpHandler) o;
1287 handler = factory.GetHandler (context, verb, url, request.MapPath (url));
1293 void IHttpHandler.ProcessRequest (HttpContext context)
1296 this.context = context;
1304 // This is used by FireOnAppStart, when we init the application
1305 // as the context is required to be set at that point (the user
1306 // might call methods that require it on that hook).
1308 internal void SetContext (HttpContext context)
1310 this.context = context;
1313 internal void SetSession (HttpSessionState session)
1315 this.session = session;
1318 IAsyncResult IHttpAsyncHandler.BeginProcessRequest (HttpContext context, AsyncCallback cb, object extraData)
1320 this.context = context;
1323 begin_iar = new AsyncRequestState (done, cb, extraData);
1328 if (Thread.CurrentThread.IsThreadPoolThread)
1332 ThreadPool.QueueUserWorkItem (new WaitCallback (Start), null);
1337 void IHttpAsyncHandler.EndProcessRequest (IAsyncResult result)
1343 if (!result.IsCompleted)
1344 result.AsyncWaitHandle.WaitOne ();
1348 public virtual void Init ()
1352 bool IHttpHandler.IsReusable {
1359 internal void ClearError ()
1361 context.ClearError ();
1364 bool RedirectErrorPage (string error_page)
1366 if (context.Request.QueryString ["aspxerrorpath"] != null)
1369 Response.Redirect (error_page + "?aspxerrorpath=" + Request.Path, false);
1373 bool RedirectCustomError (ref HttpException httpEx)
1376 if (!context.IsCustomErrorEnabledUnsafe)
1380 CustomErrorsSection config = (CustomErrorsSection)WebConfigurationManager.GetSection ("system.web/customErrors");
1382 CustomErrorsConfig config = null;
1384 config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
1388 if (config == null) {
1389 if (context.ErrorPage != null)
1390 return RedirectErrorPage (context.ErrorPage);
1396 CustomError err = config.Errors [context.Response.StatusCode.ToString()];
1397 string redirect = err == null ? null : err.Redirect;
1399 string redirect = config [context.Response.StatusCode];
1401 if (redirect == null) {
1402 redirect = context.ErrorPage;
1403 if (redirect == null)
1404 redirect = config.DefaultRedirect;
1407 if (redirect == null)
1410 return RedirectErrorPage (redirect);
1412 catch (Exception ex) {
1413 httpEx = new HttpException (500, "", ex);
1418 internal static string BinDirectory
1421 if (binDirectory == null) {
1422 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
1423 string baseDir = setup.ApplicationBase;
1426 foreach (string dir in BinDirs) {
1427 bindir = Path.Combine (baseDir, dir);
1428 if (!Directory.Exists (bindir))
1430 binDirectory = bindir;
1435 return binDirectory;
1439 internal static string[] BinDirectoryAssemblies
1442 ArrayList binDlls = null;
1445 string bindir = BinDirectory;
1446 if (bindir != null) {
1447 binDlls = new ArrayList ();
1448 dlls = Directory.GetFiles (bindir, "*.dll");
1449 binDlls.AddRange (dlls);
1452 if (binDlls == null)
1453 return new string[] {};
1455 return (string[]) binDlls.ToArray (typeof (string));
1459 internal static Type LoadType (string typeName)
1461 return LoadType (typeName, false);
1464 internal static Type LoadType (string typeName, bool throwOnMissing)
1466 Type type = Type.GetType (typeName);
1471 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
1472 foreach (Assembly ass in assemblies) {
1473 type = ass.GetType (typeName, false);
1479 IList tla = System.Web.Compilation.BuildManager.TopLevelAssemblies;
1480 if (tla != null && tla.Count > 0) {
1481 foreach (Assembly asm in tla) {
1484 type = asm.GetType (typeName, false);
1491 type = LoadTypeFromBin (typeName);
1496 throw new TypeLoadException (String.Format ("Type '{0}' cannot be found", typeName));
1501 internal static Type LoadTypeFromBin (string typeName)
1505 foreach (string s in BinDirectoryAssemblies) {
1506 Assembly binA = Assembly.LoadFrom (s);
1507 type = binA.GetType (typeName, false);
1519 // Based on Fritz' Onion's AsyncRequestState class for asynchronous IHttpAsyncHandlers
1521 class AsyncRequestState : IAsyncResult {
1525 ManualResetEvent complete_event = null;
1527 internal AsyncRequestState (ManualResetEvent complete_event, AsyncCallback cb, object cb_data)
1530 this.cb_data = cb_data;
1531 this.complete_event = complete_event;
1534 internal void Complete ()
1539 // TODO: if this throws an error, we have no way of reporting it
1540 // Not really too bad, since the only failure might be
1541 // `HttpRuntime.request_processed'.
1548 complete_event.Set ();
1551 public object AsyncState {
1557 public bool CompletedSynchronously {
1563 public bool IsCompleted {
1569 public WaitHandle AsyncWaitHandle {
1571 return complete_event;
1576 #region Helper classes
1579 // A wrapper to keep track of begin/end pairs
1581 class AsyncInvoker {
1582 public BeginEventHandler begin;
1583 public EndEventHandler end;
1586 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, object d)
1593 public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh)
1599 public void Invoke (object sender, EventArgs e)
1601 throw new Exception ("This is just a dummy");