前面分析了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?,F在連接器已經將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進行實際的業務處理。