http报文结构
start-line: 起始行,描述请求或响应的基本信息
*( header-field CRLF ): 头
CRLF
[message-body]: 消息body,实际传输的数据
状态机
jetty通过状态机解析这些字符串。具体看org.eclipse.jetty.http.HttpParse类
public enum State
{
START,
METHOD,
SPACE1,
STATUS,
URI,
SPACE2,
REQUEST_VERSION,
REASON,
PROXY,
HEADER,
CONTENT,
EOF_CONTENT,
CHUNKED_CONTENT,
CHUNK_SIZE,
CHUNK_PARAMS,
CHUNK,
TRAILER,
END,
CLOSE,
CLOSED
}
总共分成了21种状态,然后进行状态间的流转。在parseNext方法中分别对起始行 -> header -> body content分别解析
public boolean parseNext(ByteBuffer buffer)
{
……
// Start a request/response
if (_state==State.START)
{
// 快速判断
if (quickStart(buffer))
return true;
}
// Request/response line 转换
if (_state.ordinal()>= State.START.ordinal() && _state.ordinal()<State.HEADER.ordinal())
{
if (parseLine(buffer))
return true;
}
// headers转换
if (_state== State.HEADER)
{
if (parseFields(buffer))
return true;
}
// content转换
if (_state.ordinal()>= State.CONTENT.ordinal() && _state.ordinal()<State.TRAILER.ordinal())
{
// Handle HEAD response
if (_responseStatus>0 && _headResponse)
{
setState(State.END);
return handleContentMessage();
}
else
{
if (parseContent(buffer))
return true;
}
}
return false;
}
整体流程
有三条路径
1、开始 -> start-line -> header -> 结束
2、开始 -> start-line -> header -> content -> 结束
3、开始 -> start-line -> header -> chunk-content -> 结束
起始行
start-line = request-line(请求起始行)/(响应起始行)status-line
1、请求报文解析状态迁移 请求行:START -> METHOD -> SPACE1 -> URI -> SPACE2 -> REQUEST_VERSION
2、响应报文解析状态迁移 响应行:START -> RESPONSE_VERSION -> SPACE1 -> STATUS -> SPACE2 -> REASON
header 头
HEADER 的状态只有一种了,在jetty的老版本中还区分了HEADER_IN_NAM, HEADER_VALUE, HEADER_IN_VALUE等,新版本中去除了。为了提高匹配效率,jetty使用了Trie树快速匹配header头。
static
{
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
// 以下省略了很多了通用header头
content
请求体:
1、CONTENT -> END,这种是普通的带Content-Length头的报文,HttpParser一直运行CONTENT状态,直到最后ContentLength达到了指定的数量,则进入END状态
2、chunked分块传输的数据 CHUNKED_CONTENT -> CHUNK_SIZE -> CHUNK -> CHUNK_END -> END