首先:我们需要知道几个Ascii码及http请求报文:
10 : 换行键、13:回车键、32:空格。(这几个是http协议重要的分割符)、当然还有其他的解析分割符、如&、:、= 等,这些具体的就先不引入了。
同时至于tomcat是怎样启动、接受报文的在这里也不进行具体什么了:可以看以前的文章(Tomcat源码系列)可能整理的并不是很清晰,当应该能理解一个大概了。
这里我们直接从:Processor的初始化说明吧。
1、我们首先要清晰,在源码中有两个Request:org.apache.coyote.Request、
tomcat接收的http报文都是解析保存在org.apache.coyote.Request。
2、我们来看下coyote.Request对象的初始化。由于Processor是关键request、以及response的,所以其初始化是在Http11Processor(Http11Processor继承AbstractProcessor)
初始是直接new:
public AbstractProcessor(AbstractEndpoint<?> endpoint) {
this(endpoint, new Request(), new Response());
}
public Http11Processor(............ AbstractEndpoint<?> endpoint, .............) {
super(endpoint);
this.httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
this.inputBuffer = new Http11InputBuffer(this.request, maxHttpHeaderSize, rejectIllegalHeaderName, this.httpParser);
this.request.setInputBuffer(this.inputBuffer);
this.outputBuffer = new Http11OutputBuffer(this.response, maxHttpHeaderSize, sendReasonPhrase);
..........................
}
这里有关键的一步要注意(标黑)。这个inputBuffer就是tomcat会将接收到的报文保存在这里,对于报文解析的读取就是操作它。
3、我们看下processor是怎样处理socketWrapper的(其实可以简单将其看为是一个Socket)
processor再调用service:
关键就是这个service了:
在service方法中,解析请求行:
我们现在看下inputBuffer中的内容:
byteBuffer中是没有内容的(lim=0)。
在parseRequestLine中,this.fill(false)方法完成将socket中字节读到buffer中的:
我们看到在read前:
这里wrapper,就是socketWrapper。所以就将socket的字节读到了inputBuffer。
4、 同时我们知道,在http协议的请求行中,有请求方法、url、协议版本,中间以空格(32)分开。
所以解析分了几步:
boolean parseRequestLine(boolean keptAlive) throws IOException {
if (this.parsingRequestLinePhase < 2) {
.........
if (!this.fill(false)) {
.............
}
if (this.parsingRequestLinePhase == 2) {
............
if (chr != 32 && chr != 9) {
.............
this.request.method().setBytes(this.byteBuffer.array(), this.parsingRequestLineStart, pos - this.parsingRequestLineStart);
}
}
this.parsingRequestLinePhase = 3;
break;
}
}
..................
int pos;
if (this.parsingRequestLinePhase == 4) {
...........
if (chr != 32 && chr != 9) {
if (chr != 13 && chr != 10) {
if (chr == 63 && this.parsingRequestLineQPos == -1) {
this.parsingRequestLineQPos = pos;
................
//63 就是问号
if (this.parsingRequestLineQPos >= 0) {
this.request.queryString().setBytes( ...........
this.request.requestURI().setBytes(this.byteBuffer.array() .........
} else {
this.request.requestURI().setBytes(this.byteBuffer.array() ...........;
}
..........
if (this.parsingRequestLinePhase == 6) {
.............
this.request.protocol().setBytes(this.byteBuffer.array(), ...........
} else {
this.request.protocol().setString("");
}
..........
}
}
}
,我们来看解析之后的:coyote.Request
由于还没有解析头部所以headers为空。
5、 之后就是解析头部了:
头部的解析就不具体展开了。不过我们来看下inputBuffer中的byteBuffer。
在未解析前,只解析了请求行:pos为44:
而全面的13/10就代表换了一行了,所以到了头部的位置。
解析头部之后就读到373了:
然后就还是请求体在inputBuffer中了。
但我们可以看到,直到调用适配器方法的时候,并没有读出请求体的内容,(如果请求体中的内容是文件的话、需要在使用getPart()才会其解析):
。
6、然后在适配器中的service就会使用一个新的request了:
需要注意的是,这个reques只是将coyot.Reqeust包装了一下。connector.Request内部的的方法获取数据还是建立在coyot.Reqeust。(后面会进行说明)
之后则是pipeLine的调用了:
同时我们知道,tomcat还会用到一个门面模式。
将connector.Request包装了一下。
现在我们来回答前面那个问题:
通过自己写的,看看回去Parameter的过程:
这里会跳到:RequestFacade
在RequestFacade中会通过其中的reqeust(connector.Request)再去获取
而connector.Request会去获取coyot.Reqesut
这里,会将coyoteRequest中的解析放到connecotr.Request,但数据最终还是从coyoteRequest中获取。
==================================================
顺便提下body、与parameter的解析:
protected void parseParameters() {
...............
if ("multipart/form-data".equals(contentType)) {
this.parseParts(false);
success = true;
return;
}
...................
}