InternalInputBuffer處理HTTP請求行-Tomcat源碼-我們到底能走多遠系列(11)


我們到底能走多遠系列(11)

扯淡:

  最近行情不好嗎?跳槽的比較少嘛,哈哈。有些人一直抗拒跳槽,覺得弊端很多:什么業務積累,職務,別人覺得你不可靠啊等等。我就想:這一輩子時間有限,何必為了一顆可以乘涼的樹,放棄穿過森林的機會呢?祝在跳槽路上的朋友 順利!(ps:個人喜歡面試 那種刺激感)

  最爽不顧躺着,最美不過夕陽。秋天的夕陽是一年中最華麗的,各位不要錯過哦。

主題:

  在tomcat中,一個http請求,會被送到Http11Processor類,執行這個類的process(Socket theSocket) 處理的傳入的Socket,Socket里面裝的就是http消息。

  tomcat是如何調用到Http11Processor的process方法的,可以參照:http://blog.csdn.net/woorh/article/details/8017323

  Http11Processor在org.apache.coyote.http11包下。

  Http11Processor的rocess方法中,用inputBuffer.parseRequestLine();調用了解析http消息的請求行。這里的inputBuffer是tomcat自定義的InternalInputBuffer。

  需要了解的是:

  1,org.apache.coyote.Request 是tomcat內部使用用於存放關於request消息的數據結構

  2,org.apache.tomcat.util.buf.MessageBytes 用於存放消息,在org.apache.coyote.Request中大量用於存放解析后的byte字符

  3,org.apache.tomcat.util.buf.ByteChunk 真正用於存放數據的數據結構,存放的是byte[],org.apache.tomcat.util.buf.MessageBytes使用它。

  大流程:

  http消息通過inputBuffer解析后放到Request中,Request把它放到相應的MessageBytes,最后MessageBytes把它存到ByteChunk里。

  以上都可以通過方法調用來完成流程。

  主要關注的是解析的源代碼,在查看源代碼前需要了解http請求行的結構:可以參照:http://www.cnblogs.com/killbug/archive/2012/10/10/2719142.html

  閱讀前准備

  1,方法中全部的異常時,會調用getString方法,其實就是StringManager的寫日志方法,這是tomcat中統一的管理日志的方法。詳細的解釋在前一篇中已經幾時過了:Tomcat StringManager閱讀學習

  2,

轉義字符 意義 ASCII碼值(十進制)
\a 響鈴(BEL) 007
\b 退格(BS) ,將當前位置移到前一列 008
\f 換頁(FF),將當前位置移到下頁開頭 012
\n 換行(LF) ,將當前位置移到下一行開頭 010
\r 回車(CR) ,將當前位置移到本行開頭 013
\t 水平制表(HT) (跳到下一個TAB位置) 009
\v 垂直制表(VT) 011
\\ 代表一個反斜線字符''\' 092

源碼:

  

public void parseRequestLine()
        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
        //
        // space類似於開關一樣,當為false時,查內容,為true時,去除空行時間
        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
            // 查出第一個空格,tab居然也是允許的
            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
                space = true;//跳出循環
                // 把下標記錄下來,這里的method()得到一個Requast的MessageBytes:methodMB
                request.method().setBytes(buf, start, pos - start);
            }

            pos++;

        }
        
        // Spec says single SP but also says be tolerant of multiple and/or HT
        // 忽略空格后面的空格或者tab,因為是忽略的內容所以不需要什么start
        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; // 出現start了,后面肯定是需要記錄下標
        int end = 0;
        int questionPos = -1;

        //
        // Reading the URI
        // 上面是源碼的注釋,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
            // 尋找第二個空格,第一個空格和第二個空格之間就是傳說中的URI
            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
                // 為了兼容HTTP/0.9格式
                eol = true;
                space = true;
                end = pos;
            } else if ((buf[pos] == Constants.QUESTION) // 遇到‘?’了
                       && (questionPos == -1)) {
                // 把問號的位置先記錄下來
                questionPos = pos;
            }

            pos++;

        }
        // 把可能包含問號的URI的起始位和結束位記錄下來
        request.unparsedURI().setBytes(buf, start, end - start);
        if (questionPos >= 0) {// 有問號的情況
            // 問號位置記錄
            request.queryString().setBytes(buf, questionPos + 1, 
                                           end - questionPos - 1);
            // 把URI記錄下來
            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
        // 這段算是重復代碼吧,就是忽略空格用和tab用的
        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
        //
        // eol標志位是為了標記是否是HTTP/0.9 style request 前面代碼已經提到
        // 最后一樣:protocol(HTTP/ 1.1或1.0)
        while (!eol) {
            // Read new bytes if needed
            if (pos >= lastValid) {
                if (!fill())
                    throw new EOFException(sm.getString("iib.eof.error"));
            }
            // 查出  /r/n(CRLF)
            if (buf[pos] == Constants.CR) {
                end = pos;
            } else if (buf[pos] == Constants.LF) {
                if (end == 0)
                    end = pos;
                eol = true;
            }

            pos++;

        }
        // 至此把head分成三部分,放到Request定義好的MessageBytes中去了
        if ((end - start) > 0) {
            request.protocol().setBytes(buf, start, end - start);
        } else {
            request.protocol().setString("");
        }

    }

總結習點:

  1,利用表示位來控制解析式每個while的功能,上面代碼用的是space

  2,不用截斷的方式存儲需要的內容,而是記錄開始和結束的下標。

 

 

讓我們繼續前行

 

----------------------------------------------------------------------

 

努力不一定成功,但不努力肯定不會成功。
共勉

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM