一、前言

文章:https://www.cnblogs.com/runnable/p/12905401.html中介绍了Tomcat处理一次请求的大致过程,其中包括请求接收、请求数据处理以及请求响应。接下来用两篇文章详细分析请求数据解析:请求行和请求头的读取、请求体的读取。

在分析请求数据处理之前,再次回顾一下2个概念

1、Tomcat中用于读取socket数据的缓冲区buf。它是一个字节数组,默认长度8KB。有2个重要的位置下标:pos和lastValid,pos标记下次读取位置,lastValid标记有效数据最后位置。

Tomcat请求解析-请求行和请求头

 

 图中4种情况分别对应:初始数组;刚从操作系统中读取数据到buf;Tomcat解析过程中,已经读取第一位字节;本次从操作系统读取的数据已经全部解析完。

Tomcat中对请求数据的处理,其实就是重复这四个这个过程,把数据从操作系统读取到Tomcat缓存,然后逐个字节进行解析。我们后面详细分析。

2、字节块(ByteChunk),一种数据结构。有三个重要属性:字节数组buff,start,end。我们从三个属性可以看出,字节块是利用两个下标,标记了一个字节数组中的一段字节。在数据被使用时才把标记的字节转换成字符串,且相同的字节段,如果已经有字符串对应,则会共用该字符串。这样做最大的好处是提高效率、减少内存使用。如下图标记了字节块下标1-4的字节。

Tomcat请求解析-请求行和请求头

3、HTTP请求数据格式如下

Tomcat请求解析-请求行和请求头

 

整个请求数据的解析过程实际就是根据HTTP规范逐个字节分析,最终转换成请求对象的过程,因此有必要对HTTP格式有了解

下面我们进入主题,通过源码分析请求行和请求头的解析过程

首先进入HTTP11处理器中处理请求的入口

  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         /**
 10          * 设置socket的InputStream和OutStream,供后面读取数据和响应使用
 11          */
 12         getInputBuffer().init(socketWrapper, endpoint);
 13         getOutputBuffer().init(socketWrapper, endpoint);
 14 
 15         // Flags
 16         keepAlive = true;
 17         comet = false;
 18         openSocket = false;
 19         sendfileInProgress = false;
 20         readComplete = true;
 21         if (endpoint.getUsePolling()) {
 22             keptAlive = false;
 23         } else {
 24             keptAlive = socketWrapper.isKeptAlive();
 25         }
 26 
 27         /**
 28          * 长连接相关,判断当前socket是否继续处理接下来的请求
 29          */
 30         if (disableKeepAlive()) {
 31             socketWrapper.setKeepAliveLeft(0);
 32         }
 33 
 34         /**
 35          * 处理socket中的请求,在长连接的模式下,每次循环表示一个HTTP请求
 36          */
 37         while (!getErrorState().isError() && keepAlive && !comet && !isAsync() &&
 38                 upgradeInbound == null &&
 39                 httpUpgradeHandler == null && !endpoint.isPaused()) {
 40 
 41             // Parsing the request header
 42             try {
 43                 /**
 44                  * 1、设置socket超时时间
 45                  * 2、第一次从socket中读取数据
 46                  */
 47                 setRequestLineReadTimeout();
 48 
 49                 /**
 50                  * 读取请求行
 51                  */
 52                 if (!getInputBuffer().parseRequestLine(keptAlive)) {
 53                     if (handleIncompleteRequestLineRead()) {
 54                         break;
 55                     }
 56                 }
 57 
 58                 // Process the Protocol component of the request line
 59                 // Need to know if this is an HTTP 0.9 request before trying to
 60                 // parse headers.
 61                 prepareRequestProtocol();
 62 
 63                 if (endpoint.isPaused()) {
 64                     // 503 - Service unavailable
 65                     response.setStatus(503);
 66                     setErrorState(ErrorState.CLOSE_CLEAN, null);
 67                 } else {
 68                     keptAlive = true;
 69                     // Set this every time in case limit has been changed via JMX
 70                     // 设置请求头数量
 71                     request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
 72                     // 设置做多可设置cookie数量
 73                     request.getCookies().setLimit(getMaxCookieCount());
 74                     // Currently only NIO will ever return false here
 75                     // Don't parse headers for HTTP/0.9
 76                     /**
 77                      * 读取请求头
 78                      */
 79                     if (!http09 && !getInputBuffer().parseHeaders()) {
 80                         // We've read part of the request, don't recycle it
 81                         // instead associate it with the socket
 82                         openSocket = true;
 83                         readComplete = false;
 84                         break;
 85                     }
 86                     if (!disableUploadTimeout) {
 87                         setSocketTimeout(connectionUploadTimeout);
 88                     }
 89                 }
 90             } catch (IOException e) {
 91                 if (getLog().isDebugEnabled()) {
 92                     getLog().debug(
 93                             sm.getString("http11processor.header.parse"), e);
 94                 }
 95                 setErrorState(ErrorState.CLOSE_NOW, e);
 96                 break;
 97             } catch (Throwable t) {
 98                 ExceptionUtils.handleThrowable(t);
 99                 UserDataHelper.Mode logMode = userDataHelper.getNextMode();
100                 if (logMode != null) {
101                     String message = sm.getString(
102                             "http11processor.header.parse");
103                     switch (logMode) {
104                         case INFO_THEN_DEBUG:
105                             message += sm.getString(
106                                     "http11processor.fallToDebug");
107                             //$FALL-THROUGH$
108                         case INFO:
109                             getLog().info(message, t);
110                             break;
111                         case DEBUG:
112                             getLog().debug(message, t);
113                     }
114                 }
115                 // 400 - Bad Request
116                 response.setStatus(400);
117                 setErrorState(ErrorState.CLOSE_CLEAN, t);
118                 getAdapter().log(request, response, 0);
119             }
120 
121             if (!getErrorState().isError()) {
122                 // Setting up filters, and parse some request headers
123                 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
124                 try {
125                     prepareRequest();
126                 } catch (Throwable t) {
127                     ExceptionUtils.handleThrowable(t);
128                     if (getLog().isDebugEnabled()) {
129                         getLog().debug(sm.getString(
130                                 "http11processor.request.prepare"), t);
131                     }
132                     // 500 - Internal Server Error
133                     response.setStatus(500);
134                     setErrorState(ErrorState.CLOSE_CLEAN, t);
135                     getAdapter().log(request, response, 0);
136                 }
137             }
138 
139             if (maxKeepAliveRequests == 1) {
140                 keepAlive = false;
141             } else if (maxKeepAliveRequests > 0 &&
142                     socketWrapper.decrementKeepAlive() <= 0) {
143                 keepAlive = false;
144             }
145 
146             // Process the request in the adapter
147             if (!getErrorState().isError()) {
148                 try {
149                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
150                     /**
151                      * 将封装好的请求和响应对象,交由容器处理
152                      * service-->host-->context-->wrapper-->servlet
153                      * 这里非常重要,我们所写的servlet代码正是这里在调用,它遵循了Servlet规范
154                      * 这里处理完,代表程序员开发的servlet已经执行完毕
155                      */
156                     adapter.service(request, response);
157                     // Handle when the response was committed before a serious
158                     // error occurred.  Throwing a ServletException should both
159                     // set the status to 500 and set the errorException.
160                     // If we fail here, then the response is likely already
161                     // committed, so we can't try and set headers.
162                     if(keepAlive && !getErrorState().isError() && (
163                             response.getErrorException() != null ||
164                                     (!isAsync() &&
165                                     statusDropsConnection(response.getStatus())))) {
166                         setErrorState(ErrorState.CLOSE_CLEAN, null);
167                     }
168                     setCometTimeouts(socketWrapper);
169                 } catch (InterruptedIOException e) {
170                     setErrorState(ErrorState.CLOSE_NOW, e);
171                 } catch (HeadersTooLargeException e) {
172                     getLog().error(sm.getString("http11processor.request.process"), e);
173                     // The response should not have been committed but check it
174                     // anyway to be safe
175                     if (response.isCommitted()) {
176                         setErrorState(ErrorState.CLOSE_NOW, e);
177                     } else {
178                         response.reset();
179                         response.setStatus(500);
180                         setErrorState(ErrorState.CLOSE_CLEAN, e);
181                         response.setHeader("Connection", "close"); // TODO: Remove
182                     }
183                 } catch (Throwable t) {
184                     ExceptionUtils.handleThrowable(t);
185                     getLog().error(sm.getString("http11processor.request.process"), t);
186                     // 500 - Internal Server Error
187                     response.setStatus(500);
188                     setErrorState(ErrorState.CLOSE_CLEAN, t);
189                     getAdapter().log(request, response, 0);
190                 }
191             }
192 
193             // Finish the handling of the request
194             rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
195 
196             if (!isAsync() && !comet) {
197                 if (getErrorState().isError()) {
198                     // If we know we are closing the connection, don't drain
199                     // input. This way uploading a 100GB file doesn't tie up the
200                     // thread if the servlet has rejected it.
201                     getInputBuffer().setSwallowInput(false);
202                 } else {
203                     // Need to check this again here in case the response was
204                     // committed before the error that requires the connection
205                     // to be closed occurred.
206                     checkExpectationAndResponseStatus();
207                 }
208                 /**
209                  * 请求收尾工作
210                  * 判断请求体是否读取完毕,没有则读取完毕,并修正pos
211                  * 请求体读取分为两种:
212                  * 1、程序员读取:在servlet中有程序员主动读取,这种方式读取数据不一定读取完整数据,取决于业务需求
213                  * 2、Tomcat自己读取:如果servlet中没有读取,或者没有读取完全,则Tomcat负责读取剩余的请求体
214                  * 1和2的差别在于,2中仅仅把数据从操作系统读取到buf中,尽管也用了字节块做标记,但是不会做其他的事情,而1中还会把字节块标记的数据拷贝到目标数组中
215                  * 这个方法就是处理情况2中的请求体读取逻辑
216                  */
217                 endRequest();
218             }
219 
220             rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
221 
222             // If there was an error, make sure the request is counted as
223             // and error, and update the statistics counter
224             if (getErrorState().isError()) {
225                 response.setStatus(500);
226             }
227             request.updateCounters();
228 
229             if (!isAsync() && !comet || getErrorState().isError()) {
230                 if (getErrorState().isIoAllowed()) {
231                     /**
232                      * 根据修正完的pos和lastValid,初始化数组下标,以便继续处理下一次请求
233                      * 两种情况
234                      * 1、读取请求体刚好读取完,将pos=lastValid=0,即都指向buf数组第一个位置,重新读取数据
235                      * 2、读取请求体多读出了下次请求的数据,这个时候需要将下个请求的数据移动到buf数组头,以便处理下个请求
236                      * 注意,buf数组中的数据没有删除,是直接覆盖,从而达到对buf数组的重复使用
237                      */
238                     getInputBuffer().nextRequest();
239                     getOutputBuffer().nextRequest();
240                 }
241             }
242 
243             if (!disableUploadTimeout) {
244                 if(endpoint.getSoTimeout() > 0) {
245                     setSocketTimeout(endpoint.getSoTimeout());
246                 } else {
247                     setSocketTimeout(0);
248                 }
249             }
250 
251             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
252 
253             if (breakKeepAliveLoop(socketWrapper)) {
254                 break;
255             }
256         }
257 
258         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
259 
260         if (getErrorState().isError() || endpoint.isPaused()) {
261             return SocketState.CLOSED;
262         } else if (isAsync() || comet) {
263             return SocketState.LONG;
264         } else if (isUpgrade()) {
265             return SocketState.UPGRADING;
266         } else if (getUpgradeInbound() != null) {
267             return SocketState.UPGRADING_TOMCAT;
268         } else {
269             if (sendfileInProgress) {
270                 return SocketState.SENDFILE;
271             } else {
272                 if (openSocket) {
273                     if (readComplete) {
274                         return SocketState.OPEN;
275                     } else {
276                         return SocketState.LONG;
277                     }
278                 } else {
279                     return SocketState.CLOSED;
280                 }
281             }
282         }
283     }
View Code

相关文章: