Request() { // wait for at least some input if (WaitForRequestBytes() == 0) { // 等待客户端请求数据 WriteErrorAndClose(400); // 发送 HTTP 400 错误给客户端 return; }
Request request = new Request(_host, this); request.Process(); }
private int WaitForRequestBytes() { int availBytes = 0;
try { if (_socket.Available == 0) { // poll until there is data _socket.Poll(100000 /* 100ms */, SelectMode.SelectRead); // 等待客户端数据 100ms 时间 if (_socket.Available == 0 && _socket.Connected) _socket.Poll(10000000 /* 10sec */, SelectMode.SelectRead); }
availBytes = _socket.Available; } catch { }
return availBytes; }
[6] Request 在接收到 Connection 的请求后,将从客户端读取请求内容,并按照 HTTP 协议进行分析。因为本文不是做 HTTP 协议的分析工作,所以这部分代码就不详细讨论了。 在 Request.ParseRequestLine 函数分析 HTTP 请求获得请求页面路径后,会调用前面提到过的 Host.IsVirtualPathInApp 函数判断此路径是否在 Web 服务器提供的虚拟路径下级,并且返回此虚拟路径是否指向 ASP.NET 的客户端脚本。如果 Web 请求的虚拟路径以 "/" 结尾,则调用 Request.ProcessDirectoryListingRequest 方法返回列目录的响应;否则调用 HttpRuntime.ProcessRequest 方法完成实际的 ASP.NET 请求处理工作。 HttpRuntime 通过 Request 的基类 HttpWorkerRequest 提供的统一接口,采用 IoC 的策略获取最终页面的所在。与我前面文章中使用的 SimpleWorkerRequest 实现最大不同在于 Request.MapPath 完成了一个较为完整的虚拟目录到物理目录映射机制。 SimpleWorkerRequest.MapPath 实现相对简陋:
以下内容为程序代码:
public override string SimpleWorkerRequest.MapPath(string path) { if (!this._hasRuntimeInfo) { return null; }
string physPath = null; string appPhysPath = this._appPhysPath.Substring(0, (this._appPhysPath.Length - 1)); // 去掉末尾斜杠
if (((path == null) || (path.Length == 0)) || path.Equals("/")) { physPath = appPhysPath; }
if (path.StartsWith(this._appVirtPath)) { physPath = appPhysPath + path.Substring(this._appVirtPath.Length).Replace(''''/'''', ''''\''''); }
InternalSecurityPermissions.PathDiscovery(physPath).Demand();
return physPath; }
Request.MapPath 的实现则相对要完善许多,考虑了很多 SimpleWorkerRequest 无法处理的情况,使得 Request 的适应性更强。
以下内容为程序代码:
public override String Request.MapPath(String path) { String mappedPath = String.Empty;
if (path == null || path.Length == 0 || path.Equals("/")) { // asking for the site root if (_host.VirtualPath == "/") { // app at the site root mappedPath = _host.PhysicalPath; } else { // unknown site root - don''''t point to app root to avoid double config inclusion mappedPath = Environment.SystemDirectory; } } else if (_host.IsVirtualPathAppPath(path)) { // application path mappedPath = _host.PhysicalPath; } else if (_host.IsVirtualPathInApp(path)) { // inside app but not the app path itself mappedPath = _host.PhysicalPath + path.Substring(_host.NormalizedVirtualPath.Length); } else { // outside of app -- make relative to app path if (path.StartsWith("/")) mappedPath = _host.PhysicalPath + path.Substring(1); else mappedPath = _host.PhysicalPath + path; }
mappedPath = mappedPath.Replace(''''/'''', ''''\'''');
if (mappedPath.EndsWith("\") && !mappedPath.EndsWith(":\")) mappedPath = mappedPath.Substring(0, mappedPath.Length-1);
return mappedPath; }
关于 Cassini 的进一步讨论,可以参考 www.asp.net 上的讨论专版。
[7] 在 HttRuntime 完成具体的 ASP.NET 页面处理工作后,会通过 Request.SendResponseFromXXX 系列函数,将页面结果返回给客户端。
虽然 SimpleWorkerRequest.MapPath 方法实现简单,但理论上完全可以处理多级目录的情况。之所以在使用 SimpleWorkerRequest 时,无法处理嵌套目录,是因为 SimpleWorkerRequest 在构造函数中错误地分解了请求的页面所在虚拟目录等信息。 SimpleWorkerRequest 的两个构造函数,在将请求页面虚拟路径(如"/help/about.aspx")保存后,都调用了 ExtractPagePathInfo 方法对页面路径做进一步的分解工作。
以下内容为程序代码:
private void SimpleWorkerRequest.ExtractPagePathInfo() { int idx = this._page.IndexOf(''''/''''); if (idx >= 0) { this._pathInfo = this._page.Substring(idx); this._page = this._page.Substring(0, idx); } }
this._pathInfo 是为实现 HttpWorkerRequest.GetPathInfo 提供的存储字段。而 GetPathInfo 将返回 URL 中在页面后的路径信息,例如对 "path/virdir/page.html/tail" 将返回 "/tail"。早期的许多 HTTP 客户端程序,如 Delphi 中 WebAction 的分发,都利用了这个路径信息的特性,在 Web 页面或 ISAPI 一级之后,再次进行请求分发。但因为 SimpleWorkerRequest 实现上或者设计上的限制,导致在处理 PathInfo 时会将 "/help/about.aspx" 类似的多级 url 错误切断。最终返回给 HttpRuntime 的 this._path 将变成空字符串,而 this._pathInfo 被设置为 "/help/about.aspx",而单级路径如 "about.aspx" 不受影响。 知道了这个原理后,就可以对 SimpleWorkerRequest 稍作修改,重载受到 ExtractPagePathInfo 影响的几个方法,即可完成对多级目录结构下页面的支持。如果需要进一步的映射支持,如同时支持多个虚拟子目录,可以参照 Cassini 的 Request 实现 MapPath 等方法。
以下内容为程序代码:
public class Request : SimpleWorkerRequest { private string _appPhysPath; private string _appVirtPath;
private string _page; private string _pathInfo;
public Request(string page, string query, TextWriter output) : base(page, query, output) { this._appPhysPath = Thread.GetDomain().GetData(".appPath").ToString(); this._appVirtPath = Thread.GetDomain().GetData(".hostingVirtualPath").ToString();
this._page = page;
// TODO: 从 page 中进一步解析 Path Info }
public override string GetPathInfo() { if (this._pathInfo == null) { return string.Empty; } return this._pathInfo; }
private string GetPathInternal(bool includePathInfo) { string path = (_appVirtPath.Equals("/") ? _page : _appVirtPath + _page);
if (includePathInfo && (_pathInfo != null)) { return path + this._pathInfo; } else { return path; } }
public override string GetUriPath()
上一页 [1] [2] [3] 下一页 |