Tomcat基于Coyote的HTTP 1.1协议连接器

2017-02-15 17:01

1. 模块架构

org.apache.coyote.http11包支持http1.1协议,内部分为三类:ARP、NIO、普通http,这里只对最基本的普通http(使用java的IO流,而非NIO流)作简单研究。

这个包主要有以下几个类:

  1. Http11Protocol,实现了ProtocolHandler接口
  2. Http11Processor,实现了ActionHook接口
  3. InternalInputBuffer,实现了InputBuffer接口
  4. InternalOutputBuffer,实现了OutputBuffer接口
  5. InputFilter和OutputFilter接口,具体的实现类在 org.apache.coyote.http11.filters 中。

架构图如下所示(转自王程斯的博客):

HTTP 1.1 Connector Architecture 

       处理流程:

  1. JIOEndpoint起到一个连接池的作用,可以启动多个socket监听,一旦收到浏览器发来的请求后,把对应的socket对象通过process方法,传递给Http11ConnectionHandler,再交给Http11Processor
  2. Http11Processor内部有个InternalInputBuffer(图上未画出),InternalInputBuffer是真正对socket中包含的字节流进行处理的,它将字节转换为Request
  3. Request流经过滤器filters,最后到达实现了Adapter接口的容器,模块的工作就到此为止,回头继续处理下一个socket

2. 代码研究

  1. Http11Protocol

该类是http1.1协议的ProtocolHandler实现,主要包含了内部类Http11ConnectionHandler和Http11ConnectionHandler的实例对象,它的初始化代码和过程如下:

public Http11Protocol() {
        endpoint = new JIoEndpoint(); //新建endpoint,启动若干socket server,监听服务器端口
        cHandler = new Http11ConnectionHandler(this); //协议连接处理对象,用于对socket连接的处理
        ((JIoEndpoint) endpoint).setHandler(cHandler); //一旦endpoint接收到请求,会将对应socket实例传给cHandler.process方法进行处理
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

Http11ConnectionHandler内部类则继承了AbstractConnectionHandler,它主要实现了createProcessor方法,用于将Http11Processor应用于连接处理流程中。函数内新建了一个Http11Processor实例,并将该实例绑定到ConnectionHandler上。ConnectionHandler一旦接收到从endpoint发来的处理请求,使用对应的Processor对接收到的数据进行处理,形成Request对象。

  2. Http11Processor

这个类的作用就是生成Request(当然本质上还是InternalInputBuffer完成的),交给实现了Adapter接口的容器。它有adapter、request、response、inputbuffer、outputbuffer等几个关键字段,还有很多和http 1.1协议有关的字段和方法,用于协议的解析(这里就不对解析过程进行分析了)。

这个类对socket对象依次做如下的处理:

1)        把socket的inputstream和outputstream分别与inputbuffer和outputbuffer关联起来

2)        通过inputBuffer.parseRequestLine() 和 inputBuffer.parseHeaders() 方法,解析socket字节流中的头字段,写到request中

3)        通过prepareRequest方法组装filter,用于处理http消息体

4)        adapter.service(request, response) 把生成的request和response交给容器处理

5)        如果一切顺利,开始处理socket中的下一个请求(因为http1.1是支持持续连接的,所以一个socket中可能包含多个请求),循环回到第一步

6)        如果出错,则设置response的响应码,并终止循环

其中的prepareRequest方法,是用于准备inputbuffer的filter,主要包括一下几个过程:

1)        根据之前对http头字段的解析,分别检查protocol、method、expect、user-agent和MIMEheaders,此外还检查URI的格式

2)        准备加载filter

3)        如果有transfer-encoding这个头字段,则分别设置不同编码的filter

4)        校验content-length头字段

  3. InternalInputBuffer

这个类的主要功能是:从socket中获取字节流,将字节读入一个缓冲区buf,然后从缓冲区逐个解析http请求头以及内容。

主要的字段有:

  request:Request对象,从缓冲区中解析出的信息会写入request中

  buf:缓冲区,从socket的inputstream读取的字节放入此缓冲区中

  headers : MimeHeaders,保存以键值对出现的报头,也就是除去请求报文第一行之后的所有头部

主要的方法有:

  parseRequestLine()

  解析请求报头的第一行,形如:GET http://class/download.microtool.de:80/somedata.exe,包括请求方法(GET or POST)、协议(http)、URI。解析后,放入request中

  parseHeader()

  解析刚才parseRequestLine()之后的报头,由于RequestLine之后的报头都是以“:”分隔的键值对,因此每执行一次本方法,则在headers 中加入一个键值对,如果格式错误则返回false

  endRequest()

  结束一个request的处理,把多余的字节清空

  nextRequest()

  准备下一个request的处理,这个方法主要用来对所有的标记位和指针进行复位

  fill()

  从socket的inputstream中读出一定数量的字节,填充buf,在很多方法中都有用到。例如解析报头时,当发现buf已经读取完了,就调用fill重新填充buf,如果inputstream已经读完了,fill返回false。

  4. InternalOutputBuffer

  这个类是用来从response中读取信息,然后写入socket的outputstream中,返回给客户端的。里面的很多方法和InternalInputBuffer基本相似,有两个是专门用于处理Response的:nextRequest()和endRequest()。这两个函数的功能主要是在当前Request处理完并发出Response之后,跳转到下一个Request的处理或者结束处理。

3. org.apache.coyote.http11.upgrade

Apache在基于coyote的HTTP 1.1连接器实现模块中还实现了能够供基于HTTP 1.1的一些协议和在HTTP 1.1基础上进行操作的接口和实现,方便进行扩展。它主要有以下几个类:

  1. UpgradeInbound接口

接口定义了基于socket数据流处理需要实现的一些接口,主要功能是当连接器从socket中读取出数据后,会通知UpgradeInbound实现类进行数据解析,即onData函数。  

  2. UpgradeProcessor类

这个类是基于HTTP 1.1连接器的upgrade连接器Processor的模板类,定义了一个标准的UpgradeProcessor必须要重写的读写处理函数:

// Output methods
    public abstract void flush() throws IOException;
    public abstract void write(int b) throws IOException;
public abstract void write(byte[] b, int off, int len) throws IOException;
// Input methods
    public abstract int read() throws IOException; 
    public abstract int read(boolean block, byte[] bytes, int off, int len)
            throws IOException;

  3. UpgradeOutbound类

该类继承了OutputStream类,重写了write函数。这个类是一个功能封装类,在类初始化的时候传入用于解析数据的UpgradeProcessor,而在重写的write函数中则直接调用Processor的处理函数,使得数据能够被写到Upgraded的数据流中。

1. 模块架构

org.apache.coyote.http11包支持http1.1协议,内部分为三类:ARP、NIO、普通http,这里只对最基本的普通http(使用java的IO流,而非NIO流)作简单研究。

这个包主要有以下几个类:

  1. Http11Protocol,实现了ProtocolHandler接口
  2. Http11Processor,实现了ActionHook接口
  3. InternalInputBuffer,实现了InputBuffer接口
  4. InternalOutputBuffer,实现了OutputBuffer接口
  5. InputFilter和OutputFilter接口,具体的实现类在 org.apache.coyote.http11.filters 中。

架构图如下所示(转自王程斯的博客):

HTTP 1.1 Connector Architecture 

       处理流程:

  1. JIOEndpoint起到一个连接池的作用,可以启动多个socket监听,一旦收到浏览器发来的请求后,把对应的socket对象通过process方法,传递给Http11ConnectionHandler,再交给Http11Processor
  2. Http11Processor内部有个InternalInputBuffer(图上未画出),InternalInputBuffer是真正对socket中包含的字节流进行处理的,它将字节转换为Request
  3. Request流经过滤器filters,最后到达实现了Adapter接口的容器,模块的工作就到此为止,回头继续处理下一个socket

2. 代码研究

  1. Http11Protocol

该类是http1.1协议的ProtocolHandler实现,主要包含了内部类Http11ConnectionHandler和Http11ConnectionHandler的实例对象,它的初始化代码和过程如下:

public Http11Protocol() {
        endpoint = new JIoEndpoint(); //新建endpoint,启动若干socket server,监听服务器端口
        cHandler = new Http11ConnectionHandler(this); //协议连接处理对象,用于对socket连接的处理
        ((JIoEndpoint) endpoint).setHandler(cHandler); //一旦endpoint接收到请求,会将对应socket实例传给cHandler.process方法进行处理
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

Http11ConnectionHandler内部类则继承了AbstractConnectionHandler,它主要实现了createProcessor方法,用于将Http11Processor应用于连接处理流程中。函数内新建了一个Http11Processor实例,并将该实例绑定到ConnectionHandler上。ConnectionHandler一旦接收到从endpoint发来的处理请求,使用对应的Processor对接收到的数据进行处理,形成Request对象。

  2. Http11Processor

这个类的作用就是生成Request(当然本质上还是InternalInputBuffer完成的),交给实现了Adapter接口的容器。它有adapter、request、response、inputbuffer、outputbuffer等几个关键字段,还有很多和http 1.1协议有关的字段和方法,用于协议的解析(这里就不对解析过程进行分析了)。

这个类对socket对象依次做如下的处理:

1)        把socket的inputstream和outputstream分别与inputbuffer和outputbuffer关联起来

2)        通过inputBuffer.parseRequestLine() 和 inputBuffer.parseHeaders() 方法,解析socket字节流中的头字段,写到request中

3)        通过prepareRequest方法组装filter,用于处理http消息体

4)        adapter.service(request, response) 把生成的request和response交给容器处理

5)        如果一切顺利,开始处理socket中的下一个请求(因为http1.1是支持持续连接的,所以一个socket中可能包含多个请求),循环回到第一步

6)        如果出错,则设置response的响应码,并终止循环

其中的prepareRequest方法,是用于准备inputbuffer的filter,主要包括一下几个过程:

1)        根据之前对http头字段的解析,分别检查protocol、method、expect、user-agent和MIMEheaders,此外还检查URI的格式

2)        准备加载filter

3)        如果有transfer-encoding这个头字段,则分别设置不同编码的filter

4)        校验content-length头字段

  3. InternalInputBuffer

这个类的主要功能是:从socket中获取字节流,将字节读入一个缓冲区buf,然后从缓冲区逐个解析http请求头以及内容。

主要的字段有:

  request:Request对象,从缓冲区中解析出的信息会写入request中

  buf:缓冲区,从socket的inputstream读取的字节放入此缓冲区中

  headers : MimeHeaders,保存以键值对出现的报头,也就是除去请求报文第一行之后的所有头部

主要的方法有:

  parseRequestLine()

  解析请求报头的第一行,形如:GET http://class/download.microtool.de:80/somedata.exe,包括请求方法(GET or POST)、协议(http)、URI。解析后,放入request中

  parseHeader()

  解析刚才parseRequestLine()之后的报头,由于RequestLine之后的报头都是以“:”分隔的键值对,因此每执行一次本方法,则在headers 中加入一个键值对,如果格式错误则返回false

  endRequest()

  结束一个request的处理,把多余的字节清空

  nextRequest()

  准备下一个request的处理,这个方法主要用来对所有的标记位和指针进行复位

  fill()

  从socket的inputstream中读出一定数量的字节,填充buf,在很多方法中都有用到。例如解析报头时,当发现buf已经读取完了,就调用fill重新填充buf,如果inputstream已经读完了,fill返回false。

  4. InternalOutputBuffer

  这个类是用来从response中读取信息,然后写入socket的outputstream中,返回给客户端的。里面的很多方法和InternalInputBuffer基本相似,有两个是专门用于处理Response的:nextRequest()和endRequest()。这两个函数的功能主要是在当前Request处理完并发出Response之后,跳转到下一个Request的处理或者结束处理。

3. org.apache.coyote.http11.upgrade

Apache在基于coyote的HTTP 1.1连接器实现模块中还实现了能够供基于HTTP 1.1的一些协议和在HTTP 1.1基础上进行操作的接口和实现,方便进行扩展。它主要有以下几个类:

  1. UpgradeInbound接口

接口定义了基于socket数据流处理需要实现的一些接口,主要功能是当连接器从socket中读取出数据后,会通知UpgradeInbound实现类进行数据解析,即onData函数。  

  2. UpgradeProcessor类

这个类是基于HTTP 1.1连接器的upgrade连接器Processor的模板类,定义了一个标准的UpgradeProcessor必须要重写的读写处理函数:

// Output methods
    public abstract void flush() throws IOException;
    public abstract void write(int b) throws IOException;
public abstract void write(byte[] b, int off, int len) throws IOException;
// Input methods
    public abstract int read() throws IOException; 
    public abstract int read(boolean block, byte[] bytes, int off, int len)
            throws IOException;

  3. UpgradeOutbound类

该类继承了OutputStream类,重写了write函数。这个类是一个功能封装类,在类初始化的时候传入用于解析数据的UpgradeProcessor,而在重写的write函数中则直接调用Processor的处理函数,使得数据能够被写到Upgraded的数据流中。