前面分析了tomcat启动,见
Tomcat启动源代码分析。启动分析的后面已经涉及到了对客户端连接进来的socket的处理的,那么今天的文章就沿着上面的文章写下去吧。
一、Connector处理请求
MasterSlaveWorkerThread调用PoolTcpEndpoint进行处理:
// Process the request from this socket
endpoint.processSocket(socket, con, threadData);
thereaDate是一个对象数组Object[] threadData,con是TcpConnection con = new TcpConnection()。
PoolTcpEndpoint进行处理:
void processSocket(Socket s, TcpConnection con, Object[] threadData) {
// Process the connection
int step = 1;
try {
// 1: Set socket options: timeout, linger, etc
setSocketOptions(s);
// 2: SSL handshake
step = 2;
if (getServerSocketFactory() != null) {
getServerSocketFactory().handshake(s);
}
// 3: Process the connection
step = 3;
con.setEndpoint(this);
con.setSocket(s);
getConnectionHandler().processConnection(con, threadData);
} catch (SocketException se) {
log.debug(sm.getString("endpoint.err.socket", s.getInetAddress()),
se);
// Try to close the socket
try {
s.close();
} catch (IOException e) {
}
} catch (Throwable t) {
if (step == 2) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.err.handshake"), t);
}
} else {
log.error(sm.getString("endpoint.err.unexpected"), t);
}
// Try to close the socket
try {
s.close();
} catch (IOException e) {
}
} finally {
if (con != null) {
con.recycle();
}
}
}
最后交由connectionHandler即Http11ConnectionHandler进行处理:
public void processConnection(TcpConnection connection,
Object thData[]) {
Socket socket=null;
Http11Processor processor=null;
try {
processor=(Http11Processor)thData[Http11BaseProtocol.THREAD_DATA_PROCESSOR];
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_START, null);
}
socket=connection.getSocket();
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
if( proto.secure ) {
SSLSupport sslSupport=null;
if(proto.sslImplementation != null)
sslSupport = proto.sslImplementation.getSSLSupport(socket);
processor.setSSLSupport(sslSupport);
} else {
processor.setSSLSupport( null );
}
processor.setSocket( socket );
processor.process(in, out);
// If unread input arrives after the shutdownInput() call
// below and before or during the socket.close(), an error
// may be reported to the client. To help troubleshoot this
// type of error, provide a configurable delay to give the
// unread input time to arrive so it can be successfully read
// and discarded by shutdownInput().
if( proto.socketCloseDelay >= 0 ) {
try {
Thread.sleep(proto.socketCloseDelay);
} catch (InterruptedException ie) { /* ignore */ }
}
TcpConnection.shutdownInput( socket );
} catch(java.net.SocketException e) {
// SocketExceptions are normal
Http11BaseProtocol.log.debug
(sm.getString
("http11protocol.proto.socketexception.debug"), e);
} catch (IOException e) {
// IOExceptions are normal
Http11BaseProtocol.log.debug
(sm.getString
("http11protocol.proto.ioexception.debug"), e);
}
// Future developers: if you discover any other
// rare-but-nonfatal exceptions, catch them here, and log as
// above.
catch (Throwable e) {
// any other exception or error is odd. Here we log it
// with "ERROR" level, so it will show up even on
// less-than-verbose logs.
Http11BaseProtocol.log.error
(sm.getString("http11protocol.proto.error"), e);
} finally {
// if(proto.adapter != null) proto.adapter.recycle();
// processor.recycle();
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
}
// recycle kernel sockets ASAP
try { if (socket != null) socket.close (); }
catch (IOException e) { /* ignore */ }
}
}
}
Http11Processor进行处理,这里是比较细节的东西:选取重要的代码观摩一下:
inputBuffer.parseRequestLine();
inputBuffer.parseHeaders();
adapter.service(request, response);
上面第一条就是处理请求行,一般如下:
POST /loan/control/customer HTTP/1.0
获取请求方法,请求的URI和协议名称及版本。
第二条很明显就是处理请求的报头,详见
Http 协议头基础。
最后是交给Adapter进行处理。这个Adapter是
CoyoteAdapter,CoyotConnector初始化的时候设置的。见上次文章中初始化CoyotConnector部分。
CoyoteAdapter的service方法:
/**
* Service method.
*/
public void service(Request req, Response res)
throws Exception {
CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = (CoyoteRequest) connector.createRequest();
request.setCoyoteRequest(req);
response = (CoyoteResponse) connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
try {
// Parse and set Catalina and configuration specific
// request parameters
postParseRequest(req, request, res, response);
// Calling the container
connector.getContainer().invoke(request, response);
response.finishResponse();
req.action( ActionCode.ACTION_POST_REQUEST , null);
} catch (IOException e) {
;
} catch (Throwable t) {
log(sm.getString("coyoteAdapter.service"), t);
} finally {
// Recycle the wrapper request and response
request.recycle();
response.recycle();
}
}
调用容器的invoke方法,这个容器就是StandardEngine。现在连接器已经将request和response对象准备好,交由容器处理了。上面的处理主要是Connector处理,流程大致如下:
下面研究容器是怎么处理请求的。
二、容器处理请求
上面说到StandardEngine接收到请求,invoke方法调用。方法非常简单:
public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);
}
容器并不直接处理,而是交给一个pipeline的东东,这个pipeline里面会放置一些vavle,也就是说,请求沿着pipeline传递下去并且vavle对其进行相关的处理。这个valve有简单的,也有复杂的,简单的就是起个接力棒的作用,复杂的可以对请求做一些处理,比如说日志等,valve还可以自定义,查看server.xml配置文件就知道了。相关类图如下:
具体可以看一下几个Valve的功能:
StandardEngineValve:
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Validate that any HTTP/1.1 request included a host header
HttpServletRequest hrequest = (HttpServletRequest) request;
if ("HTTP/1.1".equals(hrequest.getProtocol()) &&
(hrequest.getServerName() == null)) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHostHeader",
request.getRequest().getServerName()));
return;
}
// Select the Host to be used for this Request
StandardEngine engine = (StandardEngine) getContainer();
Host host = (Host) engine.map(request, true);
if (host == null) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getRequest().getServerName()));
return;
}
// Ask this Host to process this request
host.invoke(request, response);
}
这个valve验证对象实例的正确性,选择Host并交由其处理。
StandardHostValve:
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Select the Context to be used for this Request
StandardHost host = (StandardHost) getContainer();
Context context = (Context) host.map(request, true);
if (context == null) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
}
// Bind the context CL to the current thread
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
// Update the session last access time for our session (if any)
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String sessionId = hreq.getRequestedSessionId();
if (sessionId != null) {
Manager manager = context.getManager();
if (manager != null) {
Session session = manager.findSession(sessionId);
if ((session != null) && session.isValid())
session.access();
}
}
// Ask this Context to process this request
context.invoke(request, response);
Thread.currentThread().setContextClassLoader
(StandardHostValve.class.getClassLoader());
}
Host的vavle也是先判断对象实例,然后选择相应的Context,查看是否有session信息并更新其最后获取时间(tomcat的session研究放在后面,东西太多了),最后才调用context的invoke方法。StandardContext首先check context是否在reload,如果reload结束或者没有reload那么就和上面几个容器一样,通过valve处理:
StandardContextValve:
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Disallow any direct access to resources under WEB-INF or META-INF
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String contextPath = hreq.getContextPath();
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
String relativeURI =
requestURI.substring(contextPath.length()).toUpperCase();
if (relativeURI.equals("/META-INF") ||
relativeURI.equals("/WEB-INF") ||
relativeURI.startsWith("/META-INF/") ||
relativeURI.startsWith("/WEB-INF/")) {
notFound(requestURI, (HttpServletResponse) response.getResponse());
return;
}
Context context = (Context) getContainer();
// Select the Wrapper to be used for this Request
Wrapper wrapper = null;
try {
wrapper = (Wrapper) context.map(request, true);
} catch (IllegalArgumentException e) {
badRequest(requestURI,
(HttpServletResponse) response.getResponse());
return;
}
if (wrapper == null) {
notFound(requestURI, (HttpServletResponse) response.getResponse());
return;
}
// Ask this Wrapper to process this Request
response.setContext(context);
wrapper.invoke(request, response);
}
常规检查以后是安全请求检查,然后得到一个wrapper,这个wrapper是啥呢?这就得StandardContextMapper,它的map方法大家可以研究一下,其实就是通过请求路径查找相应class的过程,查找的优先级一目了然,首先是精确匹配,然后是前缀匹配,扩展匹配最后是默认匹配。Wrapper其实就是封装了一个servlet。其valve类StandardWrapperValve的invoke方法有点长,大致功能如下:实例检查,可获得性检查,获取servlet实例,Filter和Servlet执行。这个时候就会调用servlet的service方法,交给我们自定义的servlet进行实际的业务处理。