//
// System.Web.HttpApplication.cs
//
// Author:
// Miguel de Icaza (miguel@novell.com)
// Gonzalo Paniagua (gonzalo@ximian.com)
//
//
// Copyright (C) 2005 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.IO;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Security.Permissions;
using System.Security.Principal;
using System.Threading;
using System.Web.Configuration;
using System.Web.SessionState;
using System.Web.UI;
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 {
HttpContext context;
HttpSessionState session;
ISite isite;
// The source, and the exposed API (cache).
HttpModuleCollection modcoll;
string assemblyLocation;
//
// The factory for the handler currently running.
//
IHttpHandlerFactory factory;
//
// Whether the pipeline should be stopped
//
bool stop_processing;
//
// 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;
// We don't use the EventHandlerList here, but derived classes might do
EventHandlerList events;
// Culture and IPrincipal
CultureInfo app_culture;
CultureInfo appui_culture;
CultureInfo prev_app_culture;
CultureInfo prev_appui_culture;
IPrincipal prev_user;
//
// 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 HttpApplication ()
{
done = new ManualResetEvent (false);
}
internal void InitOnce (bool full_init)
{
lock (this) {
if (modcoll != null)
return;
#if NET_2_0
HttpModulesSection modules;
modules = (HttpModulesSection) WebConfigurationManager.GetSection ("system.web/httpModules");
#else
ModulesConfiguration modules;
modules = (ModulesConfiguration) HttpContext.GetAppConfig ("system.web/httpModules");
#endif
modcoll = modules.LoadModules (this);
if (full_init)
HttpApplicationFactory.AttachEvents (this);
#if NET_2_0
GlobalizationSection cfg;
cfg = (GlobalizationSection) WebConfigurationManager.GetSection ("system.web/globalization");
app_culture = cfg.GetCulture();
appui_culture = cfg.GetUICulture();
#else
GlobalizationConfiguration cfg;
cfg = GlobalizationConfiguration.GetInstance (null);
if (cfg != null) {
app_culture = cfg.Culture;
appui_culture = cfg.UICulture;
}
#endif
}
}
internal string AssemblyLocation {
get {
if (assemblyLocation == null)
assemblyLocation = GetType ().Assembly.Location;
return assemblyLocation;
}
}
[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 new HttpException (Locale.GetText ("No context is available."));
if (false == HttpApplicationFactory.ContextAvailable)
throw new HttpException (Locale.GetText ("Request is not available in this context."));
return context.Request;
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public HttpResponse Response {
get {
if (context == null)
throw new HttpException (Locale.GetText ("No context is available."));
if (false == HttpApplicationFactory.ContextAvailable)
throw new HttpException (Locale.GetText ("Response is not available in this context."));
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 new HttpException (Locale.GetText ("No context is available."));
return context.Session;
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public virtual 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;
}
}
public virtual event EventHandler Disposed;
public virtual event EventHandler Error;
public event EventHandler PreSendRequestHeaders;
internal void TriggerPreSendRequestHeaders ()
{
if (PreSendRequestHeaders != null)
PreSendRequestHeaders (this, EventArgs.Empty);
}
public event EventHandler PreSendRequestContent;
internal void TriggerPreSendRequestContent ()
{
if (PreSendRequestContent != null)
PreSendRequestContent (this, EventArgs.Empty);
}
public event EventHandler AcquireRequestState;
public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
AcquireRequestState += new EventHandler (invoker.Invoke);
}
public event EventHandler AuthenticateRequest;
public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
AuthenticateRequest += new EventHandler (invoker.Invoke);
}
public event EventHandler AuthorizeRequest;
public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
AuthorizeRequest += new EventHandler (invoker.Invoke);
}
public event EventHandler BeginRequest;
public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
BeginRequest += new EventHandler (invoker.Invoke);
}
public event EventHandler EndRequest;
public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
EndRequest += new EventHandler (invoker.Invoke);
}
public event EventHandler PostRequestHandlerExecute;
public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
}
public event EventHandler PreRequestHandlerExecute;
public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
}
public event EventHandler ReleaseRequestState;
public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
ReleaseRequestState += new EventHandler (invoker.Invoke);
}
public event EventHandler ResolveRequestCache;
public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
ResolveRequestCache += new EventHandler (invoker.Invoke);
}
public event EventHandler UpdateRequestCache;
public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh);
UpdateRequestCache += new EventHandler (invoker.Invoke);
}
#if NET_2_0
public event EventHandler PostAuthenticateRequest;
public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh)
{
AddOnPostAuthenticateRequestAsync (bh, eh, null);
}
public void AddOnPostAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostAuthenticateRequest += new EventHandler (invoker.Invoke);
}
public event EventHandler PostAuthorizeRequest;
public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh)
{
AddOnPostAuthorizeRequestAsync (bh, eh, null);
}
public void AddOnPostAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostAuthorizeRequest += new EventHandler (invoker.Invoke);
}
public event EventHandler PostResolveRequestCache;
public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
{
AddOnPostResolveRequestCacheAsync (bh, eh, null);
}
public void AddOnPostResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostResolveRequestCache += new EventHandler (invoker.Invoke);
}
public event EventHandler PostMapRequestHandler;
public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh)
{
AddOnPostMapRequestHandlerAsync (bh, eh, null);
}
public void AddOnPostMapRequestHandlerAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostMapRequestHandler += new EventHandler (invoker.Invoke);
}
public event EventHandler PostAcquireRequestState;
public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
{
AddOnPostAcquireRequestStateAsync (bh, eh, null);
}
public void AddOnPostAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostAcquireRequestState += new EventHandler (invoker.Invoke);
}
public event EventHandler PostReleaseRequestState;
public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh)
{
AddOnPostReleaseRequestStateAsync (bh, eh, null);
}
public void AddOnPostReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostReleaseRequestState += new EventHandler (invoker.Invoke);
}
public event EventHandler PostUpdateRequestCache;
public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh)
{
AddOnPostUpdateRequestCacheAsync (bh, eh, null);
}
public void AddOnPostUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostUpdateRequestCache += new EventHandler (invoker.Invoke);
}
//
// The new overloads that take a data parameter
//
public void AddOnAcquireRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
AcquireRequestState += new EventHandler (invoker.Invoke);
}
public void AddOnAuthenticateRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
AuthenticateRequest += new EventHandler (invoker.Invoke);
}
public void AddOnAuthorizeRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
AuthorizeRequest += new EventHandler (invoker.Invoke);
}
public void AddOnBeginRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
BeginRequest += new EventHandler (invoker.Invoke);
}
public void AddOnEndRequestAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
EndRequest += new EventHandler (invoker.Invoke);
}
public void AddOnPostRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PostRequestHandlerExecute += new EventHandler (invoker.Invoke);
}
public void AddOnPreRequestHandlerExecuteAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
PreRequestHandlerExecute += new EventHandler (invoker.Invoke);
}
public void AddOnReleaseRequestStateAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
ReleaseRequestState += new EventHandler (invoker.Invoke);
}
public void AddOnResolveRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
ResolveRequestCache += new EventHandler (invoker.Invoke);
}
public void AddOnUpdateRequestCacheAsync (BeginEventHandler bh, EndEventHandler eh, object data)
{
AsyncInvoker invoker = new AsyncInvoker (bh, eh, data);
UpdateRequestCache += new EventHandler (invoker.Invoke);
}
#endif
internal event EventHandler DefaultAuthentication;
//
// 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; }
}
public virtual void Dispose ()
{
if (modcoll != null) {
for (int i = modcoll.Count; i >= 0; i--) {
modcoll.Get (i).Dispose ();
}
modcoll = null;
}
if (Disposed != null)
Disposed (this, EventArgs.Empty);
done.Close ();
done = null;
}
public virtual string GetVaryByCustomString (HttpContext context, string custom)
{
if (custom == null) // Sigh
throw new NullReferenceException ();
if (0 == String.Compare (custom, "browser", true, CultureInfo.InvariantCulture))
return context.Request.Browser.Type;
return null;
}
//
// If we catch an error, queue this error
//
void ProcessError (Exception e)
{
bool first = context.Error == null;
context.AddError (e);
if (first){
if (Error != null){
try {
Error (this, EventArgs.Empty);
} catch (ThreadAbortException ee){
// This happens on Redirect() or End()
Thread.ResetAbort ();
} catch (Exception ee){
context.AddError (ee);
}
}
}
stop_processing = true;
}
//
// Ticks the clock: next step on the pipeline.
//
void Tick ()
{
try {
if (pipeline.MoveNext ()){
if ((bool)pipeline.Current)
PipelineDone ();
}
} catch (Exception e) {
Console.WriteLine ("Tick caught an exception that has not been propagated:\n" + e);
}
}
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 = ((IHttpAsyncHandler) ar.AsyncState);
try {
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
//
#if TARGET_JVM && !NET_2_0
sealed class RunHooksEnumerator : IEnumerable, IEnumerator
{
Delegate [] delegates;
int currentStep = 0;
HttpApplication app;
internal RunHooksEnumerator(HttpApplication app, Delegate list)
{
this.app = app;
delegates = list.GetInvocationList ();
}
public IEnumerator GetEnumerator() { return this; }
public object Current { get{ return app.stop_processing; } }
public void Reset()
{
throw new NotImplementedException("HttpApplication.RunHooksEnumerator.Reset called.");
}
public bool MoveNext ()
{
while (currentStep < delegates.Length) {
if (ProcessDelegate((EventHandler)delegates[currentStep++]))
return true;
}
return false;
}
bool ProcessDelegate(EventHandler d)
{
if (d.Target != null && (d.Target is AsyncInvoker)){
app.current_ai = (AsyncInvoker) d.Target;
try {
app.must_yield = true;
app.in_begin = true;
app.context.BeginTimeoutPossible ();
app.current_ai.begin (app, EventArgs.Empty, new AsyncCallback(app.async_callback_completed_cb), app.current_ai.data);
}
catch (ThreadAbortException taex){
object obj = taex.ExceptionState;
Thread.ResetAbort ();
app.stop_processing = true;
if (obj is StepTimeout)
app.ProcessError (new HttpException ("The request timed out."));
}
catch (Exception e){
app.ProcessError (e);
}
finally {
app.in_begin = false;
app.context.EndTimeoutPossible ();
}
//
// If things are still moving forward, yield this
// thread now
//
if (app.must_yield)
return true;
else if (app.stop_processing)
return true;
}
else {
try {
app.context.BeginTimeoutPossible ();
d (app, EventArgs.Empty);
} catch (ThreadAbortException taex){
object obj = taex.ExceptionState;
Thread.ResetAbort ();
app.stop_processing = true;
if (obj is StepTimeout)
app.ProcessError (new HttpException ("The request timed out."));
}
catch (Exception e){
app.ProcessError (e);
}
finally {
app.context.EndTimeoutPossible ();
}
if (app.stop_processing)
return true;
}
return false;
}
}
IEnumerable RunHooks (Delegate list)
{
return new RunHooksEnumerator(this, list);
}
#else
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);
} catch (ThreadAbortException taex){
object obj = taex.ExceptionState;
Thread.ResetAbort ();
stop_processing = true;
if (obj is StepTimeout)
ProcessError (new HttpException ("The request timed out."));
} catch (Exception e){
ProcessError (e);
} 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);
} catch (ThreadAbortException taex){
object obj = taex.ExceptionState;
Thread.ResetAbort ();
stop_processing = true;
if (obj is StepTimeout)
ProcessError (new HttpException ("The request timed out."));
} catch (Exception e){
ProcessError (e);
} finally {
context.EndTimeoutPossible ();
}
if (stop_processing)
yield return true;
}
}
}
#endif
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 = new HttpException ("", error);
response.StatusCode = 500;
}
if (!RedirectCustomError ())
FinalErrorWrite (response, ((HttpException) error).GetHtmlErrorMessage ());
else
response.Flush (true);
} else {
if (!(error is HttpException))
error = new HttpException ("", error);
FinalErrorWrite (response, ((HttpException) error).GetHtmlErrorMessage ());
}
}
}
//
// Invoked at the end of the pipeline execution
//
void PipelineDone ()
{
try {
if (EndRequest != null)
EndRequest (this, EventArgs.Empty);
} catch (Exception e){
ProcessError (e);
}
try {
OutputPage ();
} catch (Exception e) {
Console.WriteLine ("Internal error: OutputPage threw an exception " + e);
} finally {
context.WorkerRequest.EndOfRequest();
if (begin_iar != null){
try {
begin_iar.Complete ();
} catch {
//
// 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'
//
}
}
done.Set ();
if (factory != null && context.Handler != null){
factory.ReleaseHandler (context.Handler);
factory = null;
}
context.Handler = null;
// context = null; -> moved to PostDone
pipeline = null;
current_ai = null;
}
PostDone ();
}
//
// Events fired as described in `Http Runtime Support, HttpModules,
// Handling Public Events'
//
#if TARGET_JVM && !NET_2_0
sealed class PipeLineEnumerator : IEnumerator
{
readonly HttpApplication _this;
object current;
int currentYield;
IEnumerator currentEnumerator;
IHttpHandler handler = null;
internal PipeLineEnumerator(HttpApplication app) {
_this = app;
}
public object Current { get{ return currentEnumerator != null ? currentEnumerator.Current : current; } }
public void Reset() {
currentEnumerator = null;
currentYield = 0;
}
void ResetEnumerator() {
if (currentEnumerator != null) {
current = currentEnumerator.Current;
currentEnumerator = null;
}
}
public bool MoveNext () {
switch (currentYield) {
case 0: break;
case 1: goto yield_1;
case 2: goto yield_2;
case 3: goto yield_3;
case 4: goto yield_4;
#if NET_2_0
case 5: goto yield_5;
#endif
case 6: goto yield_6;
#if NET_2_0
case 7: goto yield_7;
#endif
case 8: goto yield_8;
case 9: goto yield_9;
#if NET_2_0
case 10: goto yield_10;
case 11: goto yield_11;
#endif
case 12: goto yield_12;
#if NET_2_0
case 13: goto yield_13;
#endif
case 14: goto yield_14;
case 15: goto yield_15;
#if NET_2_0
case 16: goto yield_16;
#endif
case 17: goto yield_17;
#if NET_2_0
case 18: goto yield_18;
#endif
default: goto yield_19;
}
if (_this.stop_processing) {
//yield return true;
current = true;
currentYield = 1;
return true;
}
yield_1:
yield_2:
if (_this.BeginRequest != null) {
//foreach (bool stop in RunHooks (BeginRequest))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 2;
currentEnumerator = _this.RunHooks(_this.BeginRequest).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
yield_3:
if (_this.AuthenticateRequest != null) {
//foreach (bool stop in RunHooks (AuthenticateRequest))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 3;
currentEnumerator = _this.RunHooks(_this.AuthenticateRequest).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
yield_4:
if (_this.DefaultAuthentication != null) {
//foreach (bool stop in RunHooks (DefaultAuthentication))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 4;
currentEnumerator = _this.RunHooks(_this.DefaultAuthentication).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#if NET_2_0
yield_5:
if (_this.PostAuthenticateRequest != null) {
//foreach (bool stop in RunHooks (AuthenticateRequest))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 5;
currentEnumerator = _this.RunHooks(_this.PostAuthenticateRequest).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#endif
yield_6:
if (_this.AuthorizeRequest != null) {
//foreach (bool stop in RunHooks (AuthorizeRequest))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 6;
currentEnumerator = _this.RunHooks(_this.AuthorizeRequest).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#if NET_2_0
yield_7:
if (_this.PostAuthorizeRequest != null) {
//foreach (bool stop in RunHooks (PostAuthorizeRequest))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 7;
currentEnumerator = _this.RunHooks(_this.PostAuthorizeRequest).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#endif
yield_8:
if (_this.ResolveRequestCache != null) {
//foreach (bool stop in RunHooks (ResolveRequestCache))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 8;
currentEnumerator = _this.RunHooks(_this.ResolveRequestCache).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
// Obtain the handler for the request.
//IHttpHandler handler = null;
try {
handler = _this.GetHandler (_this.context);
} catch (FileNotFoundException fnf){
if (_this.context.Request.IsLocal)
_this.ProcessError (new HttpException (404, String.Format ("File not found {0}", fnf.FileName), fnf));
else
_this.ProcessError (new HttpException (404, "File not found", fnf));
} catch (DirectoryNotFoundException dnf){
_this.ProcessError (new HttpException (404, "Directory not found", dnf));
} catch (Exception e) {
_this.ProcessError (e);
}
if (_this.stop_processing) {
//yield return true;
current = true;
currentYield = 9;
return true;
}
yield_9:
#if NET_2_0
yield_10:
if (_this.PostResolveRequestCache != null) {
//foreach (bool stop in RunHooks (PostResolveRequestCache))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 10;
currentEnumerator = _this.RunHooks(_this.PostResolveRequestCache).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
yield_11:
if (_this.PostMapRequestHandler != null) {
//foreach (bool stop in RunHooks (PostMapRequestHandler))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 11;
currentEnumerator = _this.RunHooks(_this.PostMapRequestHandler).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#endif
yield_12:
if (_this.AcquireRequestState != null){
//foreach (bool stop in RunHooks (AcquireRequestState))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 12;
currentEnumerator = _this.RunHooks(_this.AcquireRequestState).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#if NET_2_0
yield_13:
if (_this.PostAcquireRequestState != null){
//foreach (bool stop in RunHooks (PostAcquireRequestState))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 13;
currentEnumerator = _this.RunHooks(_this.PostAcquireRequestState).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#endif
//
// 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.
//
if (_this.PreRequestHandlerExecute != null)
foreach (bool stop in _this.RunHooks (_this.PreRequestHandlerExecute))
if (stop)
goto release;
try {
_this.context.BeginTimeoutPossible ();
if (handler != null){
IHttpAsyncHandler async_handler = handler as IHttpAsyncHandler;
if (async_handler != null){
_this.must_yield = true;
_this.in_begin = true;
async_handler.BeginProcessRequest (_this.context, new AsyncCallback(_this.async_handler_complete_cb), handler);
} else {
_this.must_yield = false;
handler.ProcessRequest (_this.context);
}
}
} catch (ThreadAbortException taex){
object obj = taex.ExceptionState;
Thread.ResetAbort ();
_this.stop_processing = true;
if (obj is StepTimeout)
_this.ProcessError (new HttpException ("The request timed out."));
} catch (Exception e){
_this.ProcessError (e);
} finally {
_this.in_begin = false;
_this.context.EndTimeoutPossible ();
}
if (_this.must_yield) {
//yield return stop_processing;
current = _this.stop_processing;
currentYield = 14;
return true;
}
else if (_this.stop_processing)
goto release;
yield_14:
// These are executed after the application has returned
if (_this.PostRequestHandlerExecute != null)
foreach (bool stop in _this.RunHooks (_this.PostRequestHandlerExecute))
if (stop)
goto release;
release:
if (_this.ReleaseRequestState != null){
foreach (bool stop in _this.RunHooks (_this.ReleaseRequestState)){
//
// Ignore the stop signal while release the state
//
}
}
if (_this.stop_processing) {
//yield return true;
current = true;
currentYield = 15;
return true;
}
yield_15:
#if NET_2_0
yield_16:
if (_this.PostReleaseRequestState != null) {
//foreach (bool stop in RunHooks (PostReleaseRequestState))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 16;
currentEnumerator = _this.RunHooks(_this.PostReleaseRequestState).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#endif
if (_this.context.Error == null)
_this.context.Response.DoFilter (true);
yield_17:
if (_this.UpdateRequestCache != null) {
//foreach (bool stop in RunHooks (UpdateRequestCache))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 17;
currentEnumerator = _this.RunHooks(_this.UpdateRequestCache).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#if NET_2_0
yield_18:
if (_this.PostUpdateRequestCache != null) {
//foreach (bool stop in RunHooks (PostUpdateRequestCache))
// yield return stop;
if (currentEnumerator == null) {
currentYield = 18;
currentEnumerator = _this.RunHooks(_this.PostUpdateRequestCache).GetEnumerator();
}
while (currentEnumerator.MoveNext())
return true;
ResetEnumerator();
}
#endif
_this.PipelineDone ();
currentYield = 19;
yield_19:
return false;
}
}
IEnumerator Pipeline ()
{
return new PipeLineEnumerator(this);
}
#else
IEnumerator Pipeline ()
{
if (stop_processing)
yield return true;
if (BeginRequest != null)
foreach (bool stop in RunHooks (BeginRequest))
yield return stop;
if (AuthenticateRequest != null)
foreach (bool stop in RunHooks (AuthenticateRequest))
yield return stop;
if (DefaultAuthentication != null)
foreach (bool stop in RunHooks (DefaultAuthentication))
yield return stop;
#if NET_2_0
if (PostAuthenticateRequest != null)
foreach (bool stop in RunHooks (AuthenticateRequest))
yield return stop;
#endif
if (AuthorizeRequest != null)
foreach (bool stop in RunHooks (AuthorizeRequest))
yield return stop;
#if NET_2_0
if (PostAuthorizeRequest != null)
foreach (bool stop in RunHooks (PostAuthorizeRequest))
yield return stop;
#endif
if (ResolveRequestCache != null)
foreach (bool stop in RunHooks (ResolveRequestCache))
yield return stop;
// Obtain the handler for the request.
IHttpHandler handler = null;
try {
handler = GetHandler (context);
} catch (FileNotFoundException fnf){
if (context.Request.IsLocal)
ProcessError (new HttpException (404, String.Format ("File not found {0}", fnf.FileName), fnf));
else
ProcessError (new HttpException (404, "File not found", fnf));
} catch (DirectoryNotFoundException dnf){
ProcessError (new HttpException (404, "Directory not found", dnf));
} catch (Exception e) {
ProcessError (e);
}
if (stop_processing)
yield return true;
#if NET_2_0
if (PostResolveRequestCache != null)
foreach (bool stop in RunHooks (PostResolveRequestCache))
yield return stop;
if (PostMapRequestHandler != null)
foreach (bool stop in RunHooks (PostMapRequestHandler))
yield return stop;
#endif
if (AcquireRequestState != null){
foreach (bool stop in RunHooks (AcquireRequestState))
yield return stop;
}
#if NET_2_0
if (PostAcquireRequestState != null){
foreach (bool stop in RunHooks (PostAcquireRequestState))
yield return stop;
}
#endif
//
// 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.
//
if (PreRequestHandlerExecute != null)
foreach (bool stop in RunHooks (PreRequestHandlerExecute))
if (stop)
goto release;
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);
}
}
} catch (ThreadAbortException taex){
object obj = taex.ExceptionState;
Thread.ResetAbort ();
stop_processing = true;
if (obj is StepTimeout)
ProcessError (new HttpException ("The request timed out."));
} catch (Exception e){
ProcessError (e);
} finally {
in_begin = false;
context.EndTimeoutPossible ();
}
if (must_yield)
yield return stop_processing;
else if (stop_processing)
goto release;
// These are executed after the application has returned
if (PostRequestHandlerExecute != null)
foreach (bool stop in RunHooks (PostRequestHandlerExecute))
if (stop)
goto release;
release:
if (ReleaseRequestState != null){
foreach (bool stop in RunHooks (ReleaseRequestState)){
//
// Ignore the stop signal while release the state
//
}
}
if (stop_processing)
yield return true;
#if NET_2_0
if (PostReleaseRequestState != null)
foreach (bool stop in RunHooks (PostReleaseRequestState))
yield return stop;
#endif
if (context.Error == null)
context.Response.DoFilter (true);
if (UpdateRequestCache != null)
foreach (bool stop in RunHooks (UpdateRequestCache))
yield return stop;
#if NET_2_0
if (PostUpdateRequestCache != null)
foreach (bool stop in RunHooks (PostUpdateRequestCache))
yield return stop;
#endif
PipelineDone ();
}
#endif
void PreStart ()
{
#if !TARGET_J2EE
HttpRuntime.TimeoutManager.Add (context);
#endif
Thread th = Thread.CurrentThread;
if (app_culture != null) {
prev_app_culture = th.CurrentCulture;
th.CurrentCulture = app_culture;
}
if (appui_culture != null) {
prev_appui_culture = th.CurrentUICulture;
th.CurrentUICulture = appui_culture;
}
#if !TARGET_JVM
prev_user = Thread.CurrentPrincipal;
#endif
}
void PostDone ()
{
Thread th = Thread.CurrentThread;
#if !TARGET_JVM
if (Thread.CurrentPrincipal != prev_user)
Thread.CurrentPrincipal = prev_user;
#endif
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 !TARGET_J2EE
HttpRuntime.TimeoutManager.Remove (context);
#endif
context = null;
session = null;
HttpContext.Current = null;
}
void Start (object x)
{
InitOnce (true);
PreStart ();
pipeline = Pipeline ();
Tick ();
}
// Used by HttpServerUtility.Execute
internal IHttpHandler GetHandler (HttpContext context)
{
HttpRequest request = context.Request;
string verb = request.RequestType;
string url = request.FilePath;
IHttpHandler handler = null;
#if NET_2_0
HttpHandlersSection section = (HttpHandlersSection) WebConfigurationManager.GetSection ("system.web/httpHandlers");
object o = section.LocateHandler (verb, url);
#else
HandlerFactoryConfiguration factory_config = (HandlerFactoryConfiguration) HttpContext.GetAppConfig ("system.web/httpHandlers");
object o = factory_config.LocateHandler (verb, url);
#endif
factory = o as IHttpHandlerFactory;
if (factory == null) {
handler = (IHttpHandler) o;
} else {
handler = factory.GetHandler (context, verb, url, request.PhysicalPath);
}
context.Handler = handler;
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);
#if TARGET_JVM
if (true)
#else
if (Thread.CurrentThread.IsThreadPoolThread)
#endif
Start (null);
else
ThreadPool.QueueUserWorkItem (new WaitCallback (Start), null);
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;
}
}
#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 ()
{
if (!context.IsCustomErrorEnabled)
return false;
#if NET_2_0
CustomErrorsSection config = (CustomErrorsSection)WebConfigurationManager.GetSection ("system.web/customErrors");
#else
CustomErrorsConfig config = null;
try {
config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
} catch { }
#endif
if (config == null) {
if (context.ErrorPage != null)
return RedirectErrorPage (context.ErrorPage);
return false;
}
#if NET_2_0
CustomError err = config.Errors [context.Response.StatusCode.ToString()];
string redirect = err == null ? null : err.Redirect;
#else
string redirect = config [context.Response.StatusCode];
#endif
if (redirect == null) {
redirect = context.ErrorPage;
if (redirect == null)
redirect = config.DefaultRedirect;
}
if (redirect == null)
return false;
return RedirectErrorPage (redirect);
}
#endregion
}
//
// 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;
if (cb != null)
cb (this);
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;
public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, object d)
{
begin = bh;
end = eh;
data = d;
}
public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh)
{
begin = bh;
end = eh;
}
public void Invoke (object sender, EventArgs e)
{
throw new Exception ("This is just a dummy");
}
}
#endregion
}
|