读书人

tomcat源码分析系列之请求处理-关门打

发布时间: 2012-08-07 14:54:49 作者: rapoo

tomcat源码分析系列之请求处理---关门打狗

??? 上回我们把请求放进来了,这回我们关上门,好好修理修理它,不折腾它一番,休想轻易出去。

要关门打狗,先得知道房子在哪才行啊,上回我们说到proces的处理被委托到Http11Processor类,它就是这套房子!Http11Processor的process方法,就是这间屋子:

/**     * Process pipelined HTTP requests on the specified socket.     *     * @param socketWrapper Socket from which the HTTP requests will be read     *               and the HTTP responses will be written.     *       * @throws IOException error during an I/O operation     */    @Override    public SocketState process(SocketWrapper<Socket> socketWrapper)        throws IOException {        RequestInfo rp = request.getRequestProcessor();        //第一阶段       ?rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);        // Setting up the I/O        this.socket = socketWrapper;        inputBuffer.setInputStream(socket.getSocket().getInputStream());        outputBuffer.setOutputStream(socket.getSocket().getOutputStream());        // Error flag        error = false;        keepAlive = true;        if (maxKeepAliveRequests > 0) {            socketWrapper.decrementKeepAlive();        }                int soTimeout = endpoint.getSoTimeout();        int threadRatio = -1;           // These may return zero or negative values             // Only calculate a thread ratio when both are >0 to ensure we get a            // sensible result              if (endpoint.getCurrentThreadsBusy() >0 &&                      endpoint.getMaxThreads() >0) {                  threadRatio = (endpoint.getCurrentThreadsBusy() * 100)                          / endpoint.getMaxThreads();             }           // Disable keep-alive if we are running low on threads              if (threadRatio > getDisableKeepAlivePercentage()) {                 socketWrapper.setKeepAliveLeft(0);        }        try {            socket.getSocket().setSoTimeout(soTimeout);        } catch (Throwable t) {            ExceptionUtils.handleThrowable(t);            log.debug(sm.getString("http11processor.socket.timeout"), t);            error = true;        }        boolean keptAlive = socketWrapper.isKeptAlive();        while (!error && keepAlive && !endpoint.isPaused()) {            // Parsing the request header            try {                int standardTimeout = 0;                if (keptAlive) {                    if (keepAliveTimeout > 0) {                        standardTimeout = keepAliveTimeout;                    } else if (soTimeout > 0) {                        standardTimeout = soTimeout;                    }                }                /*                 * When there is no data in the buffer and this is not the first                 * request on this connection and timeouts are being used the                 * first read for this request may need a different timeout to                 * take account of time spent waiting for a processing thread.                 *                  * This is a little hacky but better than exposing the socket                 * and the timeout info to the InputBuffer                 */                if (inputBuffer.lastValid == 0 &&                        socketWrapper.getLastAccess() > -1 &&                        standardTimeout > 0) {                    long queueTime = System.currentTimeMillis() -                            socketWrapper.getLastAccess();                    int firstReadTimeout;                    if (queueTime >= standardTimeout) {                        // Queued for longer than timeout but there might be                        // data so use shortest possible timeout                        firstReadTimeout = 1;                    } else {                        // Cast is safe since queueTime must be less than                        // standardTimeout which is an int                        firstReadTimeout = standardTimeout - (int) queueTime;                    }                    socket.getSocket().setSoTimeout(firstReadTimeout);                    if (!inputBuffer.fill()) {                        throw new EOFException(sm.getString("iib.eof.error"));                    }                }                if (standardTimeout > 0) {                    socket.getSocket().setSoTimeout(standardTimeout);                }                /**前面一堆道具,这里正式开始 这是用来读取Http头的,只是用来读取头的哦,                  * 它负责将二进制流解析到对应的头部 后面我们会放出次方法的实现                  **/                inputBuffer.parseRequestLine(false);                if (endpoint.isPaused()) {                    // 503 - Service unavailable                    response.setStatus(503);                    adapter.log(request, response, 0);                    error = true;                } else {                    request.setStartTime(System.currentTimeMillis());                    keptAlive = true;                    if (disableUploadTimeout) {                        socket.getSocket().setSoTimeout(soTimeout);                    } else {                        socket.getSocket().setSoTimeout(connectionUploadTimeout);                    }                   /**走到这真坎坷,赶紧把到手的狗处理了吧--先处理头、脚                    * 先把头部转化了                    **/                  ?inputBuffer.parseHeaders();                }            } catch (IOException e) {                error = true;                break;            } catch (Throwable t) {                ExceptionUtils.handleThrowable(t);                if (log.isDebugEnabled()) {                    log.debug(sm.getString("http11processor.header.parse"), t);                }                // 400 - Bad Request                response.setStatus(400);                adapter.log(request, response, 0);                error = true;            }            if (!error) {                // Setting up filters, and parse some request headers                //第二阶段               ?rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);                try {                    /**拔毛----预处理请求,解析一些协议上的东西,稍后放出方法**/                   ?prepareRequest();                } catch (Throwable t) {                    ExceptionUtils.handleThrowable(t);                    if (log.isDebugEnabled()) {                        log.debug(sm.getString("http11processor.request.prepare"), t);                    }                    // 400 - Internal Server Error                    response.setStatus(400);                    adapter.log(request, response, 0);                    error = true;                }            }            if (socketWrapper.getKeepAliveLeft() == 0) {                keepAlive = false;            }            // Process the request in the adapter            if (!error) {                try {                     //第三阶段                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);                    //核心处理:去除内脏,烹饪……  处理请求体 这里又涉及到很多知识了,在下节详细介绍这个过程                   ?adapter.service(request, response);                    // Handle when the response was committed before a serious                    // error occurred.  Throwing a ServletException should both                    // set the status to 500 and set the errorException.                    // If we fail here, then the response is likely already                    // committed, so we can't try and set headers.                    if(keepAlive && !error) { // Avoid checking twice.                        error = response.getErrorException() != null ||                                (!isAsync() &&                                statusDropsConnection(response.getStatus()));                    }                } catch (InterruptedIOException e) {                    error = true;                } catch (Throwable t) {                    ExceptionUtils.handleThrowable(t);                    log.error(sm.getString("http11processor.request.process"), t);                    // 500 - Internal Server Error                    response.setStatus(500);                    adapter.log(request, response, 0);                    error = true;                }            }            // Finish the handling of the request            try {                   //第四阶段                  ?rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);                // If we know we are closing the connection, don't drain input.                // This way uploading a 100GB file doesn't tie up the thread                 // if the servlet has rejected it.                                if(error && !isAsync())                    inputBuffer.setSwallowInput(false);                if (!isAsync())                    //结束烹饪,结束请求                   ?endRequest();            } catch (Throwable t) {                ExceptionUtils.handleThrowable(t);                log.error(sm.getString("http11processor.request.finish"), t);                // 500 - Internal Server Error                response.setStatus(500);                adapter.log(request, response, 0);                error = true;            }            try {                 //第五阶段                ?rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);            } catch (Throwable t) {                ExceptionUtils.handleThrowable(t);                log.error(sm.getString("http11processor.response.finish"), t);                error = true;            }            // If there was an error, make sure the request is counted as            // and error, and update the statistics counter            if (error) {                response.setStatus(500);            }            //收尾阶段:将狗肉盛盘  更新统计数据           ?request.updateCounters();            //第六阶段   收尾阶段:收拾后厨  清楚数据,等待下一个请求,计数器            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);            // Don't reset the param - we'll see it as ended. Next request            // will reset it            // thrA.setParam(null);            // Next request            if (!isAsync() || error) {                inputBuffer.nextRequest();                outputBuffer.nextRequest();            }            // If we don't have a pipe-lined request allow this thread to be            // used by another connection            if (isAsync() || error || inputBuffer.lastValid == 0) {                break;            }                        if (maxKeepAliveRequests > 0) {                socketWrapper.decrementKeepAlive();            }        }        //第七阶段:收工  上狗肉          rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);        if (error || endpoint.isPaused()) {            return SocketState.CLOSED;        } else if (isAsync()) {            return SocketState.LONG;        } else {            if (!keepAlive) {                return SocketState.CLOSED;            } else {                return SocketState.OPEN;            }        }     }

这个过程还是挺复杂的(你以为吃顿狗肉这么容易?),分七个阶段:

第一阶段:处理头部,可能抛503和400异常;

第二阶段:预处理请求体,主要解析请求的部分header和解析url,可能抛400异常;

第三阶段:处理请求(最核心的阶段),可能抛500异常;

第四阶段:结束请求,可能抛500异常;

第五阶段:更新统计计数,可能抛500异常;

第六阶段:整理线程,准备接受下一个请求;

第七阶段:结束

走完这七步,香喷喷的狗肉就上桌了!!tomcat源码分析系列之请求处理-关门打狗,不过先别动筷子,我们还要补上前面提到的几个方法:

处理头部:

/**     * Read the request line. This function is meant to be used during the      * HTTP request header parsing. Do NOT attempt to read the request body      * using it.     *     * @throws IOException If an exception occurs during the underlying socket     * read operations, or if the given buffer is not big enough to accommodate     * the whole line.     */    @Override    public boolean parseRequestLine(boolean useAvailableDataOnly)            throws IOException {        int start = 0;        //        // Skipping blank lines        //        byte chr = 0;        do {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            chr = buf[pos++];        } while ((chr == Constants.CR) || (chr == Constants.LF));        pos--;        // Mark the current buffer position        start = pos;        //        // Reading the method name        // Method name is always US-ASCII        //        boolean space = false;        while (!space) {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            // Spec says no CR or LF in method name            if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {                throw new IllegalArgumentException(                        sm.getString("iib.invalidmethod"));            }            // Spec says single SP but it also says be tolerant of HT            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {                space = true;                request.method().setBytes(buf, start, pos - start);            }            pos++;        }                // Spec says single SP but also says be tolerant of multiple and/or HT        while (space) {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {                pos++;            } else {                space = false;            }        }        // Mark the current buffer position        start = pos;        int end = 0;        int questionPos = -1;        //        // Reading the URI        //        boolean eol = false;        while (!space) {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            // Spec says single SP but it also says be tolerant of HT            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {                space = true;                end = pos;            } else if ((buf[pos] == Constants.CR)                        || (buf[pos] == Constants.LF)) {                // HTTP/0.9 style request                eol = true;                space = true;                end = pos;            } else if ((buf[pos] == Constants.QUESTION)                        && (questionPos == -1)) {                questionPos = pos;            }            pos++;        }        request.unparsedURI().setBytes(buf, start, end - start);        if (questionPos >= 0) {            request.queryString().setBytes(buf, questionPos + 1,                                            end - questionPos - 1);            request.requestURI().setBytes(buf, start, questionPos - start);        } else {            request.requestURI().setBytes(buf, start, end - start);        }        // Spec says single SP but also says be tolerant of multiple and/or HT        while (space) {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {                pos++;            } else {                space = false;            }        }        // Mark the current buffer position        start = pos;        end = 0;        //        // Reading the protocol        // Protocol is always US-ASCII        //        while (!eol) {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            if (buf[pos] == Constants.CR) {                end = pos;            } else if (buf[pos] == Constants.LF) {                if (end == 0)                    end = pos;                eol = true;            }            pos++;        }        if ((end - start) > 0) {            request.protocol().setBytes(buf, start, end - start);        } else {            request.protocol().setString("");        }                return true;    }

?这个方法没什么好说,接下来看

  /**     * Parse an HTTP header.     *      * @return false after reading a blank line (which indicates that the     * HTTP header parsing is done     */    @SuppressWarnings("null") // headerValue cannot be null    public boolean parseHeader()        throws IOException {        //        // Check for blank line        //        byte chr = 0;        while (true) {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            chr = buf[pos];            if ((chr == Constants.CR) || (chr == Constants.LF)) {                if (chr == Constants.LF) {                    pos++;                    return false;                }            } else {                break;            }            pos++;        }        // Mark the current buffer position        int start = pos;        //        // Reading the header name        // Header name is always US-ASCII        //        boolean colon = false;        MessageBytes headerValue = null;        while (!colon) {            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            if (buf[pos] == Constants.COLON) {                colon = true;                headerValue = headers.addValue(buf, start, pos - start);            } else if (!HTTP_TOKEN_CHAR[buf[pos]]) {                // If a non-token header is detected, skip the line and                // ignore the header                skipLine(start);                return true;            }            chr = buf[pos];            if ((chr >= Constants.A) && (chr <= Constants.Z)) {                buf[pos] = (byte) (chr - Constants.LC_OFFSET);            }            pos++;        }        // Mark the current buffer position        start = pos;        int realPos = pos;        //        // Reading the header value (which can be spanned over multiple lines)        //        boolean eol = false;        boolean validLine = true;        while (validLine) {            boolean space = true;            // Skipping spaces            while (space) {                // Read new bytes if needed                if (pos >= lastValid) {                    if (!fill())                        throw new EOFException(sm.getString("iib.eof.error"));                }                if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {                    pos++;                } else {                    space = false;                }            }            int lastSignificantChar = realPos;            // Reading bytes until the end of the line            while (!eol) {                // Read new bytes if needed                if (pos >= lastValid) {                    if (!fill())                        throw new EOFException(sm.getString("iib.eof.error"));                }                if (buf[pos] == Constants.CR) {                    // Skip                } else if (buf[pos] == Constants.LF) {                    eol = true;                } else if (buf[pos] == Constants.SP) {                    buf[realPos] = buf[pos];                    realPos++;                } else {                    buf[realPos] = buf[pos];                    realPos++;                    lastSignificantChar = realPos;                }                pos++;            }            realPos = lastSignificantChar;            // Checking the first character of the new line. If the character            // is a LWS, then it's a multiline header            // Read new bytes if needed            if (pos >= lastValid) {                if (!fill())                    throw new EOFException(sm.getString("iib.eof.error"));            }            chr = buf[pos];            if ((chr != Constants.SP) && (chr != Constants.HT)) {                validLine = false;            } else {                eol = false;                // Copying one extra space in the buffer (since there must                // be at least one space inserted between the lines)                buf[realPos] = chr;                realPos++;            }        }        // Set the header value        headerValue.setBytes(buf, start, realPos - start);        return true;    }
?
/**     * After reading the request headers, we have to setup the request filters.     */    protected void prepareRequest() {        http11 = true;        http09 = false;        contentDelimitation = false;        expectation = false;                prepareRequestInternal();        if (endpoint.isSSLEnabled()) {            request.scheme().setString("https");        }        MessageBytes protocolMB = request.protocol();        if (protocolMB.equals(Constants.HTTP_11)) {            http11 = true;            protocolMB.setString(Constants.HTTP_11);        } else if (protocolMB.equals(Constants.HTTP_10)) {            http11 = false;            keepAlive = false;            protocolMB.setString(Constants.HTTP_10);        } else if (protocolMB.equals("")) {            // HTTP/0.9            http09 = true;            http11 = false;            keepAlive = false;        } else {            // Unsupported protocol            http11 = false;            error = true;            // Send 505; Unsupported HTTP version            if (getLog().isDebugEnabled()) {                getLog().debug(sm.getString("http11processor.request.prepare")+                          " Unsupported HTTP version \""+protocolMB+"\"");            }            response.setStatus(505);            adapter.log(request, response, 0);        }        MessageBytes methodMB = request.method();        if (methodMB.equals(Constants.GET)) {            methodMB.setString(Constants.GET);        } else if (methodMB.equals(Constants.POST)) {            methodMB.setString(Constants.POST);        }        MimeHeaders headers = request.getMimeHeaders();        // Check connection header        MessageBytes connectionValueMB = headers.getValue("connection");        if (connectionValueMB != null) {            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {                keepAlive = false;            } else if (findBytes(connectionValueBC,                                 Constants.KEEPALIVE_BYTES) != -1) {                keepAlive = true;            }        }        MessageBytes expectMB = null;        if (http11)            expectMB = headers.getValue("expect");        if ((expectMB != null)            && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {            getInputBuffer().setSwallowInput(false);            expectation = true;        }        // Check user-agent header        if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {            MessageBytes userAgentValueMB = headers.getValue("user-agent");            // Check in the restricted list, and adjust the http11            // and keepAlive flags accordingly            if(userAgentValueMB != null) {                String userAgentValue = userAgentValueMB.toString();                if (restrictedUserAgents != null &&                        restrictedUserAgents.matcher(userAgentValue).matches()) {                    http11 = false;                    keepAlive = false;                }            }        }        // Check for a full URI (including protocol://host:port/)        ByteChunk uriBC = request.requestURI().getByteChunk();        if (uriBC.startsWithIgnoreCase("http", 0)) {            int pos = uriBC.indexOf("://", 0, 3, 4);            int uriBCStart = uriBC.getStart();            int slashPos = -1;            if (pos != -1) {                byte[] uriB = uriBC.getBytes();                slashPos = uriBC.indexOf('/', pos + 3);                if (slashPos == -1) {                    slashPos = uriBC.getLength();                    // Set URI as "/"                    request.requestURI().setBytes                        (uriB, uriBCStart + pos + 1, 1);                } else {                    request.requestURI().setBytes                        (uriB, uriBCStart + slashPos,                         uriBC.getLength() - slashPos);                }                MessageBytes hostMB = headers.setValue("host");                hostMB.setBytes(uriB, uriBCStart + pos + 3,                                slashPos - pos - 3);            }        }        // Input filter setup        InputFilter[] inputFilters = getInputBuffer().getFilters();        // Parse transfer-encoding header        MessageBytes transferEncodingValueMB = null;        if (http11)            transferEncodingValueMB = headers.getValue("transfer-encoding");        if (transferEncodingValueMB != null) {            String transferEncodingValue = transferEncodingValueMB.toString();            // Parse the comma separated list. "identity" codings are ignored            int startPos = 0;            int commaPos = transferEncodingValue.indexOf(',');            String encodingName = null;            while (commaPos != -1) {                encodingName = transferEncodingValue.substring                    (startPos, commaPos).toLowerCase(Locale.ENGLISH).trim();                if (!addInputFilter(inputFilters, encodingName)) {                    // Unsupported transfer encoding                    error = true;                    // 501 - Unimplemented                    response.setStatus(501);                    adapter.log(request, response, 0);                }                startPos = commaPos + 1;                commaPos = transferEncodingValue.indexOf(',', startPos);            }            encodingName = transferEncodingValue.substring(startPos)                .toLowerCase(Locale.ENGLISH).trim();            if (!addInputFilter(inputFilters, encodingName)) {                // Unsupported transfer encoding                error = true;                // 501 - Unimplemented                if (getLog().isDebugEnabled()) {                    getLog().debug(sm.getString("http11processor.request.prepare")+                              " Unsupported transfer encoding \""+encodingName+"\"");                }                response.setStatus(501);                adapter.log(request, response, 0);            }        }        // Parse content-length header        long contentLength = request.getContentLengthLong();        if (contentLength >= 0 && !contentDelimitation) {            getInputBuffer().addActiveFilter                (inputFilters[Constants.IDENTITY_FILTER]);            contentDelimitation = true;        }        MessageBytes valueMB = headers.getValue("host");        // Check host header        if (http11 && (valueMB == null)) {            error = true;            // 400 - Bad request            if (getLog().isDebugEnabled()) {                getLog().debug(sm.getString("http11processor.request.prepare")+                          " host header missing");            }            response.setStatus(400);            adapter.log(request, response, 0);        }        parseHost(valueMB);        if (!contentDelimitation) {            // If there's no content length             // (broken HTTP/1.0 or HTTP/1.1), assume            // the client is not broken and didn't send a body            getInputBuffer().addActiveFilter                    (inputFilters[Constants.VOID_FILTER]);            contentDelimitation = true;        }        // Advertise sendfile support through a request attribute        if (endpoint.getUseSendfile()) {            request.setAttribute("org.apache.tomcat.sendfile.support",                    Boolean.TRUE);        }                // Advertise comet support through a request attribute        if (endpoint.getUseComet()) {            request.setAttribute("org.apache.tomcat.comet.support",                    Boolean.TRUE);        }        // Advertise comet timeout support        if (endpoint.getUseCometTimeout()) {            request.setAttribute("org.apache.tomcat.comet.timeout.support",                    Boolean.TRUE);        }    }
?

这几个方法没什么好说,感兴趣就看一下就行了。

读书人网 >开源软件

热点推荐