前面分析了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进行实际的业务处理。