基于抽象和聚合原则,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中。