扫码关注公众号:芋道源码

发送: 百事可乐
获取永久解锁本站全部文章的链接

《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://juejin.im/post/5a72c045f265da3e4c081fb0 「预流」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

先抛开之前所看到的 Tomcat 源码不谈,Tomcat 作为一个用 Java 实现的 Web 服务器,如果让你来实现,那么从何入手?

这里首先需要厘清的是 Web 服务器的概念,谷歌了一下,发现这条解释还算靠谱点,【在网络环境下可以向发出请求的浏览器提供文档的程序】。重点有两条:1.网络环境下,2.能够给出响应。用 Java 写过网络通信程序的都知道,这里必然会用到 Socket 编程。我们自己要实现的服务器程序作为 Socket 编程里的服务端,浏览器作为 Socket 编程里的客户端。

要理解 Tomcat 原理,Socket 编程这块的基本原理必须得了解,google 一把一大堆,这里不再单独做介绍。下面给出一个服务器端最简单的响应客户端请求的伪代码示例:

ServerSocket serverSocket  = new ServerSocket(8080, 1,
InetAddress.getByName(“localhost”));
Socket socket = null;
InputStream is = null;
OutputStream os = null;
try {
socket = serverSocket.accept();//1.监听到客户端的连接
is = socket.getInputStream();
os = socket.getOutputStream();
Request request = Util.getRequest(is);//2.从输入流中读取数据,并根据Http协议转换成请求
Response response = Util.service(request);//服务器内部根据请求信息给出响应信息
os.writeResponse(response);//3.将响应信息写到输出流
} catch (Exception e) {
e.printStackTrace();
} finally {//4.关闭输入输出流及连接
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
socket.close();
}

浏览器和 Web 服务器的一次交互过程分四步:连接、请求、响应、关闭。前一篇文章分析到的接收器线程,如前文开始截图里的 http-bio-8080-Acceptor-0 ,这个线程的实现类org.apache.tomcat.util.net.JIoEndpoint.Acceptor,源码如下:

 1	    // --------------------------------------------------- Acceptor Inner Class
2 /**
3 * The background thread that listens for incoming TCP/IP connections and
4 * hands them off to an appropriate processor.
5 */
6 protected class Acceptor extends AbstractEndpoint.Acceptor {
7
8 @Override
9 public void run() {
10
11 int errorDelay = 0;
12
13 // Loop until we receive a shutdown command
14 while (running) {
15
16 // Loop if endpoint is paused
17 while (paused && running) {
18 state = AcceptorState.PAUSED;
19 try {
20 Thread.sleep(50);
21 } catch (InterruptedException e) {
22 // Ignore
23 }
24 }
25
26 if (!running) {
27 break;
28 }
29 state = AcceptorState.RUNNING;
30
31 try {
32 //if we have reached max connections, wait
33 countUpOrAwaitConnection();
34
35 Socket socket = null;
36 try {
37 // Accept the next incoming connection from the server
38 // socket
39 socket = serverSocketFactory.acceptSocket(serverSocket);
40 } catch (IOException ioe) {
41 countDownConnection();
42 // Introduce delay if necessary
43 errorDelay = handleExceptionWithDelay(errorDelay);
44 // re-throw
45 throw ioe;
46 }
47 // Successful accept, reset the error delay
48 errorDelay = 0;
49
50 // Configure the socket
51 if (running && !paused && setSocketOptions(socket)) {
52 // Hand this socket off to an appropriate processor
53 if (!processSocket(socket)) {
54 countDownConnection();
55 // Close socket right away
56 closeSocket(socket);
57 }
58 } else {
59 countDownConnection();
60 // Close socket right away
61 closeSocket(socket);
62 }
63 } catch (IOException x) {
64 if (running) {
65 log.error(sm.getString("endpoint.accept.fail"), x);
66 }
67 } catch (NullPointerException npe) {
68 if (running) {
69 log.error(sm.getString("endpoint.accept.fail"), npe);
70 }
71 } catch (Throwable t) {
72 ExceptionUtils.handleThrowable(t);
73 log.error(sm.getString("endpoint.accept.fail"), t);
74 }
75 }
76 state = AcceptorState.ENDED;
77 }
78 }

第 39 行做的事就是上面伪代码示例里的监听客户端连接,监听到连接后(即浏览器向服务器发起一次请求)在第 53 行由 processSocket 方法来处理这次接收到的 Socket 连接。processSocket 方法源码如下:

 1	    protected boolean processSocket(Socket socket) {
2 // Process the request from this socket
3 try {
4 SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
5 wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
6 // During shutdown, executor may be null - avoid NPE
7 if (!running) {
8 return false;
9 }
10 getExecutor().execute(new SocketProcessor(wrapper));
11 } catch (RejectedExecutionException x) {
12 log.warn("Socket processing request was rejected for:"+socket,x);
13 return false;
14 } catch (Throwable t) {
15 ExceptionUtils.handleThrowable(t);
16 // This means we got an OOM or similar creating a thread, or that
17 // the pool and its queue are full
18 log.error(sm.getString("endpoint.process.fail"), t);
19 return false;
20 }
21 return true;
22 }

该方法中先把 Socket 包装成 SocketWrapper ,用以内部处理。重点是第 10 行:getExecutor().execute(new SocketProcessor(wrapper))。如果按照上面伪代码中的处理方式,在有并发请求的情况下,一个请求没有处理完成,服务器将无法立即响应另一个请求。这里做了一下改进,即在接收到一次 Socket 连接后另启一个线程处理该连接,使当前线程不阻塞。

下面跟着 SocketProcessor 这个线程来看看,一次 Socket 连接是如何在 Tomcat 7 中被转成内部的 Request 的。看下该线程的 run 方法:

 1	        @Override
2 public void run() {
3 boolean launch = false;
4 synchronized (socket) {
5 try {
6 SocketState state = SocketState.OPEN;
7
8 try {
9 // SSL handshake
10 serverSocketFactory.handshake(socket.getSocket());
11 } catch (Throwable t) {
12 ExceptionUtils.handleThrowable(t);
13 if (log.isDebugEnabled()) {
14 log.debug(sm.getString("endpoint.err.handshake"), t);
15 }
16 // Tell to close the socket
17 state = SocketState.CLOSED;
18 }
19
20 if ((state != SocketState.CLOSED)) {
21 if (status == null) {
22 state = handler.process(socket, SocketStatus.OPEN);
23 } else {
24 state = handler.process(socket,status);
25 }
26 }
27 if (state == SocketState.CLOSED) {
28 // Close socket
29 if (log.isTraceEnabled()) {
30 log.trace("Closing socket:"+socket);
31 }
32 countDownConnection();
33 try {
34 socket.getSocket().close();
35 } catch (IOException e) {
36 // Ignore
37 }
38 } else if (state == SocketState.OPEN ||
39 state == SocketState.UPGRADING ||
40 state == SocketState.UPGRADED){
41 socket.setKeptAlive(true);
42 socket.access();
43 launch = true;
44 } else if (state == SocketState.LONG) {
45 socket.access();
46 waitingRequests.add(socket);
47 }
48 } finally {
49 if (launch) {
50 try {
51 getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN));
52 } catch (RejectedExecutionException x) {
53 log.warn("Socket reprocessing request was rejected for:"+socket,x);
54 try {
55 //unable to handle connection at this time
56 handler.process(socket, SocketStatus.DISCONNECT);
57 } finally {
58 countDownConnection();
59 }
60
61
62 } catch (NullPointerException npe) {
63 if (running) {
64 log.error(sm.getString("endpoint.launch.fail"),
65 npe);
66 }
67 }
68 }
69 }
70 }
71 socket = null;
72 // Finish up this request
73 }
74
75 }

默认情况下会走到第 22 行,调用 handler 对象的 process 方法,这里 handler 对象实际上是 Http11ConnectionHandler 类的实例,该对象的初始化过程是在 org.apache.coyote.http11.Http11Protocol对象的构造方法中:

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);
}

所以需要到org.apache.coyote.http11.Http11Protocol类的静态内部类 Http11ConnectionHandler 中找到 process 方法的定义,但当前定义里面没有,这个方法是在其父类org.apache.coyote.AbstractProtocol.AbstractConnectionHandler中定义的:

  1	        public SocketState process(SocketWrapper<S> socket,
2 SocketStatus status) {
3 Processor<S> processor = connections.remove(socket.getSocket());
4
5 if (status == SocketStatus.DISCONNECT && processor == null) {
6 //nothing more to be done endpoint requested a close
7 //and there are no object associated with this connection
8 return SocketState.CLOSED;
9 }
10
11 socket.setAsync(false);
12
13 try {
14 if (processor == null) {
15 processor = recycledProcessors.poll();
16 }
17 if (processor == null) {
18 processor = createProcessor();
19 }
20
21 initSsl(socket, processor);
22
23 SocketState state = SocketState.CLOSED;
24 do {
25 if (status == SocketStatus.DISCONNECT &&
26 !processor.isComet()) {
27 // Do nothing here, just wait for it to get recycled
28 // Don't do this for Comet we need to generate an end
29 // event (see BZ 54022)
30 } else if (processor.isAsync() ||
31 state == SocketState.ASYNC_END) {
32 state = processor.asyncDispatch(status);
33 } else if (processor.isComet()) {
34 state = processor.event(status);
35 } else if (processor.isUpgrade()) {
36 state = processor.upgradeDispatch();
37 } else {
38 state = processor.process(socket);
39 }
40
41 if (state != SocketState.CLOSED && processor.isAsync()) {
42 state = processor.asyncPostProcess();
43 }
44
45 if (state == SocketState.UPGRADING) {
46 // Get the UpgradeInbound handler
47 UpgradeInbound inbound = processor.getUpgradeInbound();
48 // Release the Http11 processor to be re-used
49 release(socket, processor, false, false);
50 // Create the light-weight upgrade processor
51 processor = createUpgradeProcessor(socket, inbound);
52 inbound.onUpgradeComplete();
53 }
54 } while (state == SocketState.ASYNC_END ||
55 state == SocketState.UPGRADING);
56
57 if (state == SocketState.LONG) {
58 // In the middle of processing a request/response. Keep the
59 // socket associated with the processor. Exact requirements
60 // depend on type of long poll
61 longPoll(socket, processor);
62 } else if (state == SocketState.OPEN) {
63 // In keep-alive but between requests. OK to recycle
64 // processor. Continue to poll for the next request.
65 release(socket, processor, false, true);
66 } else if (state == SocketState.SENDFILE) {
67 // Sendfile in progress. If it fails, the socket will be
68 // closed. If it works, the socket will be re-added to the
69 // poller
70 release(socket, processor, false, false);
71 } else if (state == SocketState.UPGRADED) {
72 // Need to keep the connection associated with the processor
73 longPoll(socket, processor);
74 } else {
75 // Connection closed. OK to recycle the processor.
76 if (!(processor instanceof UpgradeProcessor)) {
77 release(socket, processor, true, false);
78 }
79 }
80 return state;
81 } catch(java.net.SocketException e) {
82 // SocketExceptions are normal
83 getLog().debug(sm.getString(
84 "abstractConnectionHandler.socketexception.debug"), e);
85 } catch (java.io.IOException e) {
86 // IOExceptions are normal
87 getLog().debug(sm.getString(
88 "abstractConnectionHandler.ioexception.debug"), e);
89 }
90 // Future developers: if you discover any other
91 // rare-but-nonfatal exceptions, catch them here, and log as
92 // above.
93 catch (Throwable e) {
94 ExceptionUtils.handleThrowable(e);
95 // any other exception or error is odd. Here we log it
96 // with "ERROR" level, so it will show up even on
97 // less-than-verbose logs.
98 getLog().error(
99 sm.getString("abstractConnectionHandler.error"), e);
100 }
101 // Don't try to add upgrade processors back into the pool
102 if (!(processor instanceof UpgradeProcessor)) {
103 release(socket, processor, true, false);
104 }
105 return SocketState.CLOSED;
106 }

重点在第 38 行,调用 processor 的 process 方法处理 socket 。而 processor 对象在第 18 行通过 createProcessor 方法创建出来的,createProcessor 方法在当前类里面是抽象方法,默认情况下调用的具体实现类在上面提到的 Http11ConnectionHandler 类中:

 1	        @Override
2 protected Http11Processor createProcessor() {
3 Http11Processor processor = new Http11Processor(
4 proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
5 proto.getMaxTrailerSize());
6 processor.setAdapter(proto.adapter);
7 processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
8 processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
9 processor.setConnectionUploadTimeout(
10 proto.getConnectionUploadTimeout());
11 processor.setDisableUploadTimeout(proto.getDisableUploadTimeout());
12 processor.setCompressionMinSize(proto.getCompressionMinSize());
13 processor.setCompression(proto.getCompression());
14 processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents());
15 processor.setCompressableMimeTypes(proto.getCompressableMimeTypes());
16 processor.setRestrictedUserAgents(proto.getRestrictedUserAgents());
17 processor.setSocketBuffer(proto.getSocketBuffer());
18 processor.setMaxSavePostSize(proto.getMaxSavePostSize());
19 processor.setServer(proto.getServer());
20 processor.setDisableKeepAlivePercentage(
21 proto.getDisableKeepAlivePercentage());
22 register(processor);
23 return processor;
24 }

此时的 processor 对象是 Http11Processor 类的实例,再看上一段提到的 processor.process 方法,最终会执行到 Http11Processor 类(因为该类中没有定义 process 方法)的父类org.apache.coyote.http11.AbstractHttp11Processor中的 process 方法。

为了方便理解,下面的时序图列出从 Acceptor 线程的 run 方法到 AbstractHttp11Processor 类的 process 方法的关键方法调用过程:

img

接下来分析 org.apache.coyote.http11.AbstractHttp11Processor 类的 process 方法:

  1	    @Override
2 public SocketState process(SocketWrapper<S> socketWrapper)
3 throws IOException {
4 RequestInfo rp = request.getRequestProcessor();
5 rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
6
7 // Setting up the I/O
8 setSocketWrapper(socketWrapper);
9 getInputBuffer().init(socketWrapper, endpoint);
10 getOutputBuffer().init(socketWrapper, endpoint);
11
12 // Flags
13 error = false;
14 keepAlive = true;
15 comet = false;
16 openSocket = false;
17 sendfileInProgress = false;
18 readComplete = true;
19 if (endpoint.getUsePolling()) {
20 keptAlive = false;
21 } else {
22 keptAlive = socketWrapper.isKeptAlive();
23 }
24
25 if (disableKeepAlive()) {
26 socketWrapper.setKeepAliveLeft(0);
27 }
28
29 while (!error && keepAlive && !comet && !isAsync() &&
30 upgradeInbound == null && !endpoint.isPaused()) {
31
32 // Parsing the request header
33 try {
34 setRequestLineReadTimeout();
35
36 if (!getInputBuffer().parseRequestLine(keptAlive)) {
37 if (handleIncompleteRequestLineRead()) {
38 break;
39 }
40 }
41
42 if (endpoint.isPaused()) {
43 // 503 - Service unavailable
44 response.setStatus(503);
45 error = true;
46 } else {
47 // Make sure that connectors that are non-blocking during
48 // header processing (NIO) only set the start time the first
49 // time a request is processed.
50 if (request.getStartTime() < 0) {
51 request.setStartTime(System.currentTimeMillis());
52 }
53 keptAlive = true;
54 // Set this every time in case limit has been changed via JMX
55 request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
56 // Currently only NIO will ever return false here
57 if (!getInputBuffer().parseHeaders()) {
58 // We've read part of the request, don't recycle it
59 // instead associate it with the socket
60 openSocket = true;
61 readComplete = false;
62 break;
63 }
64 if (!disableUploadTimeout) {
65 setSocketTimeout(connectionUploadTimeout);
66 }
67 }
68 } catch (IOException e) {
69 if (getLog().isDebugEnabled()) {
70 getLog().debug(
71 sm.getString("http11processor.header.parse"), e);
72 }
73 error = true;
74 break;
75 } catch (Throwable t) {
76 ExceptionUtils.handleThrowable(t);
77 UserDataHelper.Mode logMode = userDataHelper.getNextMode();
78 if (logMode != null) {
79 String message = sm.getString(
80 "http11processor.header.parse");
81 switch (logMode) {
82 case INFO_THEN_DEBUG:
83 message += sm.getString(
84 "http11processor.fallToDebug");
85 //$FALL-THROUGH$
86 case INFO:
87 getLog().info(message);
88 break;
89 case DEBUG:
90 getLog().debug(message);
91 }
92 }
93 // 400 - Bad Request
94 response.setStatus(400);
95 adapter.log(request, response, 0);
96 error = true;
97 }
98
99 if (!error) {
100 // Setting up filters, and parse some request headers
101 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
102 try {
103 prepareRequest();
104 } catch (Throwable t) {
105 ExceptionUtils.handleThrowable(t);
106 if (getLog().isDebugEnabled()) {
107 getLog().debug(sm.getString(
108 "http11processor.request.prepare"), t);
109 }
110 // 400 - Internal Server Error
111 response.setStatus(400);
112 adapter.log(request, response, 0);
113 error = true;
114 }
115 }
116
117 if (maxKeepAliveRequests == 1) {
118 keepAlive = false;
119 } else if (maxKeepAliveRequests > 0 &&
120 socketWrapper.decrementKeepAlive() <= 0) {
121 keepAlive = false;
122 }
123
124 // Process the request in the adapter
125 if (!error) {
126 try {
127 rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
128 adapter.service(request, response);
129 // Handle when the response was committed before a serious
130 // error occurred. Throwing a ServletException should both
131 // set the status to 500 and set the errorException.
132 // If we fail here, then the response is likely already
133 // committed, so we can't try and set headers.
134 if(keepAlive && !error) { // Avoid checking twice.
135 error = response.getErrorException() != null ||
136 (!isAsync() &&
137 statusDropsConnection(response.getStatus()));
138 }
139 setCometTimeouts(socketWrapper);
140 } catch (InterruptedIOException e) {
141 error = true;
142 } catch (HeadersTooLargeException e) {
143 error = true;
144 // The response should not have been committed but check it
145 // anyway to be safe
146 if (!response.isCommitted()) {
147 response.reset();
148 response.setStatus(500);
149 response.setHeader("Connection", "close");
150 }
151 } catch (Throwable t) {
152 ExceptionUtils.handleThrowable(t);
153 getLog().error(sm.getString(
154 "http11processor.request.process"), t);
155 // 500 - Internal Server Error
156 response.setStatus(500);
157 adapter.log(request, response, 0);
158 error = true;
159 }
160 }
161
162 // Finish the handling of the request
163 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
164
165 if (!isAsync() && !comet) {
166 if (error) {
167 // If we know we are closing the connection, don't drain
168 // input. This way uploading a 100GB file doesn't tie up the
169 // thread if the servlet has rejected it.
170 getInputBuffer().setSwallowInput(false);
171 }
172 endRequest();
173 }
174
175 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
176
177 // If there was an error, make sure the request is counted as
178 // and error, and update the statistics counter
179 if (error) {
180 response.setStatus(500);
181 }
182 request.updateCounters();
183
184 if (!isAsync() && !comet || error) {
185 getInputBuffer().nextRequest();
186 getOutputBuffer().nextRequest();
187 }
188
189 if (!disableUploadTimeout) {
190 if(endpoint.getSoTimeout() > 0) {
191 setSocketTimeout(endpoint.getSoTimeout());
192 } else {
193 setSocketTimeout(0);
194 }
195 }
196
197 rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
198
199 if (breakKeepAliveLoop(socketWrapper)) {
200 break;
201 }
202 }
203
204 rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
205
206 if (error || endpoint.isPaused()) {
207 return SocketState.CLOSED;
208 } else if (isAsync() || comet) {
209 return SocketState.LONG;
210 } else if (isUpgrade()) {
211 return SocketState.UPGRADING;
212 } else {
213 if (sendfileInProgress) {
214 return SocketState.SENDFILE;
215 } else {
216 if (openSocket) {
217 if (readComplete) {
218 return SocketState.OPEN;
219 } else {
220 return SocketState.LONG;
221 }
222 } else {
223 return SocketState.CLOSED;
224 }
225 }
226 }
227 }

从这个方法中可以清晰的看出解析请求的过程:第 7 到 10 行从 Socket 中获取输入输出流,第 32 到 97 行解析请求行和请求头,第 99 到 115 行校验和解析请求头中的属性,第 125 到 160 行调用适配器的 service 方法,第 172 行请求处理结束。

上面就是根据 Http 协议解析请求的总体流程。要理解上面提到的请求行、请求头等术语,需要熟悉 Http 协议,这里简单介绍下 Http 协议中的标准请求信息数据的格式:

请求信息包括以下三条

  • 请求行(request line)

例如GET /images/logo.gif HTTP/1.1,表示从/images目录下请求logo.gif这个文件。

  • 请求头(request header),空行

例如Accept-Language: en

  • 其他消息体

请求行和标题必须以<CR><LF>作为结尾。空行内必须只有<CR><LF>而无其他空格。在 HTTP/1.1 协议中,所有的请求头,除 Host 外,都是可选的。

请求行、请求头数据的格式具体看 Http 协议中的描述。所以在从输入流中读取到字节流数据之后必须按照请求行、请求头、消息体的顺序来解析。

这里以请求行数据的解析为例,在 Http 协议中该行内容格式为:

Request-Line = Method SP Request-URI SP HTTP-Version CRLF

即请求类型、要访问的资源( URI )以及使用的HTTP版本,中间以特殊字符空格来分隔,以\r\n字符结尾。

在上面列出的 AbstractHttp11Processor 类的 process 代码中的第 36 行,会调用抽象方法 getInputBuffer() ,当前该抽象方法的具体实现在子类org.apache.coyote.http11.Http11Processor中,该方法返回的是该类的实例变量 inputBuffer :

protected AbstractInputBuffer<Socket> getInputBuffer() {
return inputBuffer;
}

该实例变量在 Http11Processor 的构造方法中会被初始化:

public Http11Processor(int headerBufferSize, JIoEndpoint endpoint,
int maxTrailerSize) {

super(endpoint);

inputBuffer = new InternalInputBuffer(request, headerBufferSize);
request.setInputBuffer(inputBuffer);

outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
response.setOutputBuffer(outputBuffer);

initializeFilters(maxTrailerSize);
}

所以 AbstractHttp11Processor 类的 process 方法的 36 行 getInputBuffer().parseRequestLine() 将会调用org.apache.coyote.http11.InternalInputBuffer类中的 parseRequestLine 方法:

  1	    public boolean parseRequestLine(boolean useAvailableDataOnly)
2
3 throws IOException {
4
5 int start = 0;
6
7 //
8 // Skipping blank lines
9 //
10
11 byte chr = 0;
12 do {
13
14 // Read new bytes if needed
15 if (pos >= lastValid) {
16 if (!fill())
17 throw new EOFException(sm.getString("iib.eof.error"));
18 }
19
20 chr = buf[pos++];
21
22 } while ((chr == Constants.CR) || (chr == Constants.LF));
23
24 pos--;
25
26 // Mark the current buffer position
27 start = pos;
28
29 //
30 // Reading the method name
31 // Method name is always US-ASCII
32 //
33
34 boolean space = false;
35
36 while (!space) {
37
38 // Read new bytes if needed
39 if (pos >= lastValid) {
40 if (!fill())
41 throw new EOFException(sm.getString("iib.eof.error"));
42 }
43
44 // Spec says no CR or LF in method name
45 if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
46 throw new IllegalArgumentException(
47 sm.getString("iib.invalidmethod"));
48 }
49 // Spec says single SP but it also says be tolerant of HT
50 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
51 space = true;
52 request.method().setBytes(buf, start, pos - start);
53 }
54
55 pos++;
56
57 }
58
59
60 // Spec says single SP but also says be tolerant of multiple and/or HT
61 while (space) {
62 // Read new bytes if needed
63 if (pos >= lastValid) {
64 if (!fill())
65 throw new EOFException(sm.getString("iib.eof.error"));
66 }
67 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
68 pos++;
69 } else {
70 space = false;
71 }
72 }
73
74 // Mark the current buffer position
75 start = pos;
76 int end = 0;
77 int questionPos = -1;
78
79 //
80 // Reading the URI
81 //
82
83 boolean eol = false;
84
85 while (!space) {
86
87 // Read new bytes if needed
88 if (pos >= lastValid) {
89 if (!fill())
90 throw new EOFException(sm.getString("iib.eof.error"));
91 }
92
93 // Spec says single SP but it also says be tolerant of HT
94 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
95 space = true;
96 end = pos;
97 } else if ((buf[pos] == Constants.CR)
98 || (buf[pos] == Constants.LF)) {
99 // HTTP/0.9 style request
100 eol = true;
101 space = true;
102 end = pos;
103 } else if ((buf[pos] == Constants.QUESTION)
104 && (questionPos == -1)) {
105 questionPos = pos;
106 }
107
108 pos++;
109
110 }
111
112 request.unparsedURI().setBytes(buf, start, end - start);
113 if (questionPos >= 0) {
114 request.queryString().setBytes(buf, questionPos + 1,
115 end - questionPos - 1);
116 request.requestURI().setBytes(buf, start, questionPos - start);
117 } else {
118 request.requestURI().setBytes(buf, start, end - start);
119 }
120
121 // Spec says single SP but also says be tolerant of multiple and/or HT
122 while (space) {
123 // Read new bytes if needed
124 if (pos >= lastValid) {
125 if (!fill())
126 throw new EOFException(sm.getString("iib.eof.error"));
127 }
128 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
129 pos++;
130 } else {
131 space = false;
132 }
133 }
134
135 // Mark the current buffer position
136 start = pos;
137 end = 0;
138
139 //
140 // Reading the protocol
141 // Protocol is always US-ASCII
142 //
143
144 while (!eol) {
145
146 // Read new bytes if needed
147 if (pos >= lastValid) {
148 if (!fill())
149 throw new EOFException(sm.getString("iib.eof.error"));
150 }
151
152 if (buf[pos] == Constants.CR) {
153 end = pos;
154 } else if (buf[pos] == Constants.LF) {
155 if (end == 0)
156 end = pos;
157 eol = true;
158 }
159
160 pos++;
161
162 }
163
164 if ((end - start) > 0) {
165 request.protocol().setBytes(buf, start, end - start);
166 } else {
167 request.protocol().setString("");
168 }
169
170 return true;
171
172 }

先看这个方法中第 16 行,调用了当前类的 fill 方法:

protected boolean fill() throws IOException {
return fill(true);
}

里面调用了重载方法 fill :

 1	    protected boolean fill(boolean block) throws IOException {
2
3 int nRead = 0;
4
5 if (parsingHeader) {
6
7 if (lastValid == buf.length) {
8 throw new IllegalArgumentException
9 (sm.getString("iib.requestheadertoolarge.error"));
10 }
11
12 nRead = inputStream.read(buf, pos, buf.length - lastValid);
13 if (nRead > 0) {
14 lastValid = pos + nRead;
15 }
16
17 } else {
18
19 if (buf.length - end < 4500) {
20 // In this case, the request header was really large, so we allocate a
21 // brand new one; the old one will get GCed when subsequent requests
22 // clear all references
23 buf = new byte[buf.length];
24 end = 0;
25 }
26 pos = end;
27 lastValid = pos;
28 nRead = inputStream.read(buf, pos, buf.length - lastValid);
29 if (nRead > 0) {
30 lastValid = pos + nRead;
31 }
32
33 }
34
35 return (nRead > 0);
36
37 } }

在这里可以看到从输入流中读取数据到缓冲区 buf 。按照上面列出的请求行数据格式,从字符流中将会按顺序得到请求的类型( method )、请求的 URI 和 Http 版本。具体实现流程如下:

org.apache.coyote.http11.InternalInputBuffer类中的 parseRequestLine 方法,第 34 到 57 行根据请求头协议的格式,从中取出表示请求方法的字节数据并设置到内置实例变量 request 。第 60 到 72 行解析 method 和 uri 之间的空格字节 SP ,第 83 到 119 行读取表示请求的 URI 的字节数据并放到 request 变量中。第 122 到 133 行解析 uri 和 http 协议版本之间的空格字节 SP ,第 144 到第 168 行读取表示请求的 Http 协议版本的字节数据并放到 request 变量中。

以上是根据 Http 协议解析请求行( request line )的代码实现部分,解析请求头的部分见 InternalInputBuffer 类的 parseHeader 方法,不再赘述。

至此可以看到在 Tomcat 中如何从一次 Socket 连接中取出请求的数据,将这些原始的字符流数据转换成初步可以理解的 Tomcat 内置对象org.apache.coyote.Request的。

文章目录