深入Jetty源码之HttpParser
概述
Jetty作为HTTP服务器,服务器和客户端以HTTP协议格式通信,Jetty使用Parser(HttpParser)来抽象HTTP请求消息和响应消息的解析类引擎。在HttpParser实现中,它采用有限状态机算法:定义了21中状态,每解析一个字符,就根据当前的状态做相应的处理,并决定是否要迁移到下一个状态,直到HTTP请求消息或响应消息解析完成。HttpParser采用事件驱动机制,它定义了EventHandler类,用户可以通过注册的EventHandler实例获取相应的消息:请求行解析完成(startRequest)、响应行解析完成(startResponse)、每个消息头解析完成(parsedHeader)、所有消息头解析完成(headerComplete)、消息内容解析完成(content)、整个消息(请求消息或响应消息)解析完成(messageComplete)。Parser接口定义
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;
}
// 重置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;
}
EventHandler定义
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)
throws IOException;
}
// 消息内容解析完成
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)
throws IOException;
}
EventHandler的实现类
在HttpConnection的内部类RequestHandler类实现了HttpParser.EventHandler类,以作为HttpParser使用时的回调。startRequest:重置当前HttpConnection状态,HttpRequest的时间戳,设置新解析出来的RequestMethod、URI、version信息。
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()方法)。
HttpPaser有限状态机实现
在HttpParser中定义了21中状态,其中STATE_END以前的状态用于解析HTTP头消息,而STATE_END以后的状态用于解析HTTP消息体。它们各自的状态迁移图如下。
HttpParser在解析HTTP消息头时的状态迁移图
HttpParser在解析HTTP消息体时的状态迁移图
HttpParser在HttpConnection类中的使用
HttpParser在HttpConnection中的handle方法被调用时用于解析客户端过来的HTTP请求消息。
if (!_parser.isComplete()) {
int parsed=_parser.parseAvailable();
if (parsed>0)
progress= true;
}
int parsed=_parser.parseAvailable();
if (parsed>0)
progress= true;
}