198211401182 发表于 2015-8-9 12:54:24

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

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


[*]Http11Protocol,实现了ProtocolHandler接口
[*]Http11Processor,实现了ActionHook接口
[*]InternalInputBuffer,实现了InputBuffer接口
[*]InternalOutputBuffer,实现了OutputBuffer接口
[*]InputFilter和OutputFilter接口,具体的实现类在 org.apache.coyote.http11.filters 中。
  架构图如下所示(转自王程斯的博客):

  处理流程:


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

2. 代码研究


[*]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基础上进行操作的接口和实现,方便进行扩展。它主要有以下几个类:


[*]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的数据流中。
  
  参考材料:
  http://blog.iyunv.com/wangchengsi/article/details/2995536
页: [1]
查看完整版本: [Tomcat] 基于Coyote的HTTP 1.1协议连接器