HTTP 管道
ASP.NET ISAPI 扩展启动辅助进程后,它将传递部分命令行参数。辅助进程使用这些参数来执行加载 CLR 前需要执行的任务。传递的值包括:COM 和 DCOM 安全性所要求的身份验证等级、可以使用的命名管道的数量和 IIS 进程标识。命名管道的名称是使用 IIS 进程标识和允许的管道数随机生成的。辅助进程不接收可用管道的名称,但可以接收识别管道名称所需的信息。
COM 和 DCOM 安全性与 Microsoft? .NET Framework 有何关系?实际上,CLR 是作为 COM 对象提供的。更准确地说,CLR 本身不是由 COM 代码构成的,但是指向 CLR 的接口却是一个 COM 对象。因此,辅助进程加载 CLR 的方式与加载 COM 对象的方式相同。
当 ASPX 请求遇到 IIS 时,Web 服务器将根据选择的身份验证模型(匿名、Windows、Basic 或 Digest)来分配一个令牌。当辅助进程收到要处理的请求时,令牌被传递到辅助进程。请求由辅助进程中的线程获取。该线程从最初获取传入请求的 IIS 线程继承身份令牌。在 aspnet_wp.exe 中,负责处理请求的实际帐户取决于在特殊的 ASP.NET 应用程序中是如何配置模拟的。如果模拟被禁用(默认设置),则线程将在辅助进程的帐户下运行。默认情况下,该帐户在 ASP.NET 进程模型中为 ASPNET,在 IIS 6 进程模型中为 NETWORKSERVICE。这两个帐户都是“弱”帐户,提供的功能比较有限,可以有效抵挡回复性攻击 (Revert-to-self Attack)。(回复性攻击是指将模拟的客户端的安全性令牌回复到父进程令牌。为辅助进程分配弱帐户可以挫败此类攻击。)
高度概括起来,ASP.NET 辅助进程完成的一项主要任务就是将请求交给一系列称为的 HTTP 管道的托管对象。要激活 HTTP 管道,可以创建一个 HttpRuntime 类的新实例,然后调用其 ProcessRequest 方法。如前所述,ASP.NET 中始终只运行一个辅助进程(除非启用了 Web Garden 模型),该进程在独立的 AppDomain 中管理所有的 Web 应用程序。每个 AppDomain 都有自己的 HttpRuntime 类实例,即管道中的输入点。HttpRuntime 对象初始化一系列有助于实现请求的内部对象。Helper 对象包括缓存管理器(Cache 对象)和内部文件系统监视器(用于检测构成应用程序的源文件的更改)。HttpRuntime 为请求创建上下文,并用与请求相关的 HTTP 信息填充上下文。上下文用 HttpContext 类的实例来表示。
另一个在 HTTP 运行时的设置初期创建的 Helper 对象是文本书写器,用于包含浏览器的响应文本。文本书写器是 HttpWriter 类的实例,此对象对页面代码以编程方式发送的文本进行缓存。HTTP 运行时被初始化后,它将查找实现请求的应用程序对象。应用程序对象是 HttpApplication 类的实例,该类就是 global.asax 文件背后的类。global.asax 在编程时是可选的,但在构建结构时是必需的。因此,如果应用程序中没有构建类,则必须使用默认对象。ASP.NET 运行时包括几个中间工厂类,可以用来查找并返回有效的 Handler 对象以处理请求。整个过程中用到的第一个工厂类是 HttpApplicationFactory。它的主要任务是使用 URL 信息来查找 URL 虚拟目录和汇集的 HttpApplication 对象之间的匹配关系。
应用程序工厂类的行为可以概括为以下几点:
工厂类维护 HttpApplication 对象池,并使用它们来处理应用程序的请求。池的寿命与应用程序的寿命相同。
应用程序的第一个请求到达时,工厂类提取有关应用程序类型的信息(global.asax 类)、设置用于监视更改的文件、创建应用程序状态并触发 Application_OnStart 事件。
工厂类从池中获取一个 HttpApplication 实例,并将要处理的请求放入实例中。如果没有可用的对象,则创建一个新的 HttpApplication 对象。要创建 HttpApplication 对象,需要先完成 global.asax 应用程序文件的编译。
HttpApplication 开始处理请求,并且只能在完成这个请求后才能处理新的请求。如果收到来自同一资源的新请求,则由池中的其他对象来处理。
应用程序对象允许所有注册的 HTTP 模块对请求进行预处理,并找出最适合处理请求的处理程序类型。这通过查找请求的 URL 的扩展和配置文件中的信息来完成。
HTTP 处理程序是一些实现 IHttpHandler 接口的类。.NET Framework 为常见的资源类型提供了一些预定义的处理程序,包括 ASPX 页面和 Web 服务。machine.config 文件中的 <httpHandlers> 部分定义了 HttpApplication 对象必须实例化才能处理特定类型资源的请求的类名。如果 Helper 类是一个处理程序工厂,GetHandler 方法将确定要使用的处理程序类型。这时,将从一组类似的对象中获取适当类型的处理程序,并对其进行配置以处理请求。
IHttpHandler 接口提供了两个方法:IsReusable 和 ProcessRequest。前者将返回一个布尔值,表示处理程序是否可以被汇集。(大多数预定义的处理程序都是汇集的,但是您可以自行定义每次都需要新实例的处理程序。)ProcessRequest 方法包含处理特定类型资源所需的所有逻辑。例如,ASPX 页面的处理程序基于以下伪代码:
private void ProcessRequest() { // 确定请求是否是回发 (postback) IsPostBack = DeterminePostBackMode();
// 触发 ASPX 源代码的 Page_Init 事件 PageInit();
// 加载 ViewState,处理已发送的值。 if (IsPostBack) { LoadPageViewState(); ProcessPostData(); }
// 触发 ASPX 源代码的 Page_Load 事件 PageLoad();
// 1) 再次处理已发送的值(当 // 动态创建控件时) // 2) 将属性更改的服务器端事件提升为输入驱动的 // 控件(即复选框的状态改变) // 3) 执行与回发事件相关的所有代码 if (IsPostBack) { ProcessPostDataSecondTry(); RaiseChangedEvents(); RaisePostBackEvent(); }
// 触发 ASPX 源代码的 Page_PreRender 事件 PreRender();
// 将控件的当前状态保存到 ViewState 中 SavePageViewState();
// 将页面内容呈现给 HTML RenderControl(CreateHtmlTextWriter(Response.Output)); } |
无论调用的资源类型如何,基于 HTTP 处理程序的模型是相同的。唯一随资源类型变化而变化的元素是处理程序。HttpApplication 对象负责查找应该使用哪种处理程序来处理请求。HttpApplication 对象还负责检测对动态创建的、表示资源的程序集(如 .aspx 页面或 .asmx Web 服务)所进行的更改。如果检测到更改,应用程序对象将确保编译并加载所请求的资源的最新来源。