深入Jetty源码之HttpGenerator

简介:
基于抽象和聚合原则,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( nullfalse);
                addContent( new View( new ByteArrayBuffer(content)), Generator.LAST);
            }  else {
                completeHeader( nulltrue);
            }
            complete();
        }
    }
sendResponse方法是HttpGenerator中的方法,它将response参数直接作为响应消息体,并设置_state为STATE_FLUSHING,是一个工具方法。
send1xx方法是HttpGenerator中的方法,它将1xx的响应消息直接写入到Endpoint中。

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