先看一张图, 从这张图里, 能看到请求是如何从CLR进入HttpRuntime的.

一、AppManagerAppDomainFactory

看到这张图是从 AppManagerAppDomainFactory 开始的, 按照汤姆大叔博文中所说, 是在CLR初始化加载的时候, 来加载这个类的. 那么来看一下这个类吧.

使用Reflector反编译搜索AppManagerAppDomainFactory 类, 可以看到(由于这个类并不多, 那么我先贴一个完整的出来吧):

[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public sealed class AppManagerAppDomainFactory : IAppManagerAppDomainFactory
{
// Fields
private ApplicationManager _appManager = ApplicationManager.GetApplicationManager(); // Methods
public AppManagerAppDomainFactory()
{
this._appManager.Open();
} internal static string ConstructSimpleAppName(string virtPath)
{
if (virtPath.Length > )
{
return virtPath.Substring().ToLower(CultureInfo.InvariantCulture).Replace('/', '_');
}
if (!BuildManagerHost.InClientBuildManager && HostingEnvironment.IsDevelopmentEnvironment)
{
return "vs";
}
return "root";
} [return: MarshalAs(UnmanagedType.Interface)]
public object Create(string appId, string appPath)
{
object obj2;
try
{
if (appPath[] == '.')
{
FileInfo info = new FileInfo(appPath);
appPath = info.FullName;
}
if (!StringUtil.StringEndsWith(appPath, '\\'))
{
appPath = appPath + @"\";
}
ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath, false);
ISAPIRuntime o = (ISAPIRuntime) this._appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost, false, null);
o.StartProcessing();
obj2 = new ObjectHandle(o);
}
catch (Exception)
{
throw;
}
return obj2;
} public void Stop()
{
this._appManager.Close();
}
}

至于这里详细的解说, 推荐去 MVC之前的那些事儿 去瞧瞧, 这里并不是我想表述的重点, 就不介绍了.

只要知道, 按照大叔的说法, 这里, 在CreateObjectInternal方法中, 创建了AppDomain, 创建了HostingEnvironment等一些列操作.

后续所有的比如HttpRuntime, HttpContext等, 都是依托于这个AppDomain.

二、主题

经过各种我不知道的内部处理, 非托管代码开始正式调用 ISAPIRuntime 的 ProcessRequest(后面简称为PR方法)了.

(ISPAIRuntime继承了IISPAIRuntime接口,该接口可以和COM进行交互,并且暴露了ProcessRequest接口方法)

不要问我为什么会调用PR方法, 因为我也不知道, 但是真的是这个方法.

public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IISAPIRuntime2, IRegisteredObject
{
// Fields
private static int _isThisAppDomainRemovedFromUnmanagedTable;
private const int WORKER_REQUEST_TYPE_IN_PROC = ;
private const int WORKER_REQUEST_TYPE_IN_PROC_VERSION_2 = ;
private const int WORKER_REQUEST_TYPE_OOP = ; // Methods
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public ISAPIRuntime();
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public void DoGCCollect();
public override object InitializeLifetimeService();
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public int ProcessRequest(IntPtr ecb, int iWRType);
internal static void RemoveThisAppDomainFromUnmanagedTable();
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public void StartProcessing();
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public void StopProcessing();
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
void IISAPIRuntime2.DoGCCollect();
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
int IISAPIRuntime2.ProcessRequest(IntPtr ecb, int iWRType);
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
void IISAPIRuntime2.StartProcessing();
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
void IISAPIRuntime2.StopProcessing();
void IRegisteredObject.Stop(bool immediate);
}

这里有个方法, 看名字就觉得好熟悉, 好吧, 点进去看一下:

GC 一个叫垃圾回收的东东, 好熟悉的名字. OK, 这不是重点, 接下来继续.

[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public int ProcessRequest(IntPtr ecb, int iWRType)
{
IntPtr zero = IntPtr.Zero;
if (iWRType == )
{
zero = ecb;
ecb = UnsafeNativeMethods.GetEcb(zero);
}
ISAPIWorkerRequest wr = null;
try
{
bool useOOP = iWRType == ;
wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
wr.Initialize();
string appPathTranslated = wr.GetAppPathTranslated();
string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
{
HttpRuntime.ProcessRequestNoDemand(wr);
return ;
}
HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged,
      SR.GetString("Hosting_Phys_Path_Changed",
      new object[] { appDomainAppPathInternal, appPathTranslated }));
return ;
}
catch (Exception exception)
{
try
{
WebBaseEvent.RaiseRuntimeError(exception, this);
}
catch
{
}
if ((wr == null) || !(wr.Ecb == IntPtr.Zero))
{
throw;
}
if (zero != IntPtr.Zero)
{
UnsafeNativeMethods.SetDoneWithSessionCalled(zero);
}
if (exception is ThreadAbortException)
{
Thread.ResetAbort();
}
return ;
}
}

第一个注意到的就是该方法的IntPtr类型的参数ecb,ecb是啥?ecb是一个非托管的指针,全称是Execution Control Block,在整个Http Request Processing过程中起着非常重要的作用,我们现在来简单介绍一个ECB。

非托管环境ISAPI对ISAPIRuntime的调用,需要传递一些必须的数据,比如ISAPIRuntime要获取Server Variable的数据,获取通过Post Mehod传回Server的数据;以及最终将Response的内容返回给非托管环境ISAPI,然后呈现给Client用户。一般地ISAPIRuntime不能直接调用ISAPI,所以这里就通过一个对象指针实现对其的调用,这个对象就是ECB,ECB实现了对非托管环境ISAPI的访问。

还有一点特别需要强调的是,ISAPI对ISAPIRutime的调用是异步的,也就是说ISAPI调用ISAPIRutime之后立即返回。这主要是出于Performance和Responsibility考虑的,因为ASP.NET Application天生就是一个多线程的应用,为了具有更好的响应能力,异步操作是最有效的解决方式。但是这里就会有一个问题,我们知道我们对ASP.NET 资源的调用本质上是一个Request/Response的Message Exchange Pattern,异步调用往往意味着ISAPI将Request传递给ISAPIRuntime,将不能得到ISAPIRuntime最终生成的Response,这显然是不能接受的。而ECB解决了这个问题,ISAPI在调用ISAPIRutime的ProcessRequest方法时会将自己对应的ECB的指针传给它,ISAPIRutime不但可以将最终生成的Response返回给ISAPI,还能通过ECB调用ISAPI获得一些所需的数据。

1. CreateWorkerRequest

这个方法还是要看一下的, 有收获哦.

internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP)
{
if (useOOP)
{
EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);
if (EtwTrace.IsTraceEnabled(, ))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false);
}
return new ISAPIWorkerRequestOutOfProc(ecb);
}
int num = UnsafeNativeMethods.EcbGetVersion(ecb) >> 0x10;
if (num >= )
{
EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb);
}
else
{
EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);
}
if (EtwTrace.IsTraceEnabled(, ))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true);
}
if (num >= )
{
return new ISAPIWorkerRequestInProcForIIS7(ecb);
}
if (num == )
{
return new ISAPIWorkerRequestInProcForIIS6(ecb);
}
return new ISAPIWorkerRequestInProc(ecb);
}

通过判断ecb和type类型的具体内容,来决定创建什么类型的WorkerRequest(上述类型的ISPAIWorkerRequest都继承于HttpWorkerRequest),上面的代码可以看出对不同版本的IIS进行了不同的包装,通过其Initialize方法来初始化一些基本的信息(比如:contentType, querystring的长度,filepath等相关信息)。

2. ProcessRequestNoDemand

这个方法, 是真正进入ASP.NET Runtime Pipeline的唯一入口, 传递的参数是上面屏蔽了差异化以后的WorkerRequest对象实例.来看一下这个方法

internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)
{
RequestQueue queue = _theRuntime._requestQueue;
wr.UpdateInitialCounters();
if (queue != null)
{
wr = queue.GetRequestToExecute(wr);
}
if (wr != null)
{
CalculateWaitTimeAndUpdatePerfCounter(wr);
wr.ResetStartTime();
ProcessRequestNow(wr);
}
}

Ok, 接下来, 继续看, PRNow方法, 其实内部调用的是 HttpRuntime的 ProcessRequestInternal 方法.

private void ProcessRequestInternal(HttpWorkerRequest wr)
{
Interlocked.Increment(ref this._activeRequestCount);
if (this._disposingHttpRuntime)
{
try
{
wr.SendStatus(0x1f7, "Server Too Busy");
wr.SendKnownResponseHeader(, "text/html; charset=utf-8");
byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
wr.SendResponseFromMemory(bytes, bytes.Length);
wr.FlushResponse(true);
wr.EndOfRequest();
}
finally
{
Interlocked.Decrement(ref this._activeRequestCount);
}
}
else
{
HttpContext context;
try
{
context = new HttpContext(wr, false);
}
catch
{
try
{
wr.SendStatus(, "Bad Request");
wr.SendKnownResponseHeader(, "text/html; charset=utf-8");
byte[] data = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
wr.SendResponseFromMemory(data, data.Length);
wr.FlushResponse(true);
wr.EndOfRequest();
return;
}
finally
{
Interlocked.Decrement(ref this._activeRequestCount);
}
}
wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context);
HostingEnvironment.IncrementBusyCount();
try
{
try
{
this.EnsureFirstRequestInit(context);
}
catch
{
if (!context.Request.IsDebuggingRequest)
{
throw;
}
}
context.Response.InitResponseWriter();
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
if (applicationInstance == null)
{
throw new HttpException(SR.GetString("Unable_create_app_object"));
}
if (EtwTrace.IsTraceEnabled(, ))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest,
            applicationInstance.GetType().FullName, "Start");
}
if (applicationInstance is IHttpAsyncHandler) //异步处理
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);
}
else //同步处理
{
applicationInstance.ProcessRequest(context);
this.FinishRequest(context.WorkerRequest, context, null);
}
}
catch (Exception exception)
{
context.Response.InitResponseWriter();
this.FinishRequest(wr, context, exception);
}
}
}

最让人开心的, 可能就是看到, 在这个方法中创建了 HttpContext 对象和 HttpApplication 对象.

接下来, 分别看一下这两个对象的创建.

1). HttpContext

internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter)
{
this._timeoutStartTimeUtcTicks = -1L;
this._timeoutTicks = -1L;
this._threadAbortOnTimeout = true;
this.ThreadContextId = new object();
this._wr = wr;
this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
if (initResponseWriter)
{
this._response.InitResponseWriter();
}
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
}

我们又看到了2个惊喜的代码,HttpRequest和HttpResponse的实例化,通过对WorkerRequest和对HttpContext对象this参数的传递,将获取各自需要的信息

2). HttpApplication

这个对象的创建, 是后面那句标红的部分.

IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);

通过HttpApplicationFactory的GetApplicationInstance静态方法,获取我们熟悉的HttpApplication对象实例,由于HttpApplication对象是继承IHttpAsyncHandler,而IHttpAsyncHandler又继承于IHttpHandler,所以上面app的类型是IHttpHandler是没有错的。继续看后面的if (app is IHttpAsyncHandler)代码,就知道了app肯定走这里的分支,然后执行调用asyncHandler.BeginProcessRequest方法了。

至此,HttpRuntime已经正式发挥其无可替代的作用了,也正式通过此对象正式进入了HttpApplication对象的创建以及大家熟知的HttpApplication以后的生命周期了。

转载参考:

  MVC之前的那点事儿

目录已同步

MVC源码解析 - HttpRuntime解析的更多相关文章

  1. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  2. InfluxDB源码目录结构解析

    操作系统 : CentOS7.3.1611_x64 go语言版本:1.8.3 linux/amd64 InfluxDB版本:1.1.0 influxdata主目录结构 [root@localhost ...

  3. Spring源码入门——DefaultBeanNameGenerator解析 转发 https://www.cnblogs.com/jason0529/p/5272265.html

    Spring源码入门——DefaultBeanNameGenerator解析   我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指 ...

  4. 笔记-twisted源码-import reactor解析

    笔记-twisted源码-import reactor解析 1.      twisted源码解析-1 twisted reactor实现原理: 第一步: from twisted.internet ...

  5. 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 百篇博客分析OpenHarmony源码 | v53.02

    百篇博客系列篇.本篇为: v53.xx 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...

  6. v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口 | 百篇博客分析OpenHarmony源码

    子曰:"苟正其身矣,于从政乎何有?不能正其身,如正人何?" <论语>:子路篇 百篇博客系列篇.本篇为: v72.xx 鸿蒙内核源码分析(Shell解析篇) | 应用窥视 ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  10. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

随机推荐

  1. 四种方案:将OpenStack私有云部署到Hadoop MapReduce环境中

    摘要:OpenStack与Hadoop被誉为继Linux之后最有可能获得巨大成功的开源项目.这二者如何结合成为更猛的新方案?业内给出两种答案:Hadoop跑在OpenStack上或OpenStack部 ...

  2. 安装Oracle 9i - 初学者系列 - 学习者系列文章

    Oracle 9i数据库是经典的Oracle版本,就象SQL Server 2000一样.笔者最初使用到的Oracle版本就是Oracle 9i.下面就介绍下Oracle 9i的安装. 1.  下载O ...

  3. jquery插件推荐

    jQuery 是继 prototype 之后又一个优秀的 Javascript 框架.其宗旨是—写更少的代码,做更多的事情.它是轻量级的 js 库(压缩后只有21k) ,这是其它的 js 库所不及的, ...

  4. 你是否应该使用一个Javascript MVC框架?

    你是否应该使用一个Javascript MVC框架?本文摘自smashingmagazine的Journey Through The JavaScript MVC Jungle部分内容,希望对大家有帮 ...

  5. How feedback work for your improvement

    Why generally feedback is the perspective from others for some event. In China there is story,some k ...

  6. SZU:D89 The Settlers of Catan

    Judge Info Memory Limit: 65536KB Case Time Limit: 3000MS Time Limit: 3000MS Judger: Number Only Judg ...

  7. jQuery的入门与简介

    jQuery的入门与简介<思维导图>   <初学者请各位高手指点指点> jQuery是继Prototype之后又一个优秀的JavaScript库,在JavaScript基础上我 ...

  8. 不是技术牛人,如何拿到国内IT巨头的Offer【转】

    转:http://blog.csdn.net/lsldd/article/details/13506263 不久前,byvoid面阿里星计划的面试结果截图泄漏,引起无数IT屌丝的羡慕敬仰.看看这些牛人 ...

  9. [转]Converting a C library to gyp

    Source:http://n8.io/converting-a-c-library-to-gyp/ Converting a C library to gyp Published: 2012.10. ...

  10. 企业架构与建模之ArchiMate的由来和详述(上)

    终于完成了关于企业架构框架理论的总结,谢谢各位看官的支持,能挺过之前过于理论化的叙述而坚持到现在着实不易,笔者也自愧没有实践经验可以分享,希望日后有兴趣的看官能够不吝赐教.在本系列后面的也是最后一个大 ...