深入Jetty源码之HttpParser

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:

概述

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;
}

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;
}

EventHandler的实现类

在HttpConnection的内部类RequestHandler类实现了HttpParser.EventHandler类,以作为HttpParser使用时的回调。
startRequest:重置当前HttpConnection状态,HttpRequest的时间戳,设置新解析出来的RequestMethod、URI、version信息。
parsedHeader: 将每个HTTP头(name, value)对添加到_requestFields字段中,并检查某些头的存在性以及其值的合法性。
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;
}

相关文章
|
缓存 分布式计算 API
Spark Netty与Jetty (源码阅读十一)
  spark呢,对Netty API又做了一层封装,那么Netty是什么呢~是个鬼。它基于NIO的服务端客户端框架,具体不再说了,下面开始。   创建了一个线程工厂,生成的线程都给定一个前缀名。      像一般的netty框架一样,创建Netty的EventLoopGroup:      在常用...
1096 0