Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jmserrano-dev/a94f5f747865603c5e08fb5f9fed41f5 to your computer and use it in GitHub Desktop.
Save jmserrano-dev/a94f5f747865603c5e08fb5f9fed41f5 to your computer and use it in GitHub Desktop.
From 27d8be0c7e50b84e6f8b49236ca00ecc32df490b Mon Sep 17 00:00:00 2001
From: OlegBelousov <OlegBelousov@mail.ru>
Date: Wed, 25 May 2016 17:55:07 +0300
Subject: [PATCH] Synchronous execution of asynchronous web api stack.
It covers including the following questions:
http://stackoverflow.com/questions/33026799/mono-net-support-for-async-await
http://stackoverflow.com/questions/37012143/mono-with-owin-authentication
http://stackoverflow.com/questions/34834346/404-response-appended-to-webapi-response
http://stackoverflow.com/questions/34629192/mono-the-view-index-or-its-master-was-not-found
---
System.Web/System.Web/HttpApplication.cs | 346 +++++++++++++--------
.../System.Web/System.Web/HttpContextWrapper.cs | 11 +
System.Web/System.Web/HttpResponse.cs | 7 +
.../System.Web/System.Web/HttpResponseWrapper.cs | 3 +
4 files changed, 231 insertions(+), 136 deletions(-)
diff --git a/System.Web/System.Web/HttpApplication.cs b/System.Web/System.Web/HttpApplication.cs
index c2fc0ddb71ae..84a6fcf1461c 100644
--- a/System.Web/System.Web/HttpApplication.cs
+++ b/System.Web/System.Web/HttpApplication.cs
@@ -146,9 +146,6 @@ public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDis
// 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 ();
@@ -167,14 +164,6 @@ public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDis
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); }
@@ -919,15 +908,30 @@ internal void ProcessError (Exception e)
}
//
- // Ticks the clock: next step on the pipeline.
+ // Wait the end of Pipeline.
//
- internal void Tick ()
+ internal void WaitPipeline ()
{
try {
- if (pipeline.MoveNext ()){
- if ((bool)pipeline.Current)
- PipelineDone ();
+ while (pipeline.MoveNext ()){
+ if (pipeline.Current == null)
+ break;
+ AsyncInvoker ai = pipeline.Current as AsyncInvoker;
+ if (ai != null)
+ {
+ if (!ai.IsCompleted)
+ ai.WaitOne();
+ continue;
+ }
+ AsyncRequestInvoker ari = pipeline.Current as AsyncRequestInvoker;
+ if (ari != null)
+ {
+ if (!ari.IsCompleted)
+ ari.WaitOne ();
+ continue;
+ }
}
+ PipelineDone ();
} catch (ThreadAbortException taex) {
object obj = taex.ExceptionState;
Thread.ResetAbort ();
@@ -954,45 +958,7 @@ internal void Tick ()
}
}
- 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
@@ -1004,15 +970,13 @@ IEnumerable RunHooks (Delegate list)
foreach (EventHandler d in delegates){
if (d.Target != null && (d.Target is AsyncInvoker)){
- current_ai = (AsyncInvoker) d.Target;
+ AsyncInvoker 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);
+ ai.Invoke(this, EventArgs.Empty);
+ yield return ai;
} finally {
- in_begin = false;
context.EndTimeoutPossible ();
}
@@ -1020,10 +984,8 @@ IEnumerable RunHooks (Delegate list)
// If things are still moving forward, yield this
// thread now
//
- if (must_yield)
- yield return stop_processing;
- else if (stop_processing)
- yield return true;
+ if (stop_processing)
+ yield return null;
} else {
try {
context.BeginTimeoutPossible ();
@@ -1032,7 +994,7 @@ IEnumerable RunHooks (Delegate list)
context.EndTimeoutPossible ();
}
if (stop_processing)
- yield return true;
+ yield return null;
}
}
}
@@ -1116,7 +1078,6 @@ void PipelineDone ()
// context = null; -> moved to PostDone
pipeline = null;
- current_ai = null;
}
PostDone ();
@@ -1177,7 +1138,7 @@ IEnumerator Pipeline ()
{
Delegate eventHandler;
if (stop_processing)
- yield return true;
+ yield return null;
HttpRequest req = context.Request;
if (req != null)
req.Validate ();
@@ -1185,65 +1146,65 @@ IEnumerator Pipeline ()
StartTimer ("BeginRequest");
eventHandler = Events [BeginRequestEvent];
if (eventHandler != null) {
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
}
StopTimer ();
StartTimer ("AuthenticateRequest");
eventHandler = Events [AuthenticateRequestEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("DefaultAuthentication");
if (DefaultAuthentication != null)
- foreach (bool stop in RunHooks (DefaultAuthentication))
- yield return stop;
+ foreach (var ar in RunHooks (DefaultAuthentication))
+ yield return ar;
StopTimer ();
StartTimer ("PostAuthenticateRequest");
eventHandler = Events [PostAuthenticateRequestEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("AuthorizeRequest");
eventHandler = Events [AuthorizeRequestEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("PostAuthorizeRequest");
eventHandler = Events [PostAuthorizeRequestEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("ResolveRequestCache");
eventHandler = Events [ResolveRequestCacheEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("PostResolveRequestCache");
eventHandler = Events [PostResolveRequestCacheEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
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;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
context.MapRequestHandlerDone = true;
@@ -1276,28 +1237,28 @@ IEnumerator Pipeline ()
StopTimer ();
if (stop_processing)
- yield return true;
+ yield return null;
StartTimer ("PostMapRequestHandler");
eventHandler = Events [PostMapRequestHandlerEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("AcquireRequestState");
eventHandler = Events [AcquireRequestStateEvent];
if (eventHandler != null){
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (IAsyncResult ar in RunHooks (eventHandler))
+ yield return ar;
}
StopTimer ();
StartTimer ("PostAcquireRequestState");
eventHandler = Events [PostAcquireRequestStateEvent];
if (eventHandler != null){
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
}
StopTimer ();
@@ -1309,11 +1270,12 @@ IEnumerator Pipeline ()
StartTimer ("PreRequestHandlerExecute");
eventHandler = Events [PreRequestHandlerExecuteEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- if (stop)
+ foreach (var ar in RunHooks (eventHandler))
+ if (ar == null)
goto release;
+ else
+ yield return ar;
StopTimer ();
-
IHttpHandler ctxHandler = context.Handler;
@@ -1328,13 +1290,11 @@ IEnumerator Pipeline ()
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);
+ AsyncRequestInvoker ari = new AsyncRequestInvoker(async_handler, this);
+ ari.Invoke(context, handler);
+ yield return ari;
} else {
- must_yield = false;
handler.ProcessRequest (context);
}
} else
@@ -1342,13 +1302,10 @@ IEnumerator Pipeline ()
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)
+ if (stop_processing)
goto release;
// These are executed after the application has returned
@@ -1356,9 +1313,11 @@ IEnumerator Pipeline ()
StartTimer ("PostRequestHandlerExecute");
eventHandler = Events [PostRequestHandlerExecuteEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- if (stop)
+ foreach (var ar in RunHooks (eventHandler))
+ if (ar == null)
goto release;
+ else
+ yield return ar;
StopTimer ();
release:
@@ -1366,24 +1325,24 @@ IEnumerator Pipeline ()
eventHandler = Events [ReleaseRequestStateEvent];
if (eventHandler != null){
#pragma warning disable 219
- foreach (bool stop in RunHooks (eventHandler)) {
//
// Ignore the stop signal while release the state
//
-
- }
+ foreach (var ar in RunHooks (eventHandler))
+ if (ar != null)
+ yield return ar;
#pragma warning restore 219
}
StopTimer ();
if (stop_processing)
- yield return true;
+ yield return null;
StartTimer ("PostReleaseRequestState");
eventHandler = Events [PostReleaseRequestStateEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("Filter");
@@ -1394,33 +1353,29 @@ IEnumerator Pipeline ()
StartTimer ("UpdateRequestCache");
eventHandler = Events [UpdateRequestCacheEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("PostUpdateRequestCache");
eventHandler = Events [PostUpdateRequestCacheEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("LogRequest");
eventHandler = Events [LogRequestEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
StartTimer ("PostLogRequest");
eventHandler = Events [PostLogRequestEvent];
if (eventHandler != null)
- foreach (bool stop in RunHooks (eventHandler))
- yield return stop;
- StopTimer ();
-
- StartTimer ("PipelineDone");
- PipelineDone ();
+ foreach (var ar in RunHooks (eventHandler))
+ yield return ar;
StopTimer ();
}
@@ -1518,7 +1473,7 @@ void Start (object x)
HttpContext.Current = Context;
PreStart ();
pipeline = Pipeline ();
- Tick ();
+ WaitPipeline ();
}
const string HANDLER_CACHE = "@@HttpHandlerCache@@";
@@ -1927,6 +1882,73 @@ internal void Complete ()
}
#region Helper classes
+
+ //
+ // A wrapper to keep track of begin/end pairs for request
+ //
+ class AsyncRequestInvoker {
+ IHttpAsyncHandler async_handler;
+ HttpApplication app;
+ AsyncCallback callback;
+ bool completed = false;
+ ManualResetEvent manual_event = new ManualResetEvent(false);
+
+ CultureInfo _culture;
+ CultureInfo _ui_culture;
+
+ public AsyncRequestInvoker (IHttpAsyncHandler ah, HttpApplication a)
+ {
+ app = a;
+ async_handler = ah;
+ callback = new AsyncCallback (doAsyncCallback);
+ }
+
+ public void Invoke (HttpContext context, object extraData)
+ {
+ completed = false;
+ manual_event.Reset();
+
+ Thread th = Thread.CurrentThread;
+ _culture = th.CurrentCulture;
+ _ui_culture = th.CurrentUICulture;
+
+ IAsyncResult res = async_handler.BeginProcessRequest (context, callback, extraData);
+ if (res.IsCompleted)
+ {
+ completed = true;
+ manual_event.Set();
+ }
+ }
+
+ public bool IsCompleted
+ {
+ get {return completed; }
+ }
+
+ public void WaitOne()
+ {
+ manual_event.WaitOne ();
+ }
+
+ void doAsyncCallback (IAsyncResult res)
+ {
+ Thread th = Thread.CurrentThread;
+ th.CurrentCulture = _culture;
+ th.CurrentUICulture = _ui_culture;
+ HttpContext.Current = app.Context;
+
+ try {
+ if (res != null)
+ async_handler.EndProcessRequest(res);
+ } catch (Exception ee) {
+ app.ProcessError(ee);
+ } finally {
+ completed = true;
+ manual_event.Set();
+ }
+ }
+ }
+
//
// A wrapper to keep track of begin/end pairs
@@ -1937,6 +1959,12 @@ class AsyncInvoker {
public object data;
HttpApplication app;
AsyncCallback callback;
+ bool completed = false;
+ ManualResetEvent manual_event = new ManualResetEvent(false);
+
+ CultureInfo _culture;
+ CultureInfo _ui_culture;
+
public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, HttpApplication a, object d)
{
@@ -1951,22 +1979,68 @@ public AsyncInvoker (BeginEventHandler bh, EndEventHandler eh, HttpApplication a
public void Invoke (object sender, EventArgs e)
{
- IAsyncResult res;
- res = begin (app, e, callback, data);
+ completed = false;
+ manual_event.Reset();
+
+ Thread th = Thread.CurrentThread;
+ _culture = th.CurrentCulture;
+ _ui_culture = th.CurrentUICulture;
+
+ IAsyncResult res = begin (app, e, callback, data);
+ if (res.IsCompleted)
+ {
+ completed = true;
+ manual_event.Set();
+ }
+ }
+
+ public bool IsCompleted
+ {
+ get {return completed; }
+ }
+
+ public void WaitOne()
+ {
+ manual_event.WaitOne ();
}
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);
+ Thread th = Thread.CurrentThread;
+ th.CurrentCulture = _culture;
+ th.CurrentUICulture = _ui_culture;
+ HttpContext.Current = app.Context;
+
+ try {
+ end (res);
+ } 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 ());
+
+ // Now, we may call ProcessError.
+ app.ProcessError(ee);
+ } finally {
+ completed = true;
+ manual_event.Set();
+ }
+
+// 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 ());
+//
+// // Now, we may call ProcessError.
+// app.ProcessError(ee);
+// } finally {
+// completed = true;
+// manual_event.Set();
+// }
+// }, res);
}
}
#endregion
diff --git a/System.Web/System.Web/HttpContextWrapper.cs b/System.Web/System.Web/HttpContextWrapper.cs
index 64975a813c0f..b88ced527941 100644
--- a/System.Web/System.Web/HttpContextWrapper.cs
+++ b/System.Web/System.Web/HttpContextWrapper.cs
@@ -212,5 +212,16 @@ public override void SetSessionStateBehavior (SessionStateBehavior sessionStateB
{
w.SetSessionStateBehavior (sessionStateBehavior);
}
+
+ internal static Action<HttpContext> WrapCallback(Action<HttpContextBase> callback)
+ {
+ if (callback != null)
+ {
+ return delegate (HttpContext context) {
+ callback(new HttpContextWrapper(context));
+ };
+ }
+ return null;
+ }
}
}
diff --git a/System.Web/System.Web/HttpResponse.cs b/System.Web/System.Web/HttpResponse.cs
index cc90a54ab971..82a2c0c47c3e 100644
--- a/System.Web/System.Web/HttpResponse.cs
+++ b/System.Web/System.Web/HttpResponse.cs
@@ -644,6 +644,12 @@ public void End ()
}
}
+ public ISubscriptionToken AddOnSendingHeaders(Action<HttpContext> callback) {
+ // TODO
+ // Console.Error.WriteLine("AddOnSendingHeaders");
+ return null;
+ }
+
// Generate:
// Content-Length
// Content-Type
@@ -730,6 +736,7 @@ void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
}
}
+
internal void WriteHeaders (bool final_flush)
{
if (headers_sent)
diff --git a/System.Web/System.Web/HttpResponseWrapper.cs b/System.Web/System.Web/HttpResponseWrapper.cs
index 1e437e4621fc..bf603eb38125 100644
--- a/System.Web/System.Web/HttpResponseWrapper.cs
+++ b/System.Web/System.Web/HttpResponseWrapper.cs
@@ -225,6 +225,9 @@ public override void AppendCookie (HttpCookie cookie)
w.AppendCookie (cookie);
}
+ public override ISubscriptionToken AddOnSendingHeaders(Action<HttpContextBase> callback) =>
+ w.AddOnSendingHeaders(HttpContextWrapper.WrapCallback(callback));
+
public override void AppendHeader (string name, string value)
{
w.AppendHeader (name, value);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment