Tomcat处理请求过程

处理请求流程

数据在操作系统层面是通过TCP协议来传输通过Socket来实现,Tomcat是实现的HTTP等应用层的协议,通过Socket去获取解析数据,解析出请求行请求头请求体,从而生成具体的HttpServletRequest对象;

Tomcat接收到请求后,首先会判断该请求域名找到对应的Host,再根据请求信息找到要访问的应用即Context,Context拿到请求后,会根据请求信息找到对应的Servelet

Tomcat中有一个Connector组件,其专门用来接收Socket连接,在Connector内部有个组件叫ProtocolHandler,其有好几种实现Http11ProtocolHttp11NioProtocolHttp11AprProtocol,在Connector中通过配置的协议去处理相关的请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Connector extends LifecycleMBeanBase  {
public void setProtocol(String protocol) {
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol"); // BIO
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol); // org.apache.coyote.http11NIOProxot
}
}
}
}

Http11Protocol为例,在Http11Protocol中有个JIoEndpoint组件,实例化该实例时回去创建JIoEndpoint

1
2
3
4
5
6
7
8
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}

最终请求在JIoEndpoint通过Acceptor侦听传入TCP/IP连接并将它们移交给适当处理器的后台线程处理,非关键代码被移除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
while (running) {
try {
//达到了最大连接数限制则等待
countUpOrAwaitConnection();
Socket socket = null; // bio,nio
try {
// 此处是阻塞的,那么running属性就算已经被改成false,那么怎么进入到下一次循环呢?
socket = serverSocketFactory.acceptSocket(serverSocket);//
System.out.println("接收到了一个socket连接");
} catch (IOException ioe) {
countDownConnection();
errorDelay = handleExceptionWithDelay(errorDelay);
throw ioe;
}
errorDelay = 0;
// 如果Endpoint正在运行并且没有被暂停,那么就处理该socket
if (running && !paused && setSocketOptions(socket)) {
// socket被正常的交给了线程池,processSocket就会返回true
// 如果没有被交给线程池或者中途Endpoint被停止了,则返回false,返回false则关闭该socket
if (!processSocket(socket)) {
countDownConnection();
closeSocket(socket);
}
} else {
countDownConnection();
closeSocket(socket);
}
}
}
state = AcceptorState.ENDED;
}
}

通过processSocket将一个HTTP请求放到线程中处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
protected boolean processSocket(Socket socket) {
try {
SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
wrapper.setSecure(isSSLEnabled());
if (!running) {
return false;
}
// bio, 一个socket连接对应一个线程
getExecutor().execute(new SocketProcessor(wrapper));
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
protected class SocketProcessor implements Runnable {
protected SocketWrapper<Socket> socket = null;
protected SocketStatus status = null;
public SocketProcessor(SocketWrapper<Socket> socket) {
if (socket==null) throw new NullPointerException();
this.socket = socket;
}
@Override
public void run() {
boolean launch = false;
synchronized (socket) {
try {
SocketState state = SocketState.OPEN;
try {
serverSocketFactory.handshake(socket.getSocket());
}
// 当前socket没有关闭则处理socket
if ((state != SocketState.CLOSED)) {
// SocketState是Tomcat定义的一个状态,这个状态需要处理一下socket才能确定,因为跟客户端,跟具体的请求信息有关系
if (status == null) {
state = handler.process(socket, SocketStatus.OPEN_READ);
} else {
// status表示应该读数据还是应该写数据,state表示处理完socket后socket的状态
state = handler.process(socket,status);
}
}
// 如果Socket的状态是被关闭,那么就减掉连接数并关闭socket,那么Socket的状态是在什么时候被关闭的?
if (state == SocketState.CLOSED) {
countDownConnection();
try {
socket.getSocket().close();
}
} else if (state == SocketState.OPEN || state == SocketState.UPGRADING || state == SocketState.UPGRADING_TOMCAT || state == SocketState.UPGRADED){
socket.setKeptAlive(true);
socket.access();
launch = true;
} else if (state == SocketState.LONG) {
// socket不会关闭,但是当前线程会执行结束
socket.access();
waitingRequests.add(socket);
}
}
}
socket = null;
}
}

最终调用处理请求的核心代码,该方法也是Tomcat实现长链接的核心逻辑地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
public SocketState process(SocketWrapper<S> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE); // 设置请求状态为解析状态
setSocketWrapper(socketWrapper);
getInputBuffer().init(socketWrapper, endpoint); // 将socket的InputStream与InternalInputBuffer进行绑定
getOutputBuffer().init(socketWrapper, endpoint); // 将socket的OutputStream与InternalOutputBuffer进行绑定
keepAlive = true;
comet = false;
openSocket = false;
sendfileInProgress = false;
readComplete = true;
// NioEndpoint返回true, Bio返回false
if (endpoint.getUsePolling()) {
keptAlive = false;
} else {
keptAlive = socketWrapper.isKeptAlive();
}
// 如果当前活跃的线程数占线程池最大线程数的比例大于75%,那么则关闭KeepAlive,不再支持长连接
if (disableKeepAlive()) {
socketWrapper.setKeepAliveLeft(0);
}
// keepAlive默认为true,它的值会从请求中读取
while (!getErrorState().isError() && keepAlive && !comet && !isAsync() && upgradeInbound == null && httpUpgradeHandler == null && !endpoint.isPaused()) {
// keepAlive如果为true,接下来需要从socket中不停的获取http请求
try {
// 第一次从socket中读取数据,并设置socket的读取数据的超时时间
// 对于BIO,一个socket连接建立好后,不一定马上就被Tomcat处理了,其中需要线程池的调度,所以这段等待的时间要算在socket读取数据的时间内,而对于NIO而言,没有阻塞
setRequestLineReadTimeout();
// 解析请求行
if (!getInputBuffer().parseRequestLine(keptAlive)) {
// 下面这个方法在NIO时有用,比如在解析请求行时,如果没有从操作系统读到数据,则上面的方法会返回false
// 而下面这个方法会返回true,从而退出while,表示此处read事件处理结束,到下一次read事件发生了,就会从小进入到while中
if (handleIncompleteRequestLineRead()) {
break;
}
}
if (endpoint.isPaused()) {
// 如果Endpoint被暂停了,则返回503
response.setStatus(503);
setErrorState(ErrorState.CLOSE_CLEAN, null);
} else {
keptAlive = true;
// 每次处理一个请求就重新获取一下请求头和cookies的最大限制
request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
request.getCookies().setLimit(getMaxCookieCount());
// 解析请求头
if (!getInputBuffer().parseHeaders()) {
openSocket = true;
readComplete = false;
break;
}
if (!disableUploadTimeout) {
setSocketTimeout(connectionUploadTimeout);
}
}
}
if (!getErrorState().isError()) {
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE); // 设置请求状态为预处理状态
try {
prepareRequest(); // 预处理, 主要从请求中处理处keepAlive属性,以及进行一些验证,以及根据请求分析得到ActiveInputFilter
}
}
if (maxKeepAliveRequests == 1) {
// 如果最大的活跃http请求数量仅仅只能为1的话,那么设置keepAlive为false,则不会继续从socket中获取Http请求了
keepAlive = false;
} else if (maxKeepAliveRequests > 0 && socketWrapper.decrementKeepAlive() <= 0) {
// 如果已经达到了keepAlive的最大限制,也设置为false,则不会继续从socket中获取Http请求了
keepAlive = false;
}
if (!getErrorState().isError()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE); // 设置请求的状态为服务状态,表示正在处理请求
adapter.service(request, response); // 交给容器处理请求
if(keepAlive && !getErrorState().isError() && ( response.getErrorException() != null || (!isAsync() && statusDropsConnection(response.getStatus())))) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
setCometTimeouts(socketWrapper);
}
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT); // 设置请求的状态为处理请求结束
if (!isAsync() && !comet) {
if (getErrorState().isError()) {
getInputBuffer().setSwallowInput(false);
} else {
checkExpectationAndResponseStatus();
}
endRequest(); // 当前http请求已经处理完了,做一些收尾工作
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT); // 请求状态为输出结束
if (getErrorState().isError()) {
response.setStatus(500);
}
request.updateCounters();
if (!isAsync() && !comet || getErrorState().isError()) {
if (getErrorState().isIoAllowed()) { // 准备处理下一个请求
getInputBuffer().nextRequest();
getOutputBuffer().nextRequest();
}
}
rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
// 如果处理完当前这个Http请求之后,发现socket里没有下一个请求了,那么就退出当前循环
// 如果是keepalive,就不会关闭socket, 如果是close就会关闭socket
// 对于keepalive的情况,因为是一个线程处理一个socket,当退出这个while后,当前线程就会介绍,
// 当时对于socket来说,它仍然要继续介绍连接,所以又会新开一个线程继续来处理这个socket
if (breakKeepAliveLoop(socketWrapper)) {
break;
}
}
// 至此,循环结束
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
// 主要流程就是将socket的状态设置为CLOSED
if (getErrorState().isError() || endpoint.isPaused()) {
return SocketState.CLOSED;
} else if (isAsync() || comet) {// 异步servlet
return SocketState.LONG;
} else {
if (sendfileInProgress) {
return SocketState.SENDFILE;
} else {
if (openSocket) { // openSocket为true,表示不要关闭socket
// readComplete表示本次读数据是否完成,比如nio中可能就没有读完数据,还需要从socket中读数据
if (readComplete) {
return SocketState.OPEN;
} else { // nio可能会走到这里
return SocketState.LONG;
}
} else {
return SocketState.CLOSED;
}
}
}
}

解析字节流

不同的IO模型表示从Socket上获取字节流的方式不同,获取字节流后,Tomcat需要按照HTTP协议格式来解析字节流,浏览器或HttpClient发送数据时,需要按照HTTP协议来构造字符串数据,然后将字符串转换成字节发送出去。

HTTP请求协议格式

Tomcat解析字节流的逻辑,遍历每个字节遇到空格时,之前遍历的字节数据即请求方法,继续遍历当遇到空格时,之前遍历的字节数据即URL,继续遍历当遇到回车、换行符时,之前遍历的字节数据即协议版本,且请求行遍历结束。

继续遍历当遇到一个回车符合换行符时,所遍历的数据即请求头,当遍历到两个回车符合换行符时,表示请求头遍历完毕,剩下的数据就是请求体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
protected void setRequestLineReadTimeout() throws IOException {
// 最近一次访问的时间
if (inputBuffer.lastValid == 0 && socketWrapper.getLastAccess() > -1) {
int firstReadTimeout;
// 如果长连接没有超时时间,那么从socket中读数据也没有超时时间
if (keepAliveTimeout == -1) {
firstReadTimeout = 0;
} else {
// 一个socket在被处理之前会调用一下access方法,所以queueTime表示的是socket创建好了到真正被处理这段过程的排队时间
long queueTime = System.currentTimeMillis() - socketWrapper.getLastAccess();
// 如果排队时间大于keepAliveTimeout,表示该socket已经超时了不需要被处理了,设置一个最小的超时时间,当从这个socket上读取数据时会立刻超时
if (queueTime >= keepAliveTimeout) {
firstReadTimeout = 1;
} else {
// 如果排队时间还没有超过keepAliveTimeout,那么第一次从socket中读取数据的超时时间就是所剩下的时间了
firstReadTimeout = keepAliveTimeout - (int) queueTime;
}
}
// 设置socket的超时时间,然后开始读数据,该时间就是每次读取数据的超时时间
socketWrapper.getSocket().setSoTimeout(firstReadTimeout);
// 会从inputStream中获取数据,会阻塞,如果在firstReadTimeout的时间内没有读到数据则抛Eof异常 , 数据会被读到buf中
if (!inputBuffer.fill()) {
// eof是End Of File的意思
throw new EOFException(sm.getString("iib.eof.error"));
}
// 当第一次读取数据完成后,设置socket的超时时间为原本的超时时间
if (endpoint.getSoTimeout()> 0) {
setSocketTimeout(endpoint.getSoTimeout());
} else {
setSocketTimeout(0);
}
// 这里的场景有点像工作,我现在要做一个任务,规定是5天内要完成,但是其中由于客观原因有1天不能工作,所以那一天不算在5天之内,而客观原因解决之后,以后每次做任务就仍然按5天来限制
// 任务的就是read,5天就是timeout,客观原因就是tomcat的调度
}
}

以下方法是Tomcat从操作系统RecvBuf中读取数据的关键代码,这里的poslastValid非常关键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
protected boolean fill() throws IOException {
return fill(true);
}

@Override
protected boolean fill(boolean block) throws IOException {
int nRead = 0;
if (parsingHeader) {
// 如果还在解析请求头,lastValid表示当前解析数据的下标位置,如果该位置等于buf的长度了,表示请求头的数据超过buf了。
if (lastValid == buf.length) {
throw new IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error"));
}
// 从inputStream中读取数据,len表示要读取的数据长度,pos表示把从inputStream读到的数据放在buf的pos位置
// nRead表示真实读取到的数据
nRead = inputStream.read(buf, pos, buf.length - lastValid);
if (nRead > 0) {
lastValid = pos + nRead; // 移动lastValid
}
} else {
// 当读取请求体的数据时
// buf.length - end表示还能存放多少请求体数据,如果小于4500,那么就新生成一个byte数组,这个新的数组专门用来盛放请求体
if (buf.length - end < 4500) {
buf = new byte[buf.length];
end = 0;
}
pos = end;
lastValid = pos;
nRead = inputStream.read(buf, pos, buf.length - lastValid);
if (nRead > 0) {
lastValid = pos + nRead;
}
}
return (nRead > 0);
}

若使用长连接,就会存在多个HTTP请求共用一个Socket连接,Tomcat在获取并解析Socket连接中的字节流时,如何判断一个HTTP请求结束:

  • 设置Content-Length:在发送请求时直接设置请求体长度,Tomcat在解析请求时就知道在哪里结束了
  • 设置Transfer-Encoding为chunk:分块传输,发送请求按如下格式传输请求体:[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n],最后的chunk size = 0和两个回车换行符表示接收到最后一块,表示请求体结束

请求体解析原理

普通的Servlet获取用户请求体中的内容:

1
2
3
4
5
6
7
8
public class ElevenServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream inputStream = req.getInputStream();
byte[] bytes = new byte[20];
inputStream.read(bytes);
System.out.println(new String(bytes));
}
}

在Tomcat中HttpServletRequestgetInputStream方法的是调用的Tomcat底层自己的Request的门面模式RequestFacade

RequestFacade是调用的Tomcat自身connector包下的Request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class RequestFacade implements HttpServletRequest {
protected Request request = null;
@Override
public ServletInputStream getInputStream() throws IOException {
if (request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
}
return request.getInputStream();
}
}

public class Request implements HttpServletRequest {
@Override
public ServletInputStream getInputStream() throws IOException {
if (usingReader) {
throw new IllegalStateException(sm.getString("coyoteRequest.getInputStream.ise"));
}
usingInputStream = true;
if (inputStream == null) {
inputStream = new CoyoteInputStream(inputBuffer);
}
return inputStream;
}
}

public class CoyoteInputStream extends ServletInputStream {
protected InputBuffer ib;
protected CoyoteInputStream(InputBuffer ib) {
this.ib = ib;
}
}

所以用户程序中的req.getInputStream().read(bytes)方法最终调用的是InputBuffer的read方法:

1
2
3
4
5
6
7
8
private final ByteChunk bb;
public int read(byte[] b, int off, int len) throws IOException {
if (closed) {
throw new IOException(sm.getString("inputBuffer.streamClosed"));
}
// 从bb中截取一部分数据到b中
return bb.substract(b, off, len);
}

调用ByteChunk中的substract方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public int substract(byte dest[], int off, int len) throws IOException {
// 这里会对当前ByteChunk初始化
if (checkEof()) {
return -1;
}
int n = len;
// 如果需要的数据超过buff中标记的数据长度
if (len > getLength()) {
n = getLength();
}
// 将buff数组中从start位置开始的数据,复制到dest中,长度为n,desc数组中就有值了
System.arraycopy(buff, start, dest, off, n);
start += n;
return n;
}

private boolean checkEof() throws IOException {
if ((end - start) == 0) {
// 如果bytechunk没有标记数据了,则开始比较
if (in == null) {
return true;
}
// 从in中读取buff长度大小的数据,读到buff中,真实读到的数据为n
int n = in.realReadBytes(buff, 0, buff.length);
if (n < 0) {
return true;
}
}
return false;
}

public int realReadBytes(byte cbuf[], int off, int len) throws IOException {
if (closed) {
return -1;
}
if (coyoteRequest == null) {
return -1;
}
if(state == INITIAL_STATE) {
state = BYTE_STATE;
}
// 上层缓冲区中没有数据,则从底层取(这里的底层是InputStreamInputBuffer中的buff)
int result = coyoteRequest.doRead(bb);
return result;
}

最终是调用的Tomcat中coyote包下的Request中的doRead方法来将数据读取到用传入的字节数组中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public final class Request {
private InputBuffer inputBuffer = null;
public int doRead(ByteChunk chunk) throws IOException {
// 从InputStreamInputBuffer中读取数据,其实是标记,这里首先进入AbstractInputBuffer中的doRead方法
int n = inputBuffer.doRead(chunk, this);
if (n > 0) {
bytesRead+=n;
}
return n;
}
}

public int doRead(ByteChunk chunk, Request req) throws IOException {
// 如果没有ActiveFilter,则直接从inputStreamInputBuffer中读取
// 如果有ActiveFilter,则调用对应的ActiveFilter读取
// 要么是IdentityInputFilter: 每次读多少不确定,看能从操作系统拿到多少
// 要么是ChunkedInputFilter: 客户端分块发送的,ChunkedInputFilter一次读一块数据
// 要么是VoidInputFilter:直接读不到数据,不管到底有没有请求体
if (lastActiveFilter == -1)
return inputStreamInputBuffer.doRead(chunk, req);
else
return activeFilters[lastActiveFilter].doRead(chunk,req);

}

以IdentityInputFilter为例,若当前用户的read(bytes)方法没有将数据读完,下次调用read(bytes)方法时接着继续读取剩余数据,若当前数据读超了,返回给用户的数据还是当前请求的请求体,但是remaining变成了负数,或者用户根本就不来读取数据或者有剩余数据未读取完,若不处理则会影响下一次请求的数据读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public int doRead(ByteChunk chunk, Request req) throws IOException {
// 当servlet中读取请求体时,会进入到这个方法,该方法返回
int result = -1;
// contentLength表示请求体的长度,当读取请求体时,只会返回这么长的数据
// remaining初始值为contentLength
if (contentLength >= 0) { // 100
// 可能会多次读取请求体,所以记录一下请求体还剩下多少
if (remaining > 0) { // 10
// 这里的buffer是InputSteamInputBuffer,会从操作系统的RecvBuf中读取数据,nRead表示读到了多少了数据
int nRead = buffer.doRead(chunk, req); // 20
// 如果读到的数据超过了剩余部分,那么将chunk的标记缩小,缩小为剩余部分的最后一个位置,多余数据不属于请求体了
if (nRead > remaining) {
chunk.setBytes(chunk.getBytes(), chunk.getStart(), (int) remaining);
result = (int) remaining;
} else { // 如果真实读到的数据小于剩下的
result = nRead;
}
if (nRead > 0) {// 记录一下还需要读多少数据
// 10 - 20==10
remaining = remaining - nRead; // 如果剩余数据比真实读到的数据小,remaining将为负数
}
} else {
// 如果没有剩余数据了,返回-1
chunk.recycle();
result = -1;
}
}
return result;
}

针对上面的情况,在用户请求处理完后,会调用AbstractHttp11ProcessorendRequest进行修正:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public void endRequest() {
// Finish the handling of the request
if (getErrorState().isIoAllowed()) {
try {
// 把InputBuffer的pos位置移动到第二个请求开始的位置
getInputBuffer().endRequest();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_NOW, e);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
response.setStatus(500);
setErrorState(ErrorState.CLOSE_NOW, t);
getLog().error(sm.getString("http11processor.request.finish"), t);
}
}
if (getErrorState().isIoAllowed()) {
try {
getOutputBuffer().endRequest();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_NOW, e);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setErrorState(ErrorState.CLOSE_NOW, t);
getLog().error(sm.getString("http11processor.response.finish"), t);
}
}
}

public void endRequest() throws IOException {
if (swallowInput && (lastActiveFilter != -1)) {
int extraBytes = (int) activeFilters[lastActiveFilter].end(); // 多度的数据
pos = pos - extraBytes; // 把pos向前移动
}
}

public long end() throws IOException {
// 本次http请求已经处理完了,做收尾工作,主要处理看请求体是否有剩余数据没有读完,判断剩余数据是否超过了限制
final boolean maxSwallowSizeExceeded = (maxSwallowSize > -1 && remaining > maxSwallowSize);
long swallowed = 0;
// 还有剩余数据
while (remaining > 0) {
// 从操作系统读取数据
int nread = buffer.doRead(endChunk, null);
if (nread > 0 ) {
// 如果读到了数据
swallowed += nread;
// 更新剩余数据
remaining = remaining - nread;
// 如果在遍历剩余数据时,读到的数据超过了maxSwallowSize,则会抛异常,后续逻辑就会把socket关掉
if (maxSwallowSizeExceeded && swallowed > maxSwallowSize) {
// 我们不会提早失败,因此客户端可以去读取响应在连接关闭之前
throw new IOException(sm.getString("inputFilter.maxSwallow"));
}
} else { // errors are handled higher up.
// 如果本来认为还有剩余数据,但是真正去读的时候没有数据了,nread等于-1,索引剩余数据为0
remaining = 0;
}
}
// 读到的真实数据超过了剩余数据,则remaining为负数
return -remaining;
}