MVC源码解析 - HttpRuntime解析
先看一张图, 从这张图里, 能看到请求是如何从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源码解析 - HttpRuntime解析的更多相关文章
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- InfluxDB源码目录结构解析
操作系统 : CentOS7.3.1611_x64 go语言版本:1.8.3 linux/amd64 InfluxDB版本:1.1.0 influxdata主目录结构 [root@localhost ...
- Spring源码入门——DefaultBeanNameGenerator解析 转发 https://www.cnblogs.com/jason0529/p/5272265.html
Spring源码入门——DefaultBeanNameGenerator解析 我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指 ...
- 笔记-twisted源码-import reactor解析
笔记-twisted源码-import reactor解析 1. twisted源码解析-1 twisted reactor实现原理: 第一步: from twisted.internet ...
- 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 百篇博客分析OpenHarmony源码 | v53.02
百篇博客系列篇.本篇为: v53.xx 鸿蒙内核源码分析(ELF解析篇) | 你要忘了她姐俩你就不是银 | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...
- v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口 | 百篇博客分析OpenHarmony源码
子曰:"苟正其身矣,于从政乎何有?不能正其身,如正人何?" <论语>:子路篇 百篇博客系列篇.本篇为: v72.xx 鸿蒙内核源码分析(Shell解析篇) | 应用窥视 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...
随机推荐
- js实现tooltip动态提示效果(文字版)
页面中经常用到鼠标移动到一个元素上面显示提示的功能,最开始的做法是在下面创建一个div然后动态显示这个div,但是这样需要加很多div,比较麻烦. 针对上面个的需求,这边写了一个tooltip动态提示 ...
- Mysql高级之触发器
原文:Mysql高级之触发器 触发器是一类特殊的事务 ,可以监视某种数据操作(insert/update/delete),并触发相关操作(insert/update/delete). 看以下事件: 完 ...
- 值为NULL的对象指针
相信大家对NULL不会很陌生,NULL 是一个标准规定的宏定义,用来表示空指针常量,当一个指针变量被赋值为NULL时,表示它不再指向任何有效地址,无法在访问任何数据.在VS2012库文件stdio.h ...
- ORACLE抽象数据类型
ORACLE抽象数据类型 *抽象数据类型*/1,抽象数据类型 概念包含一个或多个子类型的数据类型不局限于ORACLE的标准数据类型可以用于其他数据类型中 2,创建抽象数据类型 的语法(必须用NOT F ...
- 基于MEF的插件框架之总体设计
基于MEF的插件框架之总体设计 1.MEF框架简介 MEF的全称是Managed Extensibility Framework(MEF),其是.net4.0的组成部分,在3.5上也可以使用.熟悉ja ...
- select省市联动选择城市 asp.net mvc4
本文在 http://www.cnblogs.com/darrenji/p/3606703.html(感谢博主的分享)基础上加入全国各省市,从文件中读取全国省市县,组成省市联动的选择标签 在Model ...
- Akka入门实例
Akka入门实例 Akka 是一个用 Scala 编写的库,用于简化编写容错的.高可伸缩性的 Java 和 Scala 的 Actor 模型应用. Actor模型并非什么新鲜事物,它由Carl Hew ...
- 利用自定义的AuthenticationFilter实现Basic认证
[ASP.NET MVC] 利用自定义的AuthenticationFilter实现Basic认证 很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前 ...
- .Net 异步随手记(一)
今天要记录的内容摘要是: 什么时候异步代码能“等”在那里,什么时候不会“等” 这两天Coding的时候碰到一个事儿,就是想让异步等在那里结果却直接执行过去了,比如这样: async static vo ...
- What's New in Core Data in iOS 7
What's New in Core Data in iOS 7 该文档主要描述coredata 在ios7的新功能特性. Core Data and iCloud 我们添加改进了对Core Data ...