天天看点

深入Jetty源码之HttpGenerator

基于抽象和聚合原则,jetty中需要一个单独的类来专门处理http响应消息和请求消息的生成和发送,jetty的作者将该抽象(接口)命名为generator,它有两个实现类:httpgenerator和nestedgenerator。其类图如下:

深入Jetty源码之HttpGenerator

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中。