天天看點

深入Jetty源碼之HttpGenerator

基于抽象和聚合原則,jetty中需要一個單獨的類來專門處理http響應消息和請求消息的生成和發送,jetty的作者将該抽象(接口)命名為generator,它有兩個實作類:httpgenerator和nestedgenerator。其類圖如下:

深入Jetty源碼之HttpGenerator

http請求消息分為請求行、消息報頭、請求正文三部分,http響應消息分為狀态行、消息報頭、消息正文。在http請求消息和響應消息格式的唯一差別是請求行和狀态行,因而隻需要将這一行的内容區分開來,其他的可以共享邏輯。

請求行和響應行設定

請求行包括請求方法、uri、http協定版本,響應行包括http協定版本、狀态碼、狀态短語,因而在generator接口中定義了各自的設定方法:

    // 設定_method、_uri字段,如果目前_version是http/0.9,則_nocontent置為true

    void setrequest(string method, string uri);

    // 設定_status、_reason字段,對_reason字段,将所有'\r', '\n'替換成空格(' ')。清除_method字段。如果generator已經開始生成效應消息,則不可再調用該方法。

    void setresponse(int status, string reason);

    // 設定http協定版本,這裡的值9表示http/0.9, 10表示http/1.0,11表示http/1.1。對http/0.9的請求消息,不能包含請求内容。如果generator已經開始生成效應消息,則不可再調用該方法。

    void setversion(int version);

http消息報頭設定

在generator中,通過completeheader中的httpfields參數傳入http消息報頭,而其中的allcontentadded參數用于檢查并設定_last字段狀态。

//通過傳入的httpfields參數設定http消息報頭,allcontentadded參數用于檢查并設定_last字段狀态。

public void completeheader(httpfields fields, boolean allcontentadded) throws ioexception

void setdate(buffer timestampbuffer);

void setsendserverversion(boolean sendserverversion);

void setcontentlength(long length);

void setpersistent(boolean persistent);

在該方法的實作中:

1. 向_header緩存中寫入請求行或響應行(對http/0.9的特殊處理不詳述)。對http響應消息,如果狀态碼是[100-200)、204、304,這些響應消息不能有響應消息體。對狀态碼是100的響應消息還不能有其他消息頭。

2. 如果設定了_date值(狀态碼在200及以上,這裡貌似木有考慮請求消息),在消息頭中包含"date"消息頭。

3. 對fields參數中的所有消息頭,依次寫入到_header緩存中(消息頭名移除'\r', '\n', ':'字元,消息頭值移除'\r', '\n'字元)。對"content-length"頭,記錄_contentlength字段;對"content-type"為"multipart/byteranges"頭,設定_contentlength為slef_defining_content;對"transfer-encoding"頭,并且_contentlength的值為chunked_content,則添加"transfer-encoding: chunked\r\n"頭(或使用者自定義的以"chunked"開頭的值);對"server"頭,且設定了"sendserverversion"字段,則添加"server"頭;對"connection"頭,在請求消息中直接設定該頭,同時更新"keep_alive"的值("close"->keep_alive=false, "keep-alive"->keep_alive=true),在響應消息中,更新"keep_alive"和"_persistent"的值,對"upgrade"值,直接添加,而對其他值,根據"keep_alive"和"_persistent"的值以及http版本号添加"close"或"keep-alive"的值,以及使用者自定義的值;根據目前_contentlength值設定"content-length"頭;最後添加"\r\n"到_header緩存表示消息頭結束。

4. 将_state狀态從state_header更新到state_content。

http消息體設定

generator中有兩個方法用于向其添加http消息體:

void addcontent(buffer content, boolean last) throws ioexception;

boolean addcontent(byte b) throws ioexception;

這兩個方法的實作:

1. 如果目前已存在未重新整理的_content内容或者_contentlength為chunked_content,則先重新整理緩存。

2. 更新_content字段和_contentwritten字段。

3. 将_content的值寫入_buffer字段中。

在重新整理緩存時:

1. 準備buffer:将_content值寫入_buffer中,并清除_content引用;如果_contentlength為chunked_content,設定_bufferchunk為true,并且對chunked内容,先寫入16進制的size,緊跟"\r\n",然後是正真的内容;對最後一個chunk,添加"\r\n\r\n"。

2. 然後根據_header, _buffer, _content狀态,将它們中的内容寫入到endpoint中。

3. 如果目前狀态是state_flushing,則将_state狀态置為state_end。

http消息完成生成

generator調用complete方法表示生成已經完成:

public void complete() throws ioexception

在該方法中,它将_state狀态設定為state_flushing,并重新整理緩存。

generator中的其他方法

在generator/httpgenerator中還有一些發送響應消息的方法:

void senderror(int code, string reason, string content, boolean close) throws ioexception;

public void send1xx(int code) throws ioexception

public void sendresponse(buffer response) throws ioexception

其中senderror是generator接口中的方法,它是一個工具方法用于一次将一個http響應消息寫入到endpoint中:

    public void senderror(int code, string reason, string content, boolean close) throws ioexception {

        if (close)

            _persistent=false;

        if (!iscommitted()) {

            setresponse(code, reason);

            if (content != null)  {

                completeheader(null, false);

                addcontent(new view(new bytearraybuffer(content)), generator.last);

            } else {

                completeheader(null, true);

            }

            complete();

        }

    }

sendresponse方法是httpgenerator中的方法,它将response參數直接作為響應消息體,并設定_state為state_flushing,是一個工具方法。

send1xx方法是httpgenerator中的方法,它将1xx的響應消息直接寫入到endpoint中。