一个Tomcat连接器必须符合以下条件:
1. 必须实现接口org.apache.catalina.Connector。
2. 必须创建请求对象,该请求对象的类必须实现接口org.apache.catalina.Request。
3. 必须创建响应对象,该响应对象的类必须实现接口org.apache.catalina.Response。
Tomcat4的默认连接器类似于第3章的简单连接器。它等待前来的HTTP请求,创建request和 response 对象,然后把 request 和 response 对象传递给容器。连接器是通过调用接口
org.apache.catalina.Container 的 invoke 方法来传递 request 和 response 对象的。invoke的方法签名如下所示:
public void invoke(
org.apache.catalina.Request request,
org.apache.catalina.Response response);
在 invoke 方法里边,容器加载 servlet,调用它的 service 方法,管理会话,记录出错日
志等等。
HTTP1.1三个新特性:
持久连接:
在HTTP1.1之前,无论什么时候浏览器连接到一个web服务器,当请求的资源被发送之后,连接就被服务器关闭了。加入页面和它所引用到的全部资源使用不同连接来下载的话,进程将会非常慢。使用持久连接的时候,当页面下载的时候,服务器并不直接关闭连接。相反,它等待web客户端请求页面所引用的全部资源。这种情况下,页面和所引用的资源使用同一个连接来下载。
块编码:
由于大部分服务器或客户端不需要知道发送多少数据。在HTTP1.0中,服务器会忽略content-length头部,并保持写入连接,写完之后,它会简单的关闭连接。客户端会保持读取状态,直到获取到-1,表示已经到达尾部。
HTTP1.1使用一个特别的头部,transfer-ecording来表示有多少以块形式的数据流将会被发送。对于每块,在数据之前,长度+\r\n将被发送,0\r\n表示这个事务的结束。
例:
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标识这个事务的结束。
状态100(持续状态)的使用:
当客户端需要发送一份很长的请求内容而未确保服务器是否接受它的时候。HTTP1.1客户端可以发送Except:100 continue头部到服务器。当服务器接收到Except时,如果可以请求,则服务器响应100 continue头部,后面跟着两对CRL(\r\n)
接着,服务器应该会继续读取输入流!
Connector接口:
org.apache.catalina.Connector接口的4个重要方法:setContainer,getContainer,createRequest,createReponse.
setContainer是用来关联连接器和容器用的。getContainer返回关联的容器。createRequest为前来的HTTP请求构造一个请求对象,而createResponse创建一个响应对象。
类org.apache.catalina.connector.http.HttpConnector是 Connector 接口的一个实现。
HttpConnector类:
本章主要研究:如何创建一个服务器套接字,它如何维护一个HttpProcessor对象池,还有它如何处理HTTP请求。
创建一个服务器套接字:
通过HttpConnector的initialize方法的私有方法open,返回一个java.net.ServerSocket实例,并把它赋给serversocket。
维护HttpProcessor实例:
在第三章中,由于HttpConnector实例仅仅拥有一个HttpProcessor实例,所以每次只能处理一个请求。在默认连接器中,HttpConnector拥有一个HttpProcessor对象池,每个HttpProcessor实例拥有它的独立线程,因此HttpConnector可以同时处理多个请求。
由于HttpConnector维护一个HttpProcessor对象池,所以不用每次都创建一个HttpProcessor实例。这些HttpProcessor实例放在一个叫processors的java.io.Stack中:
private Stack processors = new Stack();
HttpProcessor实例数量有两个变量决定:minProcessors和maxProcessors.默认情况下,minProcessors为5,maxProcessors为20,可以通过setMinProcessors和setMaxProcessors来改变他们的值。
若不修改,则当请求达到MAX值后,之后的请求会被忽略掉。
为HTTP请求服务:
对每个前来的 HTTP 请求,会通过调用私有方法 createProcessor 获得一个 HttpProcessor实例。
HttpProcessor processor = createProcessor();
然而,大部分时候createProcessor方法并不创建一个新的HttpProcessor对象。相反,它从池子中获取一个。如果在栈中已经存在一 个 HttpProcessor 实例,createProcessor 将弹出一个。如果栈是空的并且没有超过 HttpProcessor 实例的最大数 量,createProcessor 将会创建一个。然而,如果已经达到最大数量的话,createProcessor将会返回null。出现这样的情况的话,套接字将会简单关闭并且前来的HTTP请求不会被处理。
HttpProcessor类:
HttpProcessor可以让assign方法异步化,这样就可以同时处理多个Http请求。
HttpProcessor类实现了java.lang.Runnable 并且每个HttpProcessor实例运行在称作处理器线程(processor thread)的自身线程上。
HttpProcessor的run方法中,运行了一个await方法,await方法持有处理线程的控制流,直到HttpConnector调用HttpProcessor实例的assign方法。(assign方法通过一个布尔变量,并且使用wait和notifyAll方法来通知await方法)最后返回一个需要进行处理的socket。(其中await和assign是运行在不同的线程上)
请求对象:
响应对象:
处理请求:
HttpProcessor的process方法:解析连接parseConnection,解析请求parseRequest,解析头部parseHeaders。
try {
if (ok) {
parseConnection(socket);
parseRequest(input, output);
if (!request.getRequest().getProtocol().startsWith("HTTP/0"))
parseHeaders(input);
parseConnection方法获得协议的值,像HTTP0.9, HTTP1.0或HTTP1.1。如果协议是HTTP1.0,keepAlive(表示连接是否持久) 设置为 false,因为 HTTP1.0 不支持持久连接。如果在 HTTP 请求里边找到 Expect: 100-continue的头部信息,则parseHeaders方法将把sendAck设置为true。
如果协议是 HTTP1.1,并且 web 客户端发送头部 Expect: 100-continue 的话,通过调用ackRequest方法它将响应这个头部。它将会测试组块是否是允许的。
if (http11) {
// Sending a request acknowledge back to the client if requested.
ackRequest(output);
// If the protocol is HTTP/1.1, chunking is allowed.
if (connector.isChunkingAllowed())
response.setAllowChunking(true);
}
ackRequest方法测试sendAck的值,并在sendAck为true的时候发送下面的字符串: HTTP/1.1 100 Continue\r\n\r\n
解析请求:参照第三章。
解析头部:
使用org.apache.catalina.connector.http里面的HttpHeader和DefaultHeaders类。
DefaultHeaders类是一个final类,在字符数组里包含了标准的HTTP请求头部:
standard HTTP request headers in character arrays:
static final char[] AUTHORIZATION_NAME = "authorization".toCharArray();
static final char[] ACCEPT_LANGUAGE_NAME = "accept-language".toCharArray();
static final char[] COOKIE_NAME = "cookie".toCharArray();
...
HttpHeader使用字符数组来存储readHeader读取到的头部,parseHeader将会把头部名称和DefaultHeader里面的名称进行对比,如果相同,则解析头部信息。
1. 必须实现接口org.apache.catalina.Connector。
2. 必须创建请求对象,该请求对象的类必须实现接口org.apache.catalina.Request。
3. 必须创建响应对象,该响应对象的类必须实现接口org.apache.catalina.Response。
Tomcat4的默认连接器类似于第3章的简单连接器。它等待前来的HTTP请求,创建request和 response 对象,然后把 request 和 response 对象传递给容器。连接器是通过调用接口
org.apache.catalina.Container 的 invoke 方法来传递 request 和 response 对象的。invoke的方法签名如下所示:
public void invoke(
org.apache.catalina.Request request,
org.apache.catalina.Response response);
在 invoke 方法里边,容器加载 servlet,调用它的 service 方法,管理会话,记录出错日
志等等。
HTTP1.1三个新特性:
持久连接:
在HTTP1.1之前,无论什么时候浏览器连接到一个web服务器,当请求的资源被发送之后,连接就被服务器关闭了。加入页面和它所引用到的全部资源使用不同连接来下载的话,进程将会非常慢。使用持久连接的时候,当页面下载的时候,服务器并不直接关闭连接。相反,它等待web客户端请求页面所引用的全部资源。这种情况下,页面和所引用的资源使用同一个连接来下载。
块编码:
由于大部分服务器或客户端不需要知道发送多少数据。在HTTP1.0中,服务器会忽略content-length头部,并保持写入连接,写完之后,它会简单的关闭连接。客户端会保持读取状态,直到获取到-1,表示已经到达尾部。
HTTP1.1使用一个特别的头部,transfer-ecording来表示有多少以块形式的数据流将会被发送。对于每块,在数据之前,长度+\r\n将被发送,0\r\n表示这个事务的结束。
例:
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标识这个事务的结束。
状态100(持续状态)的使用:
当客户端需要发送一份很长的请求内容而未确保服务器是否接受它的时候。HTTP1.1客户端可以发送Except:100 continue头部到服务器。当服务器接收到Except时,如果可以请求,则服务器响应100 continue头部,后面跟着两对CRL(\r\n)
接着,服务器应该会继续读取输入流!
Connector接口:
org.apache.catalina.Connector接口的4个重要方法:setContainer,getContainer,createRequest,createReponse.
setContainer是用来关联连接器和容器用的。getContainer返回关联的容器。createRequest为前来的HTTP请求构造一个请求对象,而createResponse创建一个响应对象。
类org.apache.catalina.connector.http.HttpConnector是 Connector 接口的一个实现。
HttpConnector类:
本章主要研究:如何创建一个服务器套接字,它如何维护一个HttpProcessor对象池,还有它如何处理HTTP请求。
创建一个服务器套接字:
通过HttpConnector的initialize方法的私有方法open,返回一个java.net.ServerSocket实例,并把它赋给serversocket。
维护HttpProcessor实例:
在第三章中,由于HttpConnector实例仅仅拥有一个HttpProcessor实例,所以每次只能处理一个请求。在默认连接器中,HttpConnector拥有一个HttpProcessor对象池,每个HttpProcessor实例拥有它的独立线程,因此HttpConnector可以同时处理多个请求。
由于HttpConnector维护一个HttpProcessor对象池,所以不用每次都创建一个HttpProcessor实例。这些HttpProcessor实例放在一个叫processors的java.io.Stack中:
private Stack processors = new Stack();
HttpProcessor实例数量有两个变量决定:minProcessors和maxProcessors.默认情况下,minProcessors为5,maxProcessors为20,可以通过setMinProcessors和setMaxProcessors来改变他们的值。
若不修改,则当请求达到MAX值后,之后的请求会被忽略掉。
为HTTP请求服务:
对每个前来的 HTTP 请求,会通过调用私有方法 createProcessor 获得一个 HttpProcessor实例。
HttpProcessor processor = createProcessor();
然而,大部分时候createProcessor方法并不创建一个新的HttpProcessor对象。相反,它从池子中获取一个。如果在栈中已经存在一 个 HttpProcessor 实例,createProcessor 将弹出一个。如果栈是空的并且没有超过 HttpProcessor 实例的最大数 量,createProcessor 将会创建一个。然而,如果已经达到最大数量的话,createProcessor将会返回null。出现这样的情况的话,套接字将会简单关闭并且前来的HTTP请求不会被处理。
HttpProcessor类:
HttpProcessor可以让assign方法异步化,这样就可以同时处理多个Http请求。
HttpProcessor类实现了java.lang.Runnable 并且每个HttpProcessor实例运行在称作处理器线程(processor thread)的自身线程上。
HttpProcessor的run方法中,运行了一个await方法,await方法持有处理线程的控制流,直到HttpConnector调用HttpProcessor实例的assign方法。(assign方法通过一个布尔变量,并且使用wait和notifyAll方法来通知await方法)最后返回一个需要进行处理的socket。(其中await和assign是运行在不同的线程上)
请求对象:
响应对象:
处理请求:
HttpProcessor的process方法:解析连接parseConnection,解析请求parseRequest,解析头部parseHeaders。
try {
if (ok) {
parseConnection(socket);
parseRequest(input, output);
if (!request.getRequest().getProtocol().startsWith("HTTP/0"))
parseHeaders(input);
parseConnection方法获得协议的值,像HTTP0.9, HTTP1.0或HTTP1.1。如果协议是HTTP1.0,keepAlive(表示连接是否持久) 设置为 false,因为 HTTP1.0 不支持持久连接。如果在 HTTP 请求里边找到 Expect: 100-continue的头部信息,则parseHeaders方法将把sendAck设置为true。
如果协议是 HTTP1.1,并且 web 客户端发送头部 Expect: 100-continue 的话,通过调用ackRequest方法它将响应这个头部。它将会测试组块是否是允许的。
if (http11) {
// Sending a request acknowledge back to the client if requested.
ackRequest(output);
// If the protocol is HTTP/1.1, chunking is allowed.
if (connector.isChunkingAllowed())
response.setAllowChunking(true);
}
ackRequest方法测试sendAck的值,并在sendAck为true的时候发送下面的字符串: HTTP/1.1 100 Continue\r\n\r\n
解析请求:参照第三章。
解析头部:
使用org.apache.catalina.connector.http里面的HttpHeader和DefaultHeaders类。
DefaultHeaders类是一个final类,在字符数组里包含了标准的HTTP请求头部:
standard HTTP request headers in character arrays:
static final char[] AUTHORIZATION_NAME = "authorization".toCharArray();
static final char[] ACCEPT_LANGUAGE_NAME = "accept-language".toCharArray();
static final char[] COOKIE_NAME = "cookie".toCharArray();
...
HttpHeader使用字符数组来存储readHeader读取到的头部,parseHeader将会把头部名称和DefaultHeader里面的名称进行对比,如果相同,则解析头部信息。