分享
 
 
 

CASSINI源代码分析(4)

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

因为connection对象仅仅跟host对象相关,且处理一个套接字,所以其数据成员仅有:

private Host _host; //指向宿主对象

private Socket _socket; //当前套接字

我们知道host调用且仅了conn.ProcessOneRequest();方法,所以我们首先要找到此方法(coneetion很多方法,我们先看看主要的):

public void ProcessOneRequest() { // wait for at least some input

if (WaitForRequestBytes() == 0) {

WriteErrorAndClose(400);

return;

}

Request request = new Request(_host, this);

request.Process();

}

从代码来看,此过程首先要确保socket有数据读入,如果发生无法读取,就向socket写入一个400错误;如果有数据读入,那么构造一个requese对象,调用request的Process方法(呵呵,麻烦事大家总是一层层甩给别人J)。

还是继续分析Connection对象, WaitForRequestBytes()实际上是单独线程来读取socket,如果socket连接但是没有数据流,就至多等待10秒。

WriteErrorAndClose(int);函数调用WriteErrorAndClose(int , string)

{

String body = Messages.FormatErrorMessageBody(statusCode, _host.VirtualPath);

if (message != null && message.Length > 0)

body += "\r\n<!--\r\n" + message + "\r\n-->";

WriteEntireResponseFromString(statusCode, null, body, false);

}

WriteEntireResponseFromString()函数根据状态码构造返回的http数据流,并写回到客户端。实际上体现了对http协议的具体实现。我们暂时放下,追踪看看Request对象。

internal class Request : SimpleWorkerRequest {。。。。。。}继承自.net,根据MSDN介绍:HttpWorkerRequest 的这种简单实现提供请求 URL 和查询字符串,并将输出的正文捕获到 TextWriter 中。若要实现更为丰富的功能(如提供已发送的内容和标头以及捕获以二进制数据表示的响应标头或响应正文),则应扩展 SimpleWorkerRequest 并重写适当的 HttpWorkerRequest 方法。

还是从Process函数入手看看(代码较长):

{

ReadAllHeaders(); //阅读处所有的http请求头

if (_headerBytes == null || _endHeadersOffset < 0 ||

_headerByteStrings == null || _headerByteStrings.Count == 0) {

_conn.WriteErrorAndClose(400);

return; //如果读取http头错误就返回

}

ParseRequestLine(); //处理request行输入

// Check for bad path

if (IsBadPath()) { //防止用户请求bad路径

_conn.WriteErrorAndClose(400);

return;

}

// Limit to local requests only

if (!_conn.IsLocal) {

_conn.WriteErrorAndClose(403);

return;

}

// Check if the path is not well formed or is not for the current app

bool isClientScriptPath = false;

String clientScript = null;

if (!_host.IsVirtualPathInApp(_path, out isClientScriptPath, out clientScript)) {

_conn.WriteErrorAndClose(404); //检验url请求是否属于应用程序路径范围内,如果不是,报404错误。

return;

}

ParseHeaders(); //解析http请求头

ParsePostedContent(); //解析post方法的内容

if (_verb == "POST" && _contentLength > 0 && _preloadedContentLength < _contentLength) { //如果是post方法,需要等待post数据完成才继续那么调用conn的等待方法Write100Continue直到post完成

_conn.Write100Continue();

}

// special case for client script

if (isClientScriptPath) { //如果请求的是脚本路径,那么直接读取文件(也就是.js文件,按照文本文件来看待)

_conn.WriteEntireResponseFromFile(_host.PhysicalClientScriptPath + clientScript, false);

return;

}

// special case for directory listing

if (ProcessDirectoryListingRequest()) { //如果是请求目录list,则处理后返回

return;

}

PrepareResponse(); //准备响应内容

// Hand the processing over to HttpRuntime

HttpRuntime.ProcessRequest(this); //通过HttpRuntime的方法执行asp.net的内容,驱动所有 ASP.NET Web 处理执行。

}

针对该函数细节,逐个分析以下函数:

ReadAllHeaders

ParseRequestLine

ParsePostedContent

ProcessDirectoryListingRequest

PrepareResponse

因为他们处理一次http request。

private void ReadAllHeaders() {

_headerBytes = null;

do {

if (!TryReadAllHeaders())

break; // something bad happened

}

while (_endHeadersOffset < 0); // found \r\n\r\n

} 该函数不断调用TryReadAllHeaders,仔细看看这个TryReadAllHeaders:

private bool TryReadAllHeaders() {

// read the first packet (up to 32K)

byte[] headerBytes = _conn.ReadRequestBytes(maxHeaderBytes); //从connection读取最大数据32*1024字节。

if (headerBytes == null || headerBytes.Length == 0)

return false; //如果读取数据失败,返回错误,调用此函数者应当检查数据读取是否完整

if (_headerBytes != null) { // previous partial read 以下将当前读取的数据累加

int len = headerBytes.Length + _headerBytes.Length;

if (len > maxHeaderBytes)

return false;

byte[] bytes = new byte[len];

//注意调用了快速的Buffer.BlockCopy方法,不过我认为可以更好的处理读取数据问题,因为一再的产生byte数组显然不是很好的方法。

Buffer.BlockCopy(_headerBytes, 0, bytes, 0, _headerBytes.Length);

Buffer.BlockCopy(headerBytes, 0, bytes, _headerBytes.Length, headerBytes.Length);

_headerBytes = bytes;

}

else {

_headerBytes = headerBytes;

}

// start parsing 下面准备解析请求行

_startHeadersOffset = -1;

_endHeadersOffset = -1;

_headerByteStrings = new ArrayList();

// find the end of headers ByteParser是自定义的工具类,此时我们只要知道是帮助字节数组转换方便得到行,ByteString也是一个工具类,帮助将字节数组转换为字符串

ByteParser parser = new ByteParser(_headerBytes);

for (;;) {

ByteString line = parser.ReadLine();

if (line == null)

break;

if (_startHeadersOffset < 0) {

_startHeadersOffset = parser.CurrentOffset;

}

if (line.IsEmpty) {

_endHeadersOffset = parser.CurrentOffset;

break;

}

_headerByteStrings.Add(line);

}

return true;

}

如何处理分解行呢?

private void ParseRequestLine() {

ByteString requestLine = (ByteString)_headerByteStrings[0];

ByteString[] elems = requestLine.Split(' '); //我们知道每一个header同header值之间都有一个必然的空格,例如cassini返回的http响应的头:

HTTP/1.1 404 Not Found

//判断header是否读取正确,一般请求头第一行应该是例如:GET /pub/WWW/ HTTP/1.1

if (elems == null || elems.Length < 2 || elems.Length > 3) {

return;

}

_verb = elems[0].GetString(); //读取 http 请求方法

ByteString urlBytes = elems[1];

_url = urlBytes.GetString(); //获取请求的url

if (elems.Length == 3) //确定http请求的协议版本

_prot = elems[2].GetString(); //目前仅有HTTP/1.1或者HTTP/1.0

else

_prot = "HTTP/1.0";

// query string

int iqs = urlBytes.IndexOf('?'); //请求的参数获取 字节数组表示

if (iqs > 0)

_queryStringBytes = urlBytes.Substring(iqs+1).GetBytes();

else

_queryStringBytes = new byte[0];

iqs = _url.IndexOf('?'); //取得path 和 参数的字符串表示

if (iqs > 0) {

_path = _url.Substring(0, iqs);

_queryString = _url.Substring(iqs+1);

}

else {

_path = _url;

_queryStringBytes = new byte[0];

}

// url-decode path 开始url解码,这个MS之前犯的著名URL解码错误就在此处了

if (_path.IndexOf('%') >= 0) {

_path = HttpUtility.UrlDecode(_path); //调用.net的工具方法

}

// path info 以下获取path

int lastDot = _path.LastIndexOf('.');

int lastSlh = _path.LastIndexOf('/');

if (lastDot >= 0 && lastSlh >= 0 && lastDot < lastSlh) {

int ipi = _path.IndexOf('/', lastDot);

_filePath = _path.Substring(0, ipi);

_pathInfo = _path.Substring(ipi);

}

else {

_filePath = _path;

_pathInfo = String.Empty;

}

_pathTranslated = MapPath(_filePath); //映射路径,将文件映射到具体的磁盘路径

}

处理完http header后,开始处理http的请求正文,看看ParsePostedContent

private void ParsePostedContent() {

_contentLength = 0;

_preloadedContentLength = 0;

String contentLengthValue = _knownRequestHeaders[HttpWorkerRequest.HeaderContentLength]; //察看头部中的定义的长度

if (contentLengthValue != null) {

try {

_contentLength = Int32.Parse(contentLengthValue);

}

catch {

}

}

//以下检查各个长度数据是否异常

if (_headerBytes.Length > _endHeadersOffset) {

_preloadedContentLength = _headerBytes.Length - _endHeadersOffset;

if (_preloadedContentLength > _contentLength && _contentLength > 0)

_preloadedContentLength = _contentLength; // don't read more than the content-length 注意不要读取过多的数据

_preloadedContent = new byte[_preloadedContentLength];

Buffer.BlockCopy(_headerBytes, _endHeadersOffset, _preloadedContent, 0, _preloadedContentLength); //拷贝数据

}

}

以上将http请求的content数据字节拷贝到_preloadedContent成员变量中去。

接下来的ProcessDirectoryListingRequest处理不带文件名的请求,也就是直接请求浏览某个目录。略。

处理完毕后,准备构造响应数据,看看PrepareResponse

_headersSent = false; //准备回写到connection中去

_responseStatus = 200;

_responseHeadersBuilder = new StringBuilder();

_responseBodyBytes = new ArrayList();

其他的,我们应当注意

HttpRuntime.ProcessRequest(this); 隐含的调用关系。HttpRuntime.ProcessRequest会在需要回写数据的时候调用相关的函数,这些函数被cassini注释。

我们要明白这些重载的函数会在asp.net处理过程中被调用。

///////////////////////////////////////////////////////////////////////////////////////////////

//

// Implementation of HttpWorkerRequest

//

///////////////////////////////////////////////////////////////////////////////////////////////

public override String GetUriPath() { //返回请求的 URI 的虚拟路径。

return _path;

}

public override String GetQueryString() { //返回请求 URL 中指定的查询字符串。

return _queryString;

}

public override byte[] GetQueryStringRawBytes() { //在派生类中被重写时,以字节数组的形式返回响应查询字符串。

return _queryStringBytes;

}

public override String GetRawUrl() { //返回附加了查询字符串的请求标头中包含的 URL 路径。

return _url;

}

public override String GetHttpVerbName() { //返回请求标头的指定成员。

return _verb;

}

public override String GetHttpVersion() { //提供对请求的 HTTP 版本(如“HTTP/1.1”)的访问。

return _prot;

}

public override String GetRemoteAddress() {//提供对请求标头的指定成员的访问。

return _conn.RemoteIP;

}

public override int GetRemotePort() {//提供对请求标头的指定成员的访问。

return 0;

}

public override String GetLocalAddress() {//

return _conn.LocalIP;

}

public override int GetLocalPort() {

return _host.Port;

}

public override String GetFilePath() {//在派生类中被重写时,返回所请求的 URI 的物理路径。

return _filePath;

}

public override String GetFilePathTranslated() {//返回请求的 URI 的物理文件路径(并将其从虚拟路径翻译成物理路径:例如,从“/proj1/page.aspx”翻译成“c:\dir\page.aspx”)

return _pathTranslated;

}

public override String GetPathInfo() {//返回具有 URL 扩展的资源的其他路径信息。即对于路径/virdir/page.html/tail,GetPathInfo 值为/tail。

return _pathInfo;

}

public override String GetAppPath() {//返回当前正在执行的服务器应用程序的虚拟路径。

return _host.VirtualPath;

}

public override String GetAppPathTranslated() {//返回当前正在执行的服务器应用程序的 UNC 翻译路径。

return _host.PhysicalPath;

}

public override byte[] GetPreloadedEntityBody() {//返回 HTTP 请求正文已被读取的部分。

return _preloadedContent;

}

public override bool IsEntireEntityBodyIsPreloaded() {//返回一个值,该值指示是否所有请求数据都可用,以及是否不需要对客户端进行进一步读取。

return (_contentLength == _preloadedContentLength);

}

public override int ReadEntityBody(byte[] buffer, int size) {//读取客户端的请求数据(在尚未预加载时)。

int bytesRead = 0;

byte[] bytes = _conn.ReadRequestBytes(size);

if (bytes != null && bytes.Length > 0) {

bytesRead = bytes.Length;

Buffer.BlockCopy(bytes, 0, buffer, 0, bytesRead);

}

return bytesRead;

}

public override String GetKnownRequestHeader(int index) {//返回与指定的索引相对应的标准 HTTP 请求标头。

return _knownRequestHeaders[index];

}

public override String GetUnknownRequestHeader(String name) {//返回非标准的 HTTP 请求标头值。指定了名称

int n = _unknownRequestHeaders.Length;

for (int i = 0; i < n; i++) {

if (String.Compare(name, _unknownRequestHeaders[i][0], true, CultureInfo.InvariantCulture) == 0)

return _unknownRequestHeaders[i][1];

}

return null;

}

public override String[][] GetUnknownRequestHeaders() {//获取所有非标准的 HTTP 标头的名称-值对。

return _unknownRequestHeaders;

}

public override String GetServerVariable(String name) {//从与请求关联的服务器变量词典返回单个服务器变量。

String s = String.Empty;

switch (name) {

case "ALL_RAW":

s = _allRawHeaders;

break;

case "SERVER_PROTOCOL":

s = _prot;

break;

// more needed?

}

return s;

}

public override String 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;

}

public override void SendStatus(int statusCode, String statusDescription) {//指定响应的 HTTP 状态代码和状态说明;例如 SendStatus(200, "Ok")。

_responseStatus = statusCode;

}

public override void SendKnownResponseHeader(int index, String value) {//将标准 HTTP 标头添加到响应。

if (_headersSent)

return;

switch (index) {

case HttpWorkerRequest.HeaderServer:

case HttpWorkerRequest.HeaderDate:

case HttpWorkerRequest.HeaderConnection:

// ignore these

return;

// special case headers for static file responses

case HttpWorkerRequest.HeaderAcceptRanges:

if (value == "bytes") {

_specialCaseStaticFileHeaders = true;

return;

}

break;

case HttpWorkerRequest.HeaderExpires:

case HttpWorkerRequest.HeaderLastModified:

if (_specialCaseStaticFileHeaders)

return;

break;

}

_responseHeadersBuilder.Append(GetKnownResponseHeaderName(index));

_responseHeadersBuilder.Append(": ");

_responseHeadersBuilder.Append(value);

_responseHeadersBuilder.Append("\r\n");

}

public override void SendUnknownResponseHeader(String name, String value) {//将非标准 HTTP 标头添加到响应。

if (_headersSent)

return;

_responseHeadersBuilder.Append(name);

_responseHeadersBuilder.Append(": ");

_responseHeadersBuilder.Append(value);

_responseHeadersBuilder.Append("\r\n");

}

public override void SendCalculatedContentLength(int contentLength) {//将 Content-Length HTTP 标头添加到响应。

if (!_headersSent) {

_responseHeadersBuilder.Append("Content-Length: ");

_responseHeadersBuilder.Append(contentLength.ToString());

_responseHeadersBuilder.Append("\r\n");

}

}

public override bool HeadersSent() {//返回一个值,该值指示是否已为当前的请求将 HTTP 响应标头发送到客户端。

return _headersSent;

}

public override bool IsClientConnected() {//返回一个值,该值指示客户端连接是否仍处于活动状态。

return _conn.Connected;

}

public override void CloseConnection() {//终止与客户端的连接。

_conn.Close();

}

public override void SendResponseFromMemory(byte[] data, int length) {//将内存块的内容添加到响应。

if (length > 0) {

byte[] bytes = new byte[length];

Buffer.BlockCopy(data, 0, bytes, 0, length);

_responseBodyBytes.Add(bytes);

}

}

public override void SendResponseFromFile(String filename, long offset, long length) {//将文件的内容添加到响应。

if (length == 0)

return;

FileStream f = null;

try {

f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);

SendResponseFromFileStream(f, offset, length);

}

finally {

if (f != null)

f.Close();

}

}

public override void SendResponseFromFile(IntPtr handle, long offset, long length) {//将文件的内容添加到响应。注意这个方法是多态的

if (length == 0)

return;

FileStream f = null;

try {

f = new FileStream(handle, FileAccess.Read, false);

SendResponseFromFileStream(f, offset, length);

}

finally {

if (f != null)

f.Close();

}

}

private void SendResponseFromFileStream(FileStream f, long offset, long length) {//这个可不是重载的,看清楚了J

const int maxChunkLength = 64*1024; //64K作为传输块

long fileSize = f.Length;

if (length == -1)

length = fileSize - offset;

if (length == 0 || offset < 0 || length > fileSize - offset)

return;

if (offset > 0)

f.Seek(offset, SeekOrigin.Begin);

if (length <= maxChunkLength) {

byte[] fileBytes = new byte[(int)length];

int bytesRead = f.Read(fileBytes, 0, (int)length);

SendResponseFromMemory(fileBytes, bytesRead);

}

else {

byte[] chunk = new byte[maxChunkLength];

int bytesRemaining = (int)length;

while (bytesRemaining > 0) {

int bytesToRead = (bytesRemaining < maxChunkLength) ? bytesRemaining : maxChunkLength;

int bytesRead = f.Read(chunk, 0, bytesToRead);

SendResponseFromMemory(chunk, bytesRead);

bytesRemaining -= bytesRead;

// flush to release keep memory

if (bytesRemaining > 0 && bytesRead > 0)

FlushResponse(false);

}

}

}

public override void FlushResponse(bool finalFlush) {//将所有挂起的响应数据发送到客户端。

if (!_headersSent) {

_conn.WriteHeaders(_responseStatus, _responseHeadersBuilder.ToString());

_headersSent = true;

}

for (int i = 0; i < _responseBodyBytes.Count; i++) {

byte[] bytes = (byte[])_responseBodyBytes[i];

_conn.WriteBody(bytes, 0, bytes.Length);

}

_responseBodyBytes = new ArrayList();

if (finalFlush) {

_conn.Close();

}

}

public override void EndOfRequest() {//由运行库使用以通知 HttpWorkerRequest 当前请求的请求处理已完成。

// empty method

}

通过以上重载,我们实现了具体的http请求的数据获取、头分析,内容分析(如果是post方法)、querystring参数分析、处理路径映射、传输asp.net执行请求到运行库,同时构造http 响应头和相应数据。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有