一、前言
文章:https://www.cnblogs.com/runnable/p/12905401.html中介绍了Tomcat处理一次请求的大致过程,其中包括请求接收、请求数据处理以及请求响应。接下来用两篇文章详细分析请求数据解析:请求行和请求头的读取、请求体的读取。
在分析请求数据处理之前,再次回顾一下2个概念
1、Tomcat中用于读取socket数据的缓冲区buf。它是一个字节数组,默认长度8KB。有2个重要的位置下标:pos和lastValid,pos标记下次读取位置,lastValid标记有效数据最后位置。
图中4种情况分别对应:初始数组;刚从操作系统中读取数据到buf;Tomcat解析过程中,已经读取第一位字节;本次从操作系统读取的数据已经全部解析完。
Tomcat中对请求数据的处理,其实就是重复这四个这个过程,把数据从操作系统读取到Tomcat缓存,然后逐个字节进行解析。我们后面详细分析。
2、字节块(ByteChunk),一种数据结构。有三个重要属性:字节数组buff,start,end。我们从三个属性可以看出,字节块是利用两个下标,标记了一个字节数组中的一段字节。在数据被使用时才把标记的字节转换成字符串,且相同的字节段,如果已经有字符串对应,则会共用该字符串。这样做最大的好处是提高效率、减少内存使用。如下图标记了字节块下标1-4的字节。
3、HTTP请求数据格式如下
整个请求数据的解析过程实际就是根据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 }