// // System.Web.HttpApplication.cs // // Author: // Miguel de Icaza (miguel@novell.com) // Gonzalo Paniagua (gonzalo@ximian.com) // Matthias Bogad (bogad@cs.tum.edu) // // // Copyright (C) 2005-2009 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // The Application Processing Pipeline. // // The Http application pipeline implemented in this file is a // beautiful thing. The application pipeline invokes a number of // hooks at various stages of the processing of a request. These // hooks can be either synchronous or can be asynchronous. // // The pipeline must ensure that every step is completed before // moving to the next step. A trivial thing for synchronous // hooks, but asynchronous hooks introduce an extra layer of // complexity: when the hook is invoked, the thread must // relinquish its control so that the thread can be reused in // another operation while waiting. // // To implement this functionality we used C# iterators manually; // we drive the pipeline by executing the various hooks from the // `RunHooks' routine which is an enumerator that will yield the // value `false' if execution must proceed or `true' if execution // must be stopped. // // By yielding values we can suspend execution of RunHooks. // // Special attention must be given to `in_begin' and `must_yield' // variables. These are used in the case that an async hook // completes synchronously as its important to not yield in that // case or we would hang. // // Many of Mono modules used to be declared async, but they would // actually be completely synchronous, this might resurface in the // future with other modules. // // TODO: // Events Disposed // using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Security.Permissions; using System.Security.Principal; using System.Threading; using System.Web.Caching; using System.Web.Compilation; using System.Web.Configuration; using System.Web.Management; using System.Web.SessionState; using System.Web.UI; using System.Web.Util; namespace System.Web { // CAS [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] // attributes [ToolboxItem(false)] public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable { static readonly object disposedEvent = new object (); static readonly object errorEvent = new object (); // we do this static rather than per HttpApplication because // mono's perfcounters use the counter instance parameter for // the process to access shared memory. internal static PerformanceCounter requests_total_counter = new PerformanceCounter ("ASP.NET", "Requests Total"); internal static readonly string [] BinDirs = {"Bin", "bin"}; object this_lock = new object(); HttpContext context; HttpSessionState session; ISite isite; // The source, and the exposed API (cache). volatile HttpModuleCollection modcoll; string assemblyLocation; // // The factory for the handler currently running. // IHttpHandlerFactory factory; // // Whether the thread culture is to be auto-set. // Used only in the 2.0 profile, always false for 1.x // bool autoCulture; bool autoUICulture; // // Whether the pipeline should be stopped // bool stop_processing; // // See https://bugzilla.novell.com/show_bug.cgi?id=381971 // bool in_application_start; // // The Pipeline // IEnumerator pipeline; // To flag when we are done processing a request from BeginProcessRequest. ManualResetEvent done; // The current IAsyncResult for the running async request handler in the pipeline AsyncRequestState begin_iar; // Tracks the current AsyncInvocation being dispatched AsyncInvoker current_ai; EventHandlerList events; EventHandlerList nonApplicationEvents = new EventHandlerList (); // Culture and IPrincipal CultureInfo app_culture; CultureInfo appui_culture; CultureInfo prev_app_culture; CultureInfo prev_appui_culture; IPrincipal prev_user; static string binDirectory; static volatile Exception initialization_exception; bool removeConfigurationFromCache; bool fullInitComplete = false; static DynamicModuleManager dynamicModuleManeger = new DynamicModuleManager (); // // These are used to detect the case where the EndXXX method is invoked // from within the BeginXXXX delegate, so we detect whether we kick the // pipeline from here, or from the the RunHook routine // bool must_yield; bool in_begin; public virtual event EventHandler Disposed { add { nonApplicationEvents.AddHandler (disposedEvent, value); } remove { nonApplicationEvents.RemoveHandler (disposedEvent, value); } } public virtual event EventHandler Error { add { nonApplicationEvents.AddHandler (errorEvent, value); } remove { nonApplicationEvents.RemoveHandler (errorEvent, value); } } public HttpApplication () { done = new ManualResetEvent (false); } internal void InitOnce (bool full_init) { if (initialization_exception != null) return; if (modcoll != null) return; lock (this_lock) { if (initialization_exception != null) return; if (modcoll != null) return; bool mustNullContext = context == null; try { HttpModulesSection modules; modules = (HttpModulesSection) WebConfigurationManager.GetWebApplicationSection ("system.web/httpModules"); HttpContext saved = HttpContext.Current; HttpContext.Current = new HttpContext (new System.Web.Hosting.SimpleWorkerRequest (String.Empty, String.Empty, new StringWriter())); if (context == null) context = HttpContext.Current; HttpModuleCollection coll = modules.LoadModules (this); HttpModuleCollection dynMods = CreateDynamicModules (); for (int i = 0; i < dynMods.Count; i++) { coll.AddModule (dynMods.GetKey (i), dynMods.Get (i)); } Interlocked.CompareExchange (ref modcoll, coll, null); HttpContext.Current = saved; if (full_init) { HttpApplicationFactory.AttachEvents (this); Init (); fullInitComplete = true; } } catch (Exception e) { initialization_exception = e; Console.Error.WriteLine("Exception while initOnce: "+e.ToString()); // Once initialization_exception != null, we always respond with this exception // You have to restart the HttpApplication to "unlock" it Console.Error.WriteLine("Please restart your app to unlock it"); } finally { if (mustNullContext) context = null; } } } internal bool InApplicationStart { get { return in_application_start; } set { in_application_start = value; } } internal string AssemblyLocation { get { if (assemblyLocation == null) assemblyLocation = GetType ().Assembly.Location; return assemblyLocation; } } internal static Exception InitializationException { get { return initialization_exception; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public HttpApplicationState Application { get { return HttpApplicationFactory.ApplicationState; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public HttpContext Context { get { return context; } } protected EventHandlerList Events { get { if (events == null) events = new EventHandlerList (); return events; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public HttpModuleCollection Modules { [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)] get { if (modcoll == null) modcoll = new HttpModuleCollection (); return modcoll; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public HttpRequest Request { get { if (context == null) throw HttpException.NewWithCode (Locale.GetText ("No context is available."), WebEventCodes.RuntimeErrorRequestAbort); if (false == HttpApplicationFactory.ContextAvailable) throw HttpException.NewWithCode (Locale.GetText ("Request is not available in this context."), WebEventCodes.RuntimeErrorRequestAbort); return context.Request; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public HttpResponse Response { get { if (context == null) throw HttpException.NewWithCode (Locale.GetText ("No context is available."), WebEventCodes.RuntimeErrorRequestAbort); if (false == HttpApplicationFactory.ContextAvailable) throw HttpException.NewWithCode (Locale.GetText ("Response is not available in this context."), WebEventCodes.RuntimeErrorRequestAbort); return context.Response; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public HttpServerUtility Server { get { if (context != null) return context.Server; // // This is so we can get the Server and call a few methods // which are not context sensitive, see HttpServerUtilityTest // return new HttpServerUtility ((HttpContext) null); } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public HttpSessionState Session { get { // Only used for Session_End if (session != null) return session; if (context == null) throw HttpException.NewWithCode (Locale.GetText ("No context is available."), WebEventCodes.RuntimeErrorRequestAbort); HttpSessionState ret = context.Session; if (ret == null) throw HttpException.NewWithCode (Locale.GetText ("Session state is not available in the context."), WebEventCodes.RuntimeErrorRequestAbort); return ret; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public ISite Site { get { return isite; } set { isite = value; } } [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public IPrincipal User { get { if (context == null) throw new HttpException (Locale.GetText ("No context is available.")); if (context.User == null) throw new HttpException (Locale.GetText ("No currently authenticated user.")); return context.User; } } static object PreSendRequestHeadersEvent = new object (); public event EventHandler PreSendRequestHeaders { add { AddEventHandler (PreSendRequestHeadersEvent, value); } remove { RemoveEventHandler (PreSendRequestHeadersEvent, value); } } internal void TriggerPreSendRequestHeaders () { EventHandler handler = Events [PreSendRequestHeadersEvent] as EventHandler; if (handler != null) handler (this, EventArgs.Empty); } static object PreSendRequestContentEvent = new object (); public event EventHandler PreSendRequestContent { add { AddEventHandler (PreSendRequestContentEvent, value); } remove { RemoveEventHandler (PreSendRequestContentEvent, value); } } internal void TriggerPreSendRequestContent () { EventHandler handler = Events [PreSendRequestContentEvent] as EventHandler; if (handler != null) handler (this, EventArgs.Empty); } static object AcquireRequestStateEvent = new object (); public event EventHandler AcquireRequestState { add { AddEventHandler (AcquireRequestStateEvent, value); } remove { RemoveEventHandler (AcquireRequestStateEvent, value); } } public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); AcquireRequestState += new EventHandler (invoker.Invoke); } static object AuthenticateRequestEvent = new object (); public event EventHandler AuthenticateRequest { add { AddEventHandler (AuthenticateRequestEvent, value); } remove { RemoveEventHandler (AuthenticateRequestEvent, value); } } public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); AuthenticateRequest += new EventHandler (invoker.Invoke); } static object AuthorizeRequestEvent = new object (); public event EventHandler AuthorizeRequest { add { AddEventHandler (AuthorizeRequestEvent, value); } remove { RemoveEventHandler (AuthorizeRequestEvent, value); } } public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); AuthorizeRequest += new EventHandler (invoker.Invoke); } static object BeginRequestEvent = new object (); public event EventHandler BeginRequest { add { // See https://bugzilla.novell.com/show_bug.cgi?id=381971 if (InApplicationStart) return; AddEventHandler (BeginRequestEvent, value); } remove { if (InApplicationStart) return; RemoveEventHandler (BeginRequestEvent, value); } } public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); BeginRequest += new EventHandler (invoker.Invoke); } static object EndRequestEvent = new object (); public event EventHandler EndRequest { add { // See https://bugzilla.novell.com/show_bug.cgi?id=381971 if (InApplicationStart) return; AddEventHandler (EndRequestEvent, value); } remove { if (InApplicationStart) return; RemoveEventHandler (EndRequestEvent, value); } } public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); EndRequest += new EventHandler (invoker.Invoke); } static object PostRequestHandlerExecuteEvent = new object (); public event EventHandler PostRequestHandlerExecute { add { AddEventHandler (PostRequestHandlerExecuteEvent, value); } remove { RemoveEventHandler (PostRequestHandlerExecuteEvent, value); } } public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); PostRequestHandlerExecute += new EventHandler (invoker.Invoke); } static object PreRequestHandlerExecuteEvent = new object (); public event EventHandler PreRequestHandlerExecute { add { AddEventHandler (PreRequestHandlerExecuteEvent, value); } remove { RemoveEventHandler (PreRequestHandlerExecuteEvent, value); } } public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); PreRequestHandlerExecute += new EventHandler (invoker.Invoke); } static object ReleaseRequestStateEvent = new object (); public event EventHandler ReleaseRequestState { add { AddEventHandler (ReleaseRequestStateEvent, value); } remove { RemoveEventHandler (ReleaseRequestStateEvent, value); } } public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); ReleaseRequestState += new EventHandler (invoker.Invoke); } static object ResolveRequestCacheEvent = new object (); public event EventHandler ResolveRequestCache { add { AddEventHandler (ResolveRequestCacheEvent, value); } remove { RemoveEventHandler (ResolveRequestCacheEvent, value); } } public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); ResolveRequestCache += new EventHandler (invoker.Invoke); } static object UpdateRequestCacheEvent = new object (); public event EventHandler UpdateRequestCache { add { AddEventHandler (UpdateRequestCacheEvent, value); } remove { RemoveEventHandler (UpdateRequestCacheEvent, value); } } public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh) { AsyncInvoker invoker = new AsyncInvoker (bh, eh, this); UpdateRequestCache += new EventHandler (invoker.Invoke); } static object PostAuthenticateRequestEvent = new object (); public event EventHandler PostAuthenticateRequest { add { AddEventHandler (PostAuthenticateRequestEvent, value); } remove { RemoveEventHandler (PostAuthenticateRequestEvent, value); } } public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostAuthenticateRequestAsync (bh, eh, null); } public void AddOnPostAuthenticateRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostAuthenticateRequest += new EventHandler (invoker.Invoke); } static object PostAuthorizeRequestEvent = new object (); public event EventHandler PostAuthorizeRequest { add { AddEventHandler (PostAuthorizeRequestEvent, value); } remove { RemoveEventHandler (PostAuthorizeRequestEvent, value); } } public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostAuthorizeRequestAsync (bh, eh, null); } public void AddOnPostAuthorizeRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostAuthorizeRequest += new EventHandler (invoker.Invoke); } static object PostResolveRequestCacheEvent = new object (); public event EventHandler PostResolveRequestCache { add { AddEventHandler (PostResolveRequestCacheEvent, value); } remove { RemoveEventHandler (PostResolveRequestCacheEvent, value); } } public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostResolveRequestCacheAsync (bh, eh, null); } public void AddOnPostResolveRequestCacheAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostResolveRequestCache += new EventHandler (invoker.Invoke); } static object PostMapRequestHandlerEvent = new object (); public event EventHandler PostMapRequestHandler { add { AddEventHandler (PostMapRequestHandlerEvent, value); } remove { RemoveEventHandler (PostMapRequestHandlerEvent, value); } } public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostMapRequestHandlerAsync (bh, eh, null); } public void AddOnPostMapRequestHandlerAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostMapRequestHandler += new EventHandler (invoker.Invoke); } static object PostAcquireRequestStateEvent = new object (); public event EventHandler PostAcquireRequestState { add { AddEventHandler (PostAcquireRequestStateEvent, value); } remove { RemoveEventHandler (PostAcquireRequestStateEvent, value); } } public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostAcquireRequestStateAsync (bh, eh, null); } public void AddOnPostAcquireRequestStateAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostAcquireRequestState += new EventHandler (invoker.Invoke); } static object PostReleaseRequestStateEvent = new object (); public event EventHandler PostReleaseRequestState { add { AddEventHandler (PostReleaseRequestStateEvent, value); } remove { RemoveEventHandler (PostReleaseRequestStateEvent, value); } } public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostReleaseRequestStateAsync (bh, eh, null); } public void AddOnPostReleaseRequestStateAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostReleaseRequestState += new EventHandler (invoker.Invoke); } static object PostUpdateRequestCacheEvent = new object (); public event EventHandler PostUpdateRequestCache { add { AddEventHandler (PostUpdateRequestCacheEvent, value); } remove { RemoveEventHandler (PostUpdateRequestCacheEvent, value); } } public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostUpdateRequestCacheAsync (bh, eh, null); } public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostUpdateRequestCache += new EventHandler (invoker.Invoke); } // // The new overloads that take a data parameter // public void AddOnAcquireRequestStateAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); AcquireRequestState += new EventHandler (invoker.Invoke); } public void AddOnAuthenticateRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); AuthenticateRequest += new EventHandler (invoker.Invoke); } public void AddOnAuthorizeRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); AuthorizeRequest += new EventHandler (invoker.Invoke); } public void AddOnBeginRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); BeginRequest += new EventHandler (invoker.Invoke); } public void AddOnEndRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); EndRequest += new EventHandler (invoker.Invoke); } public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostRequestHandlerExecute += new EventHandler (invoker.Invoke); } public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PreRequestHandlerExecute += new EventHandler (invoker.Invoke); } public void AddOnReleaseRequestStateAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); ReleaseRequestState += new EventHandler (invoker.Invoke); } public void AddOnResolveRequestCacheAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); ResolveRequestCache += new EventHandler (invoker.Invoke); } public void AddOnUpdateRequestCacheAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); UpdateRequestCache += new EventHandler (invoker.Invoke); } // Added in 2.0 SP1 // They are for use with the IIS7 integrated mode, but have been added for // compatibility static object LogRequestEvent = new object (); public event EventHandler LogRequest { add { AddEventHandler (LogRequestEvent, value); } remove { RemoveEventHandler (LogRequestEvent, value); } } public void AddOnLogRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnLogRequestAsync (bh, eh, null); } public void AddOnLogRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); LogRequest += new EventHandler (invoker.Invoke); } static object MapRequestHandlerEvent = new object (); public event EventHandler MapRequestHandler { add { AddEventHandler (MapRequestHandlerEvent, value); } remove { RemoveEventHandler (MapRequestHandlerEvent, value); } } public void AddOnMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnMapRequestHandlerAsync (bh, eh, null); } public void AddOnMapRequestHandlerAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); MapRequestHandler += new EventHandler (invoker.Invoke); } static object PostLogRequestEvent = new object (); public event EventHandler PostLogRequest { add { AddEventHandler (PostLogRequestEvent, value); } remove { RemoveEventHandler (PostLogRequestEvent, value); } } public void AddOnPostLogRequestAsync (BeginEventHandler bh, EndEventHandler eh) { AddOnPostLogRequestAsync (bh, eh, null); } public void AddOnPostLogRequestAsync (BeginEventHandler beginHandler, EndEventHandler endHandler, object state) { AsyncInvoker invoker = new AsyncInvoker (beginHandler, endHandler, this, state); PostLogRequest += new EventHandler (invoker.Invoke); } internal event EventHandler DefaultAuthentication; void AddEventHandler (object key, EventHandler handler) { if (fullInitComplete) return; Events.AddHandler (key, handler); } void RemoveEventHandler (object key, EventHandler handler) { if (fullInitComplete) return; Events.RemoveHandler (key, handler); } // // Bypass all the event on the Http pipeline and go directly to EndRequest // public void CompleteRequest () { stop_processing = true; } internal bool RequestCompleted { set { stop_processing = value; } } internal void DisposeInternal () { Dispose (); HttpModuleCollection coll = new HttpModuleCollection (); Interlocked.Exchange (ref modcoll, coll); if (coll != null) { for (int i = coll.Count - 1; i >= 0; i--) { coll.Get (i).Dispose (); } coll = null; } EventHandler eh = nonApplicationEvents [disposedEvent] as EventHandler; if (eh != null) eh (this, EventArgs.Empty); done.Close (); done = null; } public virtual void Dispose () { } public virtual string GetOutputCacheProviderName (HttpContext context) { // LAMESPEC: doesn't throw ProviderException if context is null return OutputCache.DefaultProviderName; } public virtual string GetVaryByCustomString (HttpContext context, string custom) { if (custom == null) // Sigh throw new NullReferenceException (); if (0 == String.Compare (custom, "browser", true, Helpers.InvariantCulture)) return context.Request.Browser.Type; return null; } bool ShouldHandleException (Exception e) { if (e is ParseException) return false; return true; } // // If we catch an error, queue this error // internal void ProcessError (Exception e) { bool first = context.Error == null; context.AddError (e); if (first && ShouldHandleException (e)) { EventHandler eh = nonApplicationEvents [errorEvent] as EventHandler; if (eh != null){ try { eh (this, EventArgs.Empty); if (stop_processing) context.ClearError (); } catch (ThreadAbortException taex){ context.ClearError (); if (FlagEnd.Value == taex.ExceptionState || HttpRuntime.DomainUnloading) // This happens on Redirect(), End() and // when unloading the AppDomain Thread.ResetAbort (); else // This happens on Thread.Abort() context.AddError (taex); } catch (Exception ee){ context.AddError (ee); } } } stop_processing = true; // we want to remove configuration from the cache in case of // invalid resource not exists to prevent DOS attack. HttpException httpEx = e as HttpException; if (httpEx != null && httpEx.GetHttpCode () == 404) { removeConfigurationFromCache = true; } } // // Ticks the clock: next step on the pipeline. // internal void Tick () { try { if (pipeline.MoveNext ()){ if ((bool)pipeline.Current) PipelineDone (); } } catch (ThreadAbortException taex) { object obj = taex.ExceptionState; Thread.ResetAbort (); if (obj is StepTimeout) ProcessError (HttpException.NewWithCode ("The request timed out.", WebEventCodes.RequestTransactionAbort)); else { context.ClearError (); if (FlagEnd.Value != obj && !HttpRuntime.DomainUnloading) context.AddError (taex); } stop_processing = true; PipelineDone (); } catch (Exception e) { ThreadAbortException inner = e.InnerException as ThreadAbortException; if (inner != null && FlagEnd.Value == inner.ExceptionState && !HttpRuntime.DomainUnloading) { context.ClearError (); Thread.ResetAbort (); } else { ProcessError (e); } stop_processing = true; PipelineDone (); } } void Resume () { if (in_begin) must_yield = false; else Tick (); } // // Invoked when our async callback called from RunHooks completes, // we restart the pipeline here. // void async_callback_completed_cb (IAsyncResult ar) { if (current_ai.end != null){ try { current_ai.end (ar); } catch (Exception e) { ProcessError (e); } } Resume (); } void async_handler_complete_cb (IAsyncResult ar) { IHttpAsyncHandler async_handler = ar != null ? ar.AsyncState as IHttpAsyncHandler : null; try { if (async_handler != null) async_handler.EndProcessRequest (ar); } catch (Exception e){ ProcessError (e); } Resume (); } // // This enumerator yields whether processing must be stopped: // true: processing of the pipeline must be stopped // false: processing of the pipeline must not be stopped // IEnumerable RunHooks (Delegate list) { Delegate [] delegates = list.GetInvocationList (); foreach (EventHandler d in delegates){ if (d.Target != null && (d.Target is AsyncInvoker)){ current_ai = (AsyncInvoker) d.Target; try { must_yield = true; in_begin = true; context.BeginTimeoutPossible (); current_ai.begin (this, EventArgs.Empty, async_callback_completed_cb, current_ai.data); } finally { in_begin = false; context.EndTimeoutPossible (); } // // If things are still moving forward, yield this // thread now // if (must_yield) yield return stop_processing; else if (stop_processing) yield return true; } else { try { context.BeginTimeoutPossible (); d (this, EventArgs.Empty); } finally { context.EndTimeoutPossible (); } if (stop_processing) yield return true; } } } static void FinalErrorWrite (HttpResponse response, string error) { try { response.Write (error); response.Flush (true); } catch { response.Close (); } } void OutputPage () { if (context.Error == null){ try { context.Response.Flush (true); } catch (Exception e){ context.AddError (e); } } Exception error = context.Error; if (error != null){ HttpResponse response = context.Response; if (!response.HeadersSent){ response.ClearHeaders (); response.ClearContent (); if (error is HttpException){ response.StatusCode = ((HttpException)error).GetHttpCode (); } else { error = HttpException.NewWithCode (String.Empty, error, WebEventCodes.WebErrorOtherError); response.StatusCode = 500; } HttpException httpEx = (HttpException) error; if (!RedirectCustomError (ref httpEx)) FinalErrorWrite (response, httpEx.GetHtmlErrorMessage ()); else response.Flush (true); } else { if (!(error is HttpException)) error = HttpException.NewWithCode (String.Empty, error, WebEventCodes.WebErrorOtherError); FinalErrorWrite (response, ((HttpException) error).GetHtmlErrorMessage ()); } } } // // Invoked at the end of the pipeline execution // void PipelineDone () { try { EventHandler handler = Events [EndRequestEvent] as EventHandler; if (handler != null) handler (this, EventArgs.Empty); } catch (Exception e){ ProcessError (e); } try { OutputPage (); } catch (ThreadAbortException taex) { ProcessError (taex); Thread.ResetAbort (); } catch (Exception e) { Console.WriteLine ("Internal error: OutputPage threw an exception " + e); } finally { context.WorkerRequest.EndOfRequest(); if (factory != null && context.Handler != null){ factory.ReleaseHandler (context.Handler); context.Handler = null; factory = null; } context.PopHandler (); // context = null; -> moved to PostDone pipeline = null; current_ai = null; } PostDone (); if (begin_iar != null) begin_iar.Complete (); else done.Set (); requests_total_counter.Increment (); } class Tim { string name; DateTime start; public Tim () { } public Tim (string name) { this.name = name; } public string Name { get { return name; } set { name = value; } } public void Start () { start = DateTime.UtcNow; } public void Stop () { Console.WriteLine ("{0}: {1}ms", name, (DateTime.UtcNow - start).TotalMilliseconds); } } Tim tim; [Conditional ("PIPELINE_TIMER")] void StartTimer (string name) { if (tim == null) tim = new Tim (); tim.Name = name; tim.Start (); } [Conditional ("PIPELINE_TIMER")] void StopTimer () { tim.Stop (); } // // Events fired as described in `Http Runtime Support, HttpModules, // Handling Public Events' // IEnumerator Pipeline () { Delegate eventHandler; if (stop_processing) yield return true; HttpRequest req = context.Request; if (req != null) req.Validate (); context.MapRequestHandlerDone = false; StartTimer ("BeginRequest"); eventHandler = Events [BeginRequestEvent]; if (eventHandler != null) { foreach (bool stop in RunHooks (eventHandler)) yield return stop; } StopTimer (); StartTimer ("AuthenticateRequest"); eventHandler = Events [AuthenticateRequestEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("DefaultAuthentication"); if (DefaultAuthentication != null) foreach (bool stop in RunHooks (DefaultAuthentication)) yield return stop; StopTimer (); StartTimer ("PostAuthenticateRequest"); eventHandler = Events [PostAuthenticateRequestEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("AuthorizeRequest"); eventHandler = Events [AuthorizeRequestEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("PostAuthorizeRequest"); eventHandler = Events [PostAuthorizeRequestEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("ResolveRequestCache"); eventHandler = Events [ResolveRequestCacheEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("PostResolveRequestCache"); eventHandler = Events [PostResolveRequestCacheEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("MapRequestHandler"); // As per http://msdn2.microsoft.com/en-us/library/bb470252(VS.90).aspx eventHandler = Events [MapRequestHandlerEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); context.MapRequestHandlerDone = true; StartTimer ("GetHandler"); // Obtain the handler for the request. IHttpHandler handler = null; try { handler = GetHandler (context, context.Request.CurrentExecutionFilePath); context.Handler = handler; context.PushHandler (handler); } catch (FileNotFoundException fnf){ if (context.Request.IsLocal) ProcessError (HttpException.NewWithCode (404, String.Format ("File not found {0}", fnf.FileName), fnf, context.Request.FilePath, WebEventCodes.RuntimeErrorRequestAbort)); else ProcessError (HttpException.NewWithCode (404, "File not found: " + Path.GetFileName (fnf.FileName), context.Request.FilePath, WebEventCodes.RuntimeErrorRequestAbort)); } catch (DirectoryNotFoundException dnf){ if (!context.Request.IsLocal) dnf = null; // Do not "leak" real path information ProcessError (HttpException.NewWithCode (404, "Directory not found", dnf, WebEventCodes.RuntimeErrorRequestAbort)); } catch (Exception e) { ProcessError (e); } StopTimer (); if (stop_processing) yield return true; StartTimer ("PostMapRequestHandler"); eventHandler = Events [PostMapRequestHandlerEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("AcquireRequestState"); eventHandler = Events [AcquireRequestStateEvent]; if (eventHandler != null){ foreach (bool stop in RunHooks (eventHandler)) yield return stop; } StopTimer (); StartTimer ("PostAcquireRequestState"); eventHandler = Events [PostAcquireRequestStateEvent]; if (eventHandler != null){ foreach (bool stop in RunHooks (eventHandler)) yield return stop; } StopTimer (); // // From this point on, we need to ensure that we call // ReleaseRequestState, so the code below jumps to // `release:' to guarantee it rather than yielding. // StartTimer ("PreRequestHandlerExecute"); eventHandler = Events [PreRequestHandlerExecuteEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) if (stop) goto release; StopTimer (); IHttpHandler ctxHandler = context.Handler; if (ctxHandler != null && handler != ctxHandler) { context.PopHandler (); handler = ctxHandler; context.PushHandler (handler); } StartTimer ("ProcessRequest"); try { context.BeginTimeoutPossible (); if (handler != null){ IHttpAsyncHandler async_handler = handler as IHttpAsyncHandler; if (async_handler != null){ must_yield = true; in_begin = true; async_handler.BeginProcessRequest (context, async_handler_complete_cb, handler); } else { must_yield = false; handler.ProcessRequest (context); } } else throw new InvalidOperationException ("No handler for the current request."); if (context.Error != null) throw new TargetInvocationException(context.Error); } finally { in_begin = false; context.EndTimeoutPossible (); } StopTimer (); if (must_yield) yield return stop_processing; else if (stop_processing) goto release; // These are executed after the application has returned StartTimer ("PostRequestHandlerExecute"); eventHandler = Events [PostRequestHandlerExecuteEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) if (stop) goto release; StopTimer (); release: StartTimer ("ReleaseRequestState"); eventHandler = Events [ReleaseRequestStateEvent]; if (eventHandler != null){ #pragma warning disable 219 foreach (bool stop in RunHooks (eventHandler)) { // // Ignore the stop signal while release the state // } #pragma warning restore 219 } StopTimer (); if (stop_processing) yield return true; StartTimer ("PostReleaseRequestState"); eventHandler = Events [PostReleaseRequestStateEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("Filter"); if (context.Error == null) context.Response.DoFilter (true); StopTimer (); StartTimer ("UpdateRequestCache"); eventHandler = Events [UpdateRequestCacheEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("PostUpdateRequestCache"); eventHandler = Events [PostUpdateRequestCacheEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("LogRequest"); eventHandler = Events [LogRequestEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("PostLogRequest"); eventHandler = Events [PostLogRequestEvent]; if (eventHandler != null) foreach (bool stop in RunHooks (eventHandler)) yield return stop; StopTimer (); StartTimer ("PipelineDone"); PipelineDone (); StopTimer (); } internal CultureInfo GetThreadCulture (HttpRequest request, CultureInfo culture, bool isAuto) { if (!isAuto) return culture; CultureInfo ret = null; string[] languages = request.UserLanguages; try { if (languages != null && languages.Length > 0) ret = CultureInfo.CreateSpecificCulture (languages[0]); } catch { } if (ret == null) ret = culture; return ret; } void PreStart () { GlobalizationSection cfg; cfg = (GlobalizationSection) WebConfigurationManager.GetSection ("system.web/globalization"); app_culture = cfg.GetCulture (); autoCulture = cfg.IsAutoCulture; appui_culture = cfg.GetUICulture (); autoUICulture = cfg.IsAutoUICulture; context.StartTimeoutTimer (); Thread th = Thread.CurrentThread; if (app_culture != null) { prev_app_culture = th.CurrentCulture; CultureInfo new_app_culture = GetThreadCulture (Request, app_culture, autoCulture); if (!new_app_culture.Equals (Helpers.InvariantCulture)) th.CurrentCulture = new_app_culture; } if (appui_culture != null) { prev_appui_culture = th.CurrentUICulture; CultureInfo new_app_culture = GetThreadCulture (Request, appui_culture, autoUICulture); if (!new_app_culture.Equals (Helpers.InvariantCulture)) th.CurrentUICulture = new_app_culture; } prev_user = Thread.CurrentPrincipal; } void PostDone () { if (removeConfigurationFromCache) { WebConfigurationManager.RemoveConfigurationFromCache (context); removeConfigurationFromCache = false; } Thread th = Thread.CurrentThread; if (Thread.CurrentPrincipal != prev_user) Thread.CurrentPrincipal = prev_user; if (prev_appui_culture != null && prev_appui_culture != th.CurrentUICulture) th.CurrentUICulture = prev_appui_culture; if (prev_app_culture != null && prev_app_culture != th.CurrentCulture) th.CurrentCulture = prev_app_culture; if (context == null) context = HttpContext.Current; context.StopTimeoutTimer (); context.Request.ReleaseResources (); context.Response.ReleaseResources (); context = null; session = null; HttpContext.Current = null; } void Start (object x) { var cultures = x as CultureInfo []; if (cultures != null && cultures.Length == 2) { Thread ct = Thread.CurrentThread; ct.CurrentCulture = cultures [0]; ct.CurrentUICulture = cultures [1]; } InitOnce (true); if (initialization_exception != null) { Exception e = initialization_exception; HttpException exc = HttpException.NewWithCode (String.Empty, e, WebEventCodes.RuntimeErrorRequestAbort); context.Response.StatusCode = 500; FinalErrorWrite (context.Response, exc.GetHtmlErrorMessage ()); PipelineDone (); return; } HttpContext.Current = Context; PreStart (); pipeline = Pipeline (); Tick (); } const string HANDLER_CACHE = "@@HttpHandlerCache@@"; internal static Hashtable GetHandlerCache () { Cache cache = HttpRuntime.InternalCache; Hashtable ret = cache [HANDLER_CACHE] as Hashtable; if (ret == null) { ret = new Hashtable (); cache.Insert (HANDLER_CACHE, ret); } return ret; } internal static void ClearHandlerCache () { Hashtable cache = GetHandlerCache (); cache.Clear (); } object LocateHandler (HttpRequest req, string verb, string url) { Hashtable cache = GetHandlerCache (); string id = String.Concat (verb, url); object ret = cache [id]; if (ret != null) return ret; bool allowCache; HttpHandlersSection httpHandlersSection = WebConfigurationManager.GetSection ("system.web/httpHandlers", req.Path, req.Context) as HttpHandlersSection; ret = httpHandlersSection.LocateHandler (verb, url, out allowCache); IHttpHandler handler = ret as IHttpHandler; if (allowCache && handler != null && handler.IsReusable) cache [id] = ret; return ret; } internal IHttpHandler GetHandler (HttpContext context, string url) { return GetHandler (context, url, false); } // Used by HttpServerUtility.Execute internal IHttpHandler GetHandler (HttpContext context, string url, bool ignoreContextHandler) { if (!ignoreContextHandler && context.Handler != null) return context.Handler; HttpRequest request = context.Request; string verb = request.RequestType; IHttpHandler handler = null; object o = LocateHandler (request, verb, url); factory = o as IHttpHandlerFactory; if (factory == null) { handler = (IHttpHandler) o; } else { handler = factory.GetHandler (context, verb, url, request.MapPath (url)); } return handler; } void IHttpHandler.ProcessRequest (HttpContext context) { begin_iar = null; this.context = context; done.Reset (); Start (null); done.WaitOne (); } // // This is used by FireOnAppStart, when we init the application // as the context is required to be set at that point (the user // might call methods that require it on that hook). // internal void SetContext (HttpContext context) { this.context = context; } internal void SetSession (HttpSessionState session) { this.session = session; } IAsyncResult IHttpAsyncHandler.BeginProcessRequest (HttpContext context, AsyncCallback cb, object extraData) { this.context = context; done.Reset (); begin_iar = new AsyncRequestState (done, cb, extraData); CultureInfo[] cultures = new CultureInfo [2]; cultures [0] = Thread.CurrentThread.CurrentCulture; cultures [1] = Thread.CurrentThread.CurrentUICulture; if (Thread.CurrentThread.IsThreadPoolThread) Start (null); else ThreadPool.QueueUserWorkItem (x => { try { Start (x); } catch (Exception e) { Console.Error.WriteLine (e); } }); return begin_iar; } void IHttpAsyncHandler.EndProcessRequest (IAsyncResult result) { if (!result.IsCompleted) result.AsyncWaitHandle.WaitOne (); begin_iar = null; } public virtual void Init () { } bool IHttpHandler.IsReusable { get { return true; } } public static void RegisterModule (Type moduleType) { HttpRuntimeSection config = (HttpRuntimeSection)WebConfigurationManager.GetSection ("system.web/httpRuntime"); if (!config.AllowDynamicModuleRegistration) throw new InvalidOperationException ("The Application has requested to register a dynamic Module, but dynamic module registration is disabled in web.config."); dynamicModuleManeger.Add (moduleType); } HttpModuleCollection CreateDynamicModules () { HttpModuleCollection modules = new HttpModuleCollection (); foreach (var module in dynamicModuleManeger.LockAndGetModules ()) { IHttpModule httpModule = CreateModuleInstance (module.Type); httpModule.Init (this); modules.AddModule (module.Name, httpModule); } return modules; } IHttpModule CreateModuleInstance (Type type) { return (IHttpModule) Activator.CreateInstance (type, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance, null, null, null); } #region internals internal void ClearError () { context.ClearError (); } bool RedirectErrorPage (string error_page) { if (context.Request.QueryString ["aspxerrorpath"] != null) return false; Response.Redirect (error_page + "?aspxerrorpath=" + Request.Path, false); return true; } bool RedirectCustomError (ref HttpException httpEx) { try { if (!context.IsCustomErrorEnabledUnsafe) return false; CustomErrorsSection config = (CustomErrorsSection)WebConfigurationManager.GetSection ("system.web/customErrors"); if (config == null) { if (context.ErrorPage != null) return RedirectErrorPage (context.ErrorPage); return false; } CustomError err = config.Errors [context.Response.StatusCode.ToString()]; string redirect = err == null ? null : err.Redirect; if (redirect == null) { redirect = context.ErrorPage; if (redirect == null) redirect = config.DefaultRedirect; } if (redirect == null) return false; if (config.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite) { context.Server.Execute (redirect); return true; } return RedirectErrorPage (redirect); } catch (Exception ex) { httpEx = HttpException.NewWithCode (500, String.Empty, ex, WebEventCodes.WebErrorOtherError); return false; } } #endregion internal static string BinDirectory { get { if (binDirectory == null) { AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation; string baseDir = setup.ApplicationBase; string bindir; foreach (string dir in BinDirs) { bindir = Path.Combine (baseDir, dir); if (!Directory.Exists (bindir)) continue; binDirectory = bindir; break; } } return binDirectory; } } internal static string[] BinDirectoryAssemblies { get { ArrayList binDlls = null; string[] dlls; string bindir = BinDirectory; if (bindir != null) { binDlls = new ArrayList (); dlls = Directory.GetFiles (bindir, "*.dll"); binDlls.AddRange (dlls); } if (binDlls == null) return new string[] {}; return (string[]) binDlls.ToArray (typeof (string)); } } internal static Type LoadType (string typeName) { return LoadType (typeName, false); } internal static Type LoadType (string typeName, bool throwOnMissing) { Type type = Type.GetType (typeName); if (type != null) return type; Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies (); foreach (Assembly ass in assemblies) { type = ass.GetType (typeName, false); if (type != null) return type; } IList tla = System.Web.Compilation.BuildManager.TopLevelAssemblies; if (tla != null && tla.Count > 0) { foreach (Assembly asm in tla) { if (asm == null) continue; type = asm.GetType (typeName, false); if (type != null) return type; } } Exception loadException = null; try { type = null; type = LoadTypeFromBin (typeName); } catch (Exception ex) { loadException = ex; } if (type != null) return type; if (throwOnMissing) throw new TypeLoadException (String.Format ("Type '{0}' cannot be found", typeName), loadException); return null; } internal static Type LoadType (string typeName, bool throwOnMissing) { Type ret = LoadType (typeName, throwOnMissing); if (typeof (TBaseType).IsAssignableFrom (ret)) return ret; if (throwOnMissing) throw new TypeLoadException (String.Format ("Type '{0}' found but it doesn't derive from base type '{1}'.", typeName, typeof (TBaseType))); return null; } internal static Type LoadTypeFromBin (string typeName) { Type type = null; foreach (string s in BinDirectoryAssemblies) { Assembly binA = null; try { binA = Assembly.LoadFrom (s); } catch (FileLoadException) { // ignore continue; } catch (BadImageFormatException) { // ignore continue; } type = binA.GetType (typeName, false); if (type == null) continue; return type; } return null; } } // // Based on Fritz' Onion's AsyncRequestState class for asynchronous IHttpAsyncHandlers // class AsyncRequestState : IAsyncResult { AsyncCallback cb; object cb_data; bool completed; ManualResetEvent complete_event = null; internal AsyncRequestState (ManualResetEvent complete_event, AsyncCallback cb, object cb_data) { this.cb = cb; this.cb_data = cb_data; this.complete_event = complete_event; } internal void Complete () { completed = true; try { // // TODO: if this throws an error, we have no way of reporting it // Not really too bad, since the only failure might be // `HttpRuntime.request_processed'. // if (cb != null) cb (this); } catch { } complete_event.Set (); } public object AsyncState { get { return cb_data; } } public bool CompletedSynchronously { get { return false; } } public bool IsCompleted { get { return completed; } } public WaitHandle AsyncWaitHandle { get { return complete_event; } } } #region Helper classes // // A wrapper to keep track of begin/end pairs // class AsyncInvoker { public BeginEventHandler begin; public EndEventHandler end; public object data; HttpApplication app; AsyncCallback callback; public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, HttpApplication a, object d) { begin = bh; end = eh; data = d; app = a; callback = new AsyncCallback (doAsyncCallback); } public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, HttpApplication app) : this(bh, eh, app, null) { } public void Invoke (object sender, EventArgs e) { IAsyncResult res; res = begin (app, e, callback, data); } void doAsyncCallback (IAsyncResult res) { ThreadPool.QueueUserWorkItem ((object ores) => { IAsyncResult tres = (IAsyncResult) ores; try { end (tres); } catch (Exception ee) { // I tried using ProcessError(), but we only come here frome an Invokation in PipelineDone(). // Using ProcessError, I still get a blank screen, this way, we at least log the error to console... Console.Error.WriteLine (ee.ToString ()); } }, res); } } #endregion }