基于抽象和聚合原則,Jetty中需要一個單獨的類來專門處理HTTP響應消息和請求消息的生成和發送,Jetty的作者將該抽象(接口)命名為Generator,它有兩個實現類:HttpGenerator和NestedGenerator。其類圖如下:
Generator接口
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中。
posted on 2014-04-20 15:58
DLevin 閱讀(1282)
評論(2) 編輯 收藏 所屬分類:
Jetty