天天看點

深入Jetty源碼之HttpParser

jetty作為http伺服器,伺服器和用戶端以http協定格式通信,jetty使用parser(httpparser)來抽象http請求消息和響應消息的解析類引擎。在httpparser實作中,它采用有限狀态機算法:定義了21中狀态,每解析一個字元,就根據目前的狀态做相應的處理,并決定是否要遷移到下一個狀态,直到http請求消息或響應消息解析完成。httpparser采用事件驅動機制,它定義了eventhandler類,使用者可以通過注冊的eventhandler執行個體擷取相應的消息:請求行解析完成(startrequest)、響應行解析完成(startresponse)、每個消息頭解析完成(parsedheader)、所有消息頭解析完成(headercomplete)、消息内容解析完成(content)、整個消息(請求消息或響應消息)解析完成(messagecomplete)。

public interface parser {

    // 重置parser的内部狀态,以重用parser執行個體,如果returnbuffers為true,則将内部buffer回收。

    void reset(boolean returnbuffers);

    // 目前parser是否已經解析完成。

    boolean iscomplete();

    // 目前parser是否處于idle狀态,它還處于初始狀态,解析還沒有開始。

    boolean isidle();

    // 内部buffer是否還有内容沒有解析。

    boolean ismoreinbuffer() throws ioexception;

    // 開始解析已接收到的消息,傳回-1表示解析到流的末位,0表示沒有該次調用沒有解析任何消息,>0表示這次調用總共解析過的位元組數。

    int parseavailable() throws ioexception;

}

public static abstract class eventhandler {

    // 消息内容解析完成

    public abstract void content(buffer ref) throws ioexception;

    // 所有消息頭解析完成

    public void headercomplete() throws ioexception {

    }

    // 整個消息(請求消息或響應消息)解析完成

    public void messagecomplete(long contentlength) throws ioexception {

    // 每個消息頭解析完成

    public void parsedheader(buffer name, buffer value) throws ioexception {

    // 請求行解析完成

    public abstract void startrequest(buffer method, buffer url, buffer version)

            throws ioexception;

    // 響應行解析完成

    public abstract void startresponse(buffer version, int status, buffer reason)

在httpconnection的内部類requesthandler類實作了httpparser.eventhandler類,以作為httpparser使用時的回調。

startrequest:重置目前httpconnection狀态,httprequest的時間戳,設定新解析出來的requestmethod、uri、version資訊。

parsedheader:将每個http頭(name, value)對添加到_requestfields字段中,并檢查某些頭的存在性以及其值的合法性。

1. 如果“host”頭存在,則設定_host為true。

2. 對“expect”頭,如果其值是“100-continue”,設定_expect100continue為true,若值是“102-processing”,設定_expect102processing值為true,當資訊不足時,設定_expect為true。

3. 對“accept-encoding”和“user-agent”頭,隻能是預定義的值。

4. 對“content-type”頭隻能是預定義的值,并且根據該值設定_charset字段。

5. 對“connectin”頭,如果是“close”值,則設定httpgenerator的persistent屬性為false,并且設定_responsefields的“connection”值為“close”,否則為“keep-alive”。

headercomplete:在http消息頭解析結束後,對asyncendpoint,調用其scheduleidle()方法,設定httpgenerator中的http version字段,以及目前請求是否為head請求,如果目前server配置了senddateheader,則設定httpgenerator的date字段為httprequest的時間戳(在startrequest方法調用是設定)。對http/1.1,如果沒有設定host頭,直接傳回400響應(調用_generator的completeheader和complete方法);如果expect為true,表示expect頭設定有問題,直接傳回417響應(調用_generator的completeheader和complete方法)。設定_charset字段,對chunk請求立即開始處理請求(handlerequest),否則延遲到消息讀取完成。

content:對asyncendpoint,調用其scheduleidle()方法,如果請求還未開始處理,則立即開始處理請求。

messagecomplete:如果請求還未開始處理,則立即開始處理請求。

注:這裡并沒有在content方法中儲存消息體的内容,在jetty中使用httpinput類從httpparser中直接讀取消息體的内容(通過httpinput的read方法調用httpparser.blockforcontent()方法)。

在httpparser中定義了21中狀态,其中state_end以前的狀态用于解析http頭消息,而state_end以後的狀态用于解析http消息體。它們各自的狀态遷移圖如下。

深入Jetty源碼之HttpParser

httpparser在解析http消息頭時的狀态遷移圖

深入Jetty源碼之HttpParser

httpparser在解析http消息體時的狀态遷移圖

httpparser在httpconnection中的handle方法被調用時用于解析用戶端過來的http請求消息。

if (!_parser.iscomplete()) {

     int parsed=_parser.parseavailable();

     if (parsed>0)

         progress=true;

繼續閱讀