您的位置:首页 > 理论基础 > 计算机网络

InternalInputBuffer处理HTTP请求行-Tomcat源码-我们到底能走多远系列(11)

2012-10-21 22:12 423 查看

我们到底能走多远系列(11)

扯淡:

  最近行情不好吗?跳槽的比较少嘛,哈哈。有些人一直抗拒跳槽,觉得弊端很多:什么业务积累,职务,别人觉得你不可靠啊等等。我就想:这一辈子时间有限,何必为了一颗可以乘凉的树,放弃穿过森林的机会呢?祝在跳槽路上的朋友 顺利!(ps:个人喜欢面试 那种刺激感)

  最爽不顾躺着,最美不过夕阳。秋天的夕阳是一年中最华丽的,各位不要错过哦。

主题:

  在tomcat中,一个http请求,会被送到Http11Processor类,执行这个类的process(Socket theSocket) 处理的传入的Socket,Socket里面装的就是http消息。

  tomcat是如何调用到Http11Processor的process方法的,可以参照:http://blog.csdn.net/woorh/article/details/8017323

  Http11Processor在org.apache.coyote.http11包下。

  Http11Processor的rocess方法中,用inputBuffer.parseRequestLine();调用了解析http消息的请求行。这里的inputBuffer是tomcat自定义的InternalInputBuffer。

  需要了解的是:

  1,org.apache.coyote.Request 是tomcat内部使用用于存放关于request消息的数据结构

  2,org.apache.tomcat.util.buf.MessageBytes 用于存放消息,在org.apache.coyote.Request中大量用于存放解析后的byte字符

  3,org.apache.tomcat.util.buf.ByteChunk 真正用于存放数据的数据结构,存放的是byte[],org.apache.tomcat.util.buf.MessageBytes使用它。

  大流程:

  http消息通过inputBuffer解析后放到Request中,Request把它放到相应的MessageBytes,最后MessageBytes把它存到ByteChunk里。

  以上都可以通过方法调用来完成流程。

  主要关注的是解析的源代码,在查看源代码前需要了解http请求行的结构:可以参照:/article/5117703.html

  阅读前准备

  1,方法中全部的异常时,会调用getString方法,其实就是StringManager的写日志方法,这是tomcat中统一的管理日志的方法。详细的解释在前一篇中已经几时过了:Tomcat StringManager阅读学习

  2,

转义字符意义ASCII码值(十进制)
\a响铃(BEL)007
\b退格(BS) ,将当前位置移到前一列008
\f换页(FF),将当前位置移到下页开头012
\n换行(LF) ,将当前位置移到下一行开头010
\r回车(CR) ,将当前位置移到本行开头013
\t水平制表(HT) (跳到下一个TAB位置)009
\v垂直制表(VT)011
\\代表一个反斜线字符''\'092
源码:

  

public void parseRequestLine()
throws IOException {

int start = 0;

//
// Skipping blank lines
// 忽略空行
//

byte chr = 0;
do {

// Read new bytes if needed
if (pos >= lastValid) {
if (!fill())
throw new EOFException(sm.getString("iib.eof.error"));
}

chr = buf[pos++];

} while ((chr == Constants.CR) || (chr == Constants.LF));

pos--;

// Mark the current buffer position
start = pos;

//
// Reading the method name
// Method name is always US-ASCII
//
// space类似于开关一样,当为false时,查内容,为true时,去除空行时间
boolean space = false;

while (!space) {

// Read new bytes if needed
if (pos >= lastValid) {
if (!fill())
throw new EOFException(sm.getString("iib.eof.error"));
}

// Spec says no CR or LF in method name
if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
throw new IllegalArgumentException(
sm.getString("iib.invalidmethod"));
}
// Spec says single SP but it also says be tolerant of HT
// 查出第一个空格,tab居然也是允许的
if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
space = true;//跳出循环
// 把下标记录下来,这里的method()得到一个Requast的MessageBytes:methodMB
request.method().setBytes(buf, start, pos - start);
}

pos++;

}

// Spec says single SP but also says be tolerant of multiple and/or HT
// 忽略空格后面的空格或者tab,因为是忽略的内容所以不需要什么start
while (space) {
// Read new bytes if needed
if (pos >= lastValid) {
if (!fill())
throw new EOFException(sm.getString("iib.eof.error"));
}
if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
pos++;// 忽略的方式就是继续移动下标
} else {
space = false;
}
}

// Mark the current buffer position
start = pos; // 出现start了,后面肯定是需要记录下标
int end = 0;
int questionPos = -1;

//
// Reading the URI
// 上面是源码的注释,URI是什么?你懂的
//

boolean eol = false;

while (!space) {

// Read new bytes if needed
if (pos >= lastValid) {
if (!fill())
throw new EOFException(sm.getString("iib.eof.error"));
}

// Spec says single SP but it also says be tolerant of HT
// 寻找第二个空格,第一个空格和第二个空格之间就是传说中的URI
if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
space = true;
end = pos;
} else if ((buf[pos] == Constants.CR)
|| (buf[pos] == Constants.LF)) {
// HTTP/0.9 style request
// 为了兼容HTTP/0.9格式
eol = true;
space = true;
end = pos;
} else if ((buf[pos] == Constants.QUESTION) // 遇到‘?’了
&& (questionPos == -1)) {
// 把问号的位置先记录下来
questionPos = pos;
}

pos++;

}
// 把可能包含问号的URI的起始位和结束位记录下来
request.unparsedURI().setBytes(buf, start, end - start);
if (questionPos >= 0) {// 有问号的情况
// 问号位置记录
request.queryString().setBytes(buf, questionPos + 1,
end - questionPos - 1);
// 把URI记录下来
request.requestURI().setBytes(buf, start, questionPos - start);
} else {
request.requestURI().setBytes(buf, start, end - start);
}

// Spec says single SP but also says be tolerant of multiple and/or HT
// 这段算是重复代码吧,就是忽略空格用和tab用的
while (space) {
// Read new bytes if needed
if (pos >= lastValid) {
if (!fill())
throw new EOFException(sm.getString("iib.eof.error"));
}
if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
pos++;
} else {
space = false;
}
}

// Mark the current buffer position
start = pos;
end = 0;

//
// Reading the protocol
// Protocol is always US-ASCII
//
// eol标志位是为了标记是否是HTTP/0.9 style request 前面代码已经提到
// 最后一样:protocol(HTTP/ 1.1或1.0)
while (!eol) {
// Read new bytes if needed
if (pos >= lastValid) {
if (!fill())
throw new EOFException(sm.getString("iib.eof.error"));
}
// 查出  /r/n(CRLF)
if (buf[pos] == Constants.CR) {
end = pos;
} else if (buf[pos] == Constants.LF) {
if (end == 0)
end = pos;
eol = true;
}

pos++;

}
// 至此把head分成三部分,放到Request定义好的MessageBytes中去了
if ((end - start) > 0) {
request.protocol().setBytes(buf, start, end - start);
} else {
request.protocol().setString("");
}

}


总结习点:

  1,利用表示位来控制解析式每个while的功能,上面代码用的是space

  2,不用截断的方式存储需要的内容,而是记录开始和结束的下标。

让我们继续前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不会成功。
共勉
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: