正文
上文
分析到了
org.apache.coyote.http11.AbstractHttp11Processor
类 process 方法,以解析请求头的 getInputBuffer().parseRequestLine 方法调用为例,看到如何从 Socket 的 IO 流中取出字节流数据,根据 Http 协议将字节流组装到 Tomcat 内部的
org.apache.coyote.Request
对象的相关属性中。
接下来将会解释构造好的 Tomcat 的内部请求对象从 Connector 到 Engine 到 Host 到 Context 最后到 Servlet 的过程。
回到
org.apache.coyote.http11.AbstractHttp11Processor
类 process 方法的源码:
1 public SocketState process(SocketWrapper<S> socketWrapper)
2 throws IOException {
3 RequestInfo rp = request.getRequestProcessor();
4 rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
5
6 // Setting up the I/O
7 setSocketWrapper(socketWrapper);
8 getInputBuffer().init(socketWrapper, endpoint);
9 getOutputBuffer().init(socketWrapper, endpoint);
10
11 // Flags
12 error = false;
13 keepAlive = true;
14 comet = false;
15 openSocket = false;
16 sendfileInProgress = false;
17 readComplete = true;
18 if (endpoint.getUsePolling()) {
19 keptAlive = false;
20 } else {
21 keptAlive = socketWrapper.isKeptAlive();
22 }
23
24 if (disableKeepAlive()) {
25 socketWrapper.setKeepAliveLeft(0);
26 }
27
28 while (!error && keepAlive && !comet && !isAsync() &&
29 upgradeInbound == null && !endpoint.isPaused()) {
30
31 // Parsing the request header
32 try {
33 setRequestLineReadTimeout();
34
35 if (!getInputBuffer().parseRequestLine(keptAlive)) {
36 if (handleIncompleteRequestLineRead()) {
37 break;
38 }
39 }
40
41 if (endpoint.isPaused()) {
42 // 503 - Service unavailable
43 response.setStatus(503);
44 error = true;
45 } else {
46 // Make sure that connectors that are non-blocking during
47 // header processing (NIO) only set the start time the first
48 // time a request is processed.
49 if (request.getStartTime() < 0) {
50 request.setStartTime(System.currentTimeMillis());
51 }
52 keptAlive = true;
53 // Set this every time in case limit has been changed via JMX
54 request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
55 // Currently only NIO will ever return false here
56 if (!getInputBuffer().parseHeaders()) {
57 // We've read part of the request, don't recycle it
58 // instead associate it with the socket
59 openSocket = true;
60 readComplete = false;
61 break;
62 }
63 if (!disableUploadTimeout) {
64 setSocketTimeout(connectionUploadTimeout);
65 }
66 }
67 } catch (IOException e) {
68 if (getLog().isDebugEnabled()) {
69 getLog().debug(
70 sm.getString("http11processor.header.parse"), e);
71 }
72 error = true;
73 break;
74 } catch (Throwable t) {
75 ExceptionUtils.handleThrowable(t);
76 UserDataHelper.Mode logMode = userDataHelper.getNextMode();
77 if (logMode != null) {
78 String message = sm.getString(
79 "http11processor.header.parse");
80 switch (logMode) {
81 case INFO_THEN_DEBUG:
82 message += sm.getString(
83 "http11processor.fallToDebug");
84 //$FALL-THROUGH$
85 case INFO:
86 getLog().info(message);
87 break;
88 case DEBUG:
89 getLog().debug(message);
90 }
91 }
92 // 400 - Bad Request
93 response.setStatus(400);
94 adapter.log(request, response, 0);
95 error = true;
96 }
97
98 if (!error) {
99 // Setting up filters, and parse some request headers
100 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
101 try {
102 prepareRequest();
103 } catch (Throwable t) {
104 ExceptionUtils.handleThrowable(t);
105 if (getLog().isDebugEnabled()) {
106 getLog().debug(sm.getString(
107 "http11processor.request.prepare"), t);
108 }
109 // 400 - Internal Server Error
110 response.setStatus(400);
111 adapter.log(request, response, 0);
112 error = true;
113 }
114 }
115
116 if (maxKeepAliveRequests == 1) {
117 keepAlive = false;
118 } else if (maxKeepAliveRequests > 0 &&
119 socketWrapper.decrementKeepAlive() <= 0) {
120 keepAlive = false;
121 }
122
123 // Process the request in the adapter
124 if (!error) {
125 try {
126 rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
127 adapter.service(request, response);
128 // Handle when the response was committed before a serious
129 // error occurred. Throwing a ServletException should both
130 // set the status to 500 and set the errorException.
131 // If we fail here, then the response is likely already
132 // committed, so we can't try and set headers.
133 if(keepAlive && !error) { // Avoid checking twice.
134 error = response.getErrorException() != null ||
135 (!isAsync() &&
136 statusDropsConnection(response.getStatus()));
137 }
138 setCometTimeouts(socketWrapper);
139 } catch (InterruptedIOException e) {
140 error = true;
141 } catch (HeadersTooLargeException e) {
142 error = true;
143 // The response should not have been committed but check it
144 // anyway to be safe
145 if (!response.isCommitted()) {
146 response.reset();
147 response.setStatus(500);
148 response.setHeader("Connection", "close");
149 }
150 } catch (Throwable t) {
151 ExceptionUtils.handleThrowable(t);
152 getLog().error(sm.getString(
153 "http11processor.request.process"), t);
154 // 500 - Internal Server Error
155 response.setStatus(500);
156 adapter.log(request, response, 0);
157 error = true;
158 }
159 }
160
161 // Finish the handling of the request
162 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
163
164 if (!isAsync() && !comet) {
165 if (error) {
166 // If we know we are closing the connection, don't drain
167 // input. This way uploading a 100GB file doesn't tie up the
168 // thread if the servlet has rejected it.
169 getInputBuffer().setSwallowInput(false);
170 }
171 endRequest();
172 }
173
174 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
175
176 // If there was an error, make sure the request is counted as
177 // and error, and update the statistics counter
178 if (error) {
179 response.setStatus(500);
180 }
181 request.updateCounters();
182
183 if (!isAsync() && !comet || error) {
184 getInputBuffer().nextRequest();
185 getOutputBuffer().nextRequest();
186 }
187
188 if (!disableUploadTimeout) {
189 if(endpoint.getSoTimeout() > 0) {
190 setSocketTimeout(endpoint.getSoTimeout());
191 } else {
192 setSocketTimeout(0);
193 }
194 }
195
196 rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
197
198 if (breakKeepAliveLoop(socketWrapper)) {
199 break;
200 }
201 }
202
203 rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
204
205 if (error || endpoint.isPaused()) {
206 return SocketState.CLOSED;
207 } else if (isAsync() || comet) {
208 return SocketState.LONG;
209 } else if (isUpgrade()) {
210 return SocketState.UPGRADING;
211 } else {
212 if (sendfileInProgress) {
213 return SocketState.SENDFILE;
214 } else {
215 if (openSocket) {
216 if (readComplete) {
217 return SocketState.OPEN;
218 } else {
219 return SocketState.LONG;
220 }
221 } else {
222 return SocketState.CLOSED;
223 }
224 }
225 }
226 }
概述一下这个方法做的事情:第 3 到 26 行主要是在初始化变量。关注接下来一大段的 while 循环里面的代码,第 31 到 121 行在解析请求头,第 123 到 159 行将请求交由适配器( adapter )处理,第 161 到 200 行结束请求的处理(做一些收尾工作,比如废弃剩下的无意义字节流数据,设置相应状态码等)。
请求对象在容器中的流转在第 127 行:
adapter.service(request, response);
这里的 adapter 对象是在 Http11Processor 对象创建的时候设置的,见
org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler
类的 createProcessor 方法:
1 protected Http11Processor createProcessor() {
2 Http11Processor processor = new Http11Processor(
3 proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
4 proto.getMaxTrailerSize());
5 processor.setAdapter(proto.adapter);
6 processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
7 processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
8 processor.setConnectionUploadTimeout(
9 proto.getConnectionUploadTimeout());
10 processor.setDisableUploadTimeout(proto.getDisableUploadTimeout());
11 processor.setCompressionMinSize(proto.getCompressionMinSize());
12 processor.setCompression(proto.getCompression());
13 processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents());
14 processor.setCompressableMimeTypes(proto.getCompressableMimeTypes());
15 processor.setRestrictedUserAgents(proto.getRestrictedUserAgents());
16 processor.setSocketBuffer(proto.getSocketBuffer());
17 processor.setMaxSavePostSize(proto.getMaxSavePostSize());
18 processor.setServer(proto.getServer());
19 processor.setDisableKeepAlivePercentage(
20 proto.getDisableKeepAlivePercentage());
21 register(processor);
22 return processor;
23 } }
可以看到 adapter 对象设置的是
org.apache.coyote.http11.Http11Protocol
的 adapter 变量,而该变量是在 Connector 类的 initInternal 方法中设值的:
1 protected void initInternal() throws LifecycleException {
2
3 super.initInternal();
4
5 // Initialize adapter
6 adapter = new CoyoteAdapter(this);
7 protocolHandler.setAdapter(adapter);
8
9 // Make sure parseBodyMethodsSet has a default
10 if( null == parseBodyMethodsSet ) {
11 setParseBodyMethods(getParseBodyMethods());
12 }
13
14 if (protocolHandler.isAprRequired() &&
15 !AprLifecycleListener.isAprAvailable()) {
16 throw new LifecycleException(
17 sm.getString("coyoteConnector.protocolHandlerNoApr",
18 getProtocolHandlerClassName()));
19 }
20
21 try {
22 protocolHandler.init();
23 } catch (Exception e) {
24 throw new LifecycleException
25 (sm.getString
26 ("coyoteConnector.protocolHandlerInitializationFailed"), e);
27 }
28
29 // Initialize mapper listener
30 mapperListener.init();
31 }
第 6、7 行就是初始化 adapter 对象并设值到 Http11Protocol 对象中的。
所以上面看到的
adapter.service(request, response)
方法实际执行的是
org.apache.catalina.connector.CoyoteAdapter
类的 service 方法:
1 public void service(org.apache.coyote.Request req,
2 org.apache.coyote.Response res)
3 throws Exception {
4
5 Request request = (Request) req.getNote(ADAPTER_NOTES);
6 Response response = (Response) res.getNote(ADAPTER_NOTES);
7
8 if (request == null) {
9
10 // Create objects
11 request = connector.createRequest();
12 request.setCoyoteRequest(req);
13 response = connector.createResponse();
14 response.setCoyoteResponse(res);
15
16 // Link objects
17 request.setResponse(response);
18 response.setRequest(request);
19
20 // Set as notes
21 req.setNote(ADAPTER_NOTES, request);
22 res.setNote(ADAPTER_NOTES, response);
23
24 // Set query string encoding
25 req.getParameters().setQueryStringEncoding
26 (connector.getURIEncoding());
27
28 }
29
30 if (connector.getXpoweredBy()) {
31 response.addHeader("X-Powered-By", POWERED_BY);
32 }
33
34 boolean comet = false;
35 boolean async = false;
36
37 try {
38
39 // Parse and set Catalina and configuration specific
40 // request parameters
41 req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
42 boolean postParseSuccess = postParseRequest(req, request, res, response);
43 if (postParseSuccess) {
44 //check valves if we support async
45 request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
46 // Calling the container
47 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
48
49 if (request.isComet()) {
50 if (!response.isClosed() && !response.isError()) {
51 if (request.getAvailable() || (request.getContentLength() > 0 && (!request.isParametersParsed()))) {
52 // Invoke a read event right away if there are available bytes
53 if (event(req, res, SocketStatus.OPEN)) {
54 comet = true;
55 res.action(ActionCode.COMET_BEGIN, null);
56 }
57 } else {
58 comet = true;
59 res.action(ActionCode.COMET_BEGIN, null);
60 }
61 } else {
62 // Clear the filter chain, as otherwise it will not be reset elsewhere
63 // since this is a Comet request
64 request.setFilterChain(null);
65 }
66 }
67
68 }
69 AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
70 if (asyncConImpl != null) {
71 async = true;
72 } else if (!comet) {
73 request.finishRequest();
74 response.finishResponse();
75 if (postParseSuccess &&
76 request.getMappingData().context != null) {
77 // Log only if processing was invoked.
78 // If postParseRequest() failed, it has already logged it.
79 // If context is null this was the start of a comet request
80 // that failed and has already been logged.
81 ((Context) request.getMappingData().context).logAccess(
82 request, response,
83 System.currentTimeMillis() - req.getStartTime(),
84 false);
85 }
86 req.action(ActionCode.POST_REQUEST , null);
87 }
88
89 } catch (IOException e) {
90 // Ignore
91 } finally {
92 req.getRequestProcessor().setWorkerThreadName(null);
93 // Recycle the wrapper request and response
94 if (!comet && !async) {
95 request.recycle();
96 response.recycle();
97 } else {
98 // Clear converters so that the minimum amount of memory
99 // is used by this processor
100 request.clearEncoders();
101 response.clearEncoders();
102 }
103 }
104
105 }
这段代码中可以看到入参
org.apache.coyote.Request
对象被转成了
org.apache.catalina.connector.Request
对象,后一类型的对象才是在 Tomcat 容器流转时真正传递的对象。重点关注第 42 行和第 47 行。
在第 42 行调用了 postParseRequest 方法:
1 /**
2 * Parse additional request parameters.
3 */
4 protected boolean postParseRequest(org.apache.coyote.Request req,
5 Request request,
6 org.apache.coyote.Response res,
7 Response response)
8 throws Exception {
9
10 // XXX the processor may have set a correct scheme and port prior to this point,
11 // in ajp13 protocols dont make sense to get the port from the connector...
12 // otherwise, use connector configuration
13 if (! req.scheme().isNull()) {
14 // use processor specified scheme to determine secure state
15 request.setSecure(req.scheme().equals("https"));
16 } else {
17 // use connector scheme and secure configuration, (defaults to
18 // "http" and false respectively)
19 req.scheme().setString(connector.getScheme());
20 request.setSecure(connector.getSecure());
21 }
22
23 // FIXME: the code below doesnt belongs to here,
24 // this is only have sense
25 // in Http11, not in ajp13..
26 // At this point the Host header has been processed.
27 // Override if the proxyPort/proxyHost are set
28 String proxyName = connector.getProxyName();
29 int proxyPort = connector.getProxyPort();
30 if (proxyPort != 0) {
31 req.setServerPort(proxyPort);
32 }
33 if (proxyName != null) {
34 req.serverName().setString(proxyName);
35 }
36
37 // Copy the raw URI to the decodedURI
38 MessageBytes decodedURI = req.decodedURI();
39 decodedURI.duplicate(req.requestURI());
40
41 // Parse the path parameters. This will:
42 // - strip out the path parameters
43 // - convert the decodedURI to bytes
44 parsePathParameters(req, request);
45
46 // URI decoding
47 // %xx decoding of the URL
48 try {
49 req.getURLDecoder().convert(decodedURI, false);
50 } catch (IOException ioe) {
51 res.setStatus(400);
52 res.setMessage("Invalid URI: " + ioe.getMessage());
53 connector.getService().getContainer().logAccess(
54 request, response, 0, true);
55 return false;
56 }
57 // Normalization
58 if (!normalize(req.decodedURI())) {
59 res.setStatus(400);
60 res.setMessage("Invalid URI");
61 connector.getService().getContainer().logAccess(
62 request, response, 0, true);
63 return false;
64 }
65 // Character decoding
66 convertURI(decodedURI, request);
67 // Check that the URI is still normalized
68 if (!checkNormalize(req.decodedURI())) {
69 res.setStatus(400);
70 res.setMessage("Invalid URI character encoding");
71 connector.getService().getContainer().logAccess(
72 request, response, 0, true);
73 return false;
74 }
75
76 // Set the remote principal
77 String principal = req.getRemoteUser().toString();
78 if (principal != null) {
79 request.setUserPrincipal(new CoyotePrincipal(principal));
80 }
81
82 // Set the authorization type
83 String authtype = req.getAuthType().toString();
84 if (authtype != null) {
85 request.setAuthType(authtype);
86 }
87
88 // Request mapping.
89 MessageBytes serverName;
90 if (connector.getUseIPVHosts()) {
91 serverName = req.localName();
92 if (serverName.isNull()) {
93 // well, they did ask for it
94 res.action(ActionCode.REQ_LOCAL_NAME_ATTRIBUTE, null);
95 }
96 } else {
97 serverName = req.serverName();
98 }
99 if (request.isAsyncStarted()) {
100 //TODO SERVLET3 - async
101 //reset mapping data, should prolly be done elsewhere
102 request.getMappingData().recycle();
103 }
104
105 boolean mapRequired = true;
106 String version = null;
107
108 while (mapRequired) {
109 if (version != null) {
110 // Once we have a version - that is it
111 mapRequired = false;
112 }
113 // This will map the the latest version by default
114 connector.getMapper().map(serverName, decodedURI, version,
115 request.getMappingData());
116 request.setContext((Context) request.getMappingData().context);
117 request.setWrapper((Wrapper) request.getMappingData().wrapper);
118
119 // Single contextVersion therefore no possibility of remap
120 if (request.getMappingData().contexts == null) {
121 mapRequired = false;
122 }
123
124 // If there is no context at this point, it is likely no ROOT context
125 // has been deployed
126 if (request.getContext() == null) {
127 res.setStatus(404);
128 res.setMessage("Not found");
129 // No context, so use host
130 Host host = request.getHost();
131 // Make sure there is a host (might not be during shutdown)
132 if (host != null) {
133 host.logAccess(request, response, 0, true);
134 }
135 return false;
136 }
137
138 // Now we have the context, we can parse the session ID from the URL
139 // (if any). Need to do this before we redirect in case we need to
140 // include the session id in the redirect
141 String sessionID = null;
142 if (request.getServletContext().getEffectiveSessionTrackingModes()
143 .contains(SessionTrackingMode.URL)) {
144
145 // Get the session ID if there was one
146 sessionID = request.getPathParameter(
147 SessionConfig.getSessionUriParamName(
148 request.getContext()));
149 if (sessionID != null) {
150 request.setRequestedSessionId(sessionID);
151 request.setRequestedSessionURL(true);
152 }
153 }
154
155 // Look for session ID in cookies and SSL session
156 parseSessionCookiesId(req, request);
157 parseSessionSslId(request);
158
159 sessionID = request.getRequestedSessionId();
160
161 if (mapRequired) {
162 if (sessionID == null) {
163 // No session means no possibility of needing to remap
164 mapRequired = false;
165 } else {
166 // Find the context associated with the session
167 Object[] objs = request.getMappingData().contexts;
168 for (int i = (objs.length); i > 0; i--) {
169 Context ctxt = (Context) objs[i - 1];
170 if (ctxt.getManager().findSession(sessionID) != null) {
171 // Was the correct context already mapped?
172 if (ctxt.equals(request.getMappingData().context)) {
173 mapRequired = false;
174 } else {
175 // Set version so second time through mapping the
176 // correct context is found
177 version = ctxt.getWebappVersion();
178 // Reset mapping
179 request.getMappingData().recycle();
180 break;
181 }
182 }
183 }
184 if (version == null) {
185 // No matching context found. No need to re-map
186 mapRequired = false;
187 }
188 }
189 }
190 if (!mapRequired && request.getContext().getPaused()) {
191 // Found a matching context but it is paused. Mapping data will
192 // be wrong since some Wrappers may not be registered at this
193 // point.
194 try {
195 Thread.sleep(1000);
196 } catch (InterruptedException e) {
197 // Should never happen
198 }
199 // Reset mapping
200 request.getMappingData().recycle();
201 mapRequired = true;
202 }
203 }
204
205 // Possible redirect
206 MessageBytes redirectPathMB = request.getMappingData().redirectPath;
207 if (!redirectPathMB.isNull()) {
208 String redirectPath = urlEncoder.encode(redirectPathMB.toString());
209 String query = request.getQueryString();
210 if (request.isRequestedSessionIdFromURL()) {
211 // This is not optimal, but as this is not very common, it
212 // shouldn't matter
213 redirectPath = redirectPath + ";" +
214 SessionConfig.getSessionUriParamName(
215 request.getContext()) +
216 "=" + request.getRequestedSessionId();
217 }
218 if (query != null) {
219 // This is not optimal, but as this is not very common, it
220 // shouldn't matter
221 redirectPath = redirectPath + "?" + query;
222 }
223 response.sendRedirect(redirectPath);
224 request.getContext().logAccess(request, response, 0, true);
225 return false;
226 }
227
228 // Filter trace method
229 if (!connector.getAllowTrace()
230 && req.method().equalsIgnoreCase("TRACE")) {
231 Wrapper wrapper = request.getWrapper();
232 String header = null;
233 if (wrapper != null) {
234 String[] methods = wrapper.getServletMethods();
235 if (methods != null) {
236 for (int i=0; i<methods.length; i++) {
237 if ("TRACE".equals(methods[i])) {
238 continue;
239 }
240 if (header == null) {
241 header = methods[i];
242 } else {
243 header += ", " + methods[i];
244 }
245 }
246 }
247 }
248 res.setStatus(405);
249 res.addHeader("Allow", header);
250 res.setMessage("TRACE method is not allowed");
251 request.getContext().logAccess(request, response, 0, true);
252 return false;
253 }
254
255 return true;
256 }
这段代码的主要作用是给
org.apache.catalina.connector.Request
对象设值,其中第 113 到 117 行: