一、概述
第三章介绍的connector是一个很好的学习工具,但是我们还可以做的更多。这一章介绍的是Tomcat4默认的connector。
一个Tomcat的connector是一个独立的模块,能够被嵌入到一个servlet容器中。现在已经存在了很多个tomcat连接器,比如说Coyote,mod_jk,mod_jk2,mod_webapp。一个Tomcat的connector需要满足以下的条件:
- 实现org.apache.catalina.Connector接口
- 创建代表请求的对象并且实现org.apache.catalina.Request接口
- 创建代表响应的对象并且实现org.apache.catalina.Response接口
Tomcat4默认的connector和第三张模拟的connector是很相似的,它等待http请求,创建request对象和response对象,然后把request对象和response对象传递到container(容器)中,通过调用org.apache.catalina.Container的invoke方法,这个方法签名如下,
/** * Process the specified Request, and generate the corresponding Response, * according to the design of this particular Container. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred while * processing * @exception ServletException if a ServletException was thrown * while processing this request */ public void invoke(Request request, Response response) throws IOException, ServletException;
在invoke方法内部,容器会加载对象的servlet class文件,调用它的service方法,管理sessions,log出错信息等。
Tomcat4默认的connector也做了一些优化,第一点是使用各种各样的对象池来避免占用资源的对象创建,第二点是在很多地方它使用了字节数组而不是string。
这一章的程序是一个简单的container关联默认的connector,重点在于理解connector,在chapter 5会介绍各种各样的容器。在这一章中,只需要了解如何使用这个简单的container就可以了。
还有一点需要注意的是,默认的connector实现了HTTP 1.1的所有的新的特性,同时也能够处理HTTP 0.9和HTTP 1.0的请求。首先我们会介绍HTTP1.1的新特性,然后介绍org.apache.catalina.Connector接口以及如何创建request和reponse对象。在这一章中,我们能够发现很多在第三章中出现的类,比如HttpConnector,HttpProcessor等,当然它们的功能更加高级一些。
二、HTTP 1.1新特性
1、持久连接(Persistent Connection)
在HTTP1.1之前,不论什么时候一个浏览器连接到server,当server发送完成资源后会立即断开连接。但是,一个page可能包含其他的资源,比如说图片,applets等。所以,当请求一个页面时,浏览器会同时请求这个页面引用的资源。如果一个页面和它引用的资源是通过不同的connection下载到本地,这个处理会很慢。这就是为什么HTTP1.1引进持久化连接的原因。有了持久化连接,当传送完成一个页面时,server不会立即断开连接,它会等待web client来请求这个页面所关联的其他资源。这样,一个页面和它所关联的资源就是通过一个相同的connection进行下载。这样web server、client、网络都会节省很多处理和时间,因为考虑到建立和断开http 连接都是很费时的操作。
HTTP1.1默认建立持久连接。当然,浏览器可以发送如下的请求头来显示地指出使用持久连接:
connection: keep-alive
2、chunked编码
建立了持久连接的结果是,server可以发送多个资源的字节流,并且client可以发送多个请求,都使用的是同样的一个connection。所以,对于每个reqeust或者response,发送方必须发送content length请求头,只有这样,接收方才能知道如何解析收到的字节。但是,经常的一种情况是,发送发不知道要发送多少长度的字节流,举例来说,一个servlet容器能够在只有一部分字节准备到位时发送响应而不必等到所有的字节都到位。这就意味着,必须有一种方法在不能提前知道content-length请求头的请求下,告诉接收方如何解析收到的字节流。
实际上,如果不需要发送多个请求或者响应,server或者client不必知道它需要发送多个数据。在HTTP1.0时,server可以不用理会content-length响应头,一直往connection中写入数据。当它结束的时候,它会简单地关闭连接。在这种情况下,client就会一直从连接中读取数据,直到读取到-1,说明已经读取到文件的末尾了。
HTTP1.1引进了一个特殊的header称为transfer-encoding,来指示字节流是以块(chunks)的方式来发送的。对于每个块,在发送块的数据之前,会首先发送块的长度(以十六进制表示),长度和数据之间以CR/LF(回车换行)间隔。一个transaction(发送一个完整的数据或者文件)用一个0长度的chunk来标示。假设你想用2个chunk来发送以下的38个字节,第一个chunk的长度是29,第二个是9,如下所示。
数据:
I'm as helpless as a kitten up a tree.
发送的协议:
1D\r\n I'm as helpless as a kitten u 9\r\n p a tree. 0\r\n
其中,1D(十六进制)=29(十进制),说明第一个块包含了29个字节。0\r\n指示了发送已经完成。
3、100(Continue)状态的使用
支持HTTP1.1的客户端可能会发送一个这样的请求头(Expect: 100-continue),并且等待server的确认(acknowledgement)。这经常会在客户端准备发送一个很长的请求body但是不确定server是不是会接受的情况下发生。如果仅仅发送了一个很长的request body,只是为了确定server不会接受该客户端的请求,那么只会是一种浪费。
如果收到Expect: 100-continue请求头,并且server愿意或者可以处理该客户端的请求,服务器端就会响应以下的header,前面加上连个CRLF(回车换行)。如下。
CRLF
CRLF
HTTP/1.1 100 Continue
响应之后,服务器会接着读取输入流。
三、Connector接口
四、HttpConnector类
五、HttpProcessor类
六、Request对象
七、Response对象
八、解析请求
九、简单的Container
十、总结