默认连接器
上一节的连接器可以正常工作,经过修改还可以实现更多功能。这里介绍Tomcat的默认连接器(已被Coyote取代)。
连接器是一个独立的模块,可以被插入到servlet容器中。
Tomcat的连接器必须满足:1.实现org.apache.catalina.Connector接口 2.负责创建org.apache.catalina.Request接口的request对象 3.负责创建org.apache.catalina.Reponse接口的reponse对象
连接器,通过调用它的invoke方法,将request和response传给servlet,并载入servlet类,调用service方法。
与上节相比,多了很多优化的方法。比如:使用一个对象池来避免频繁创建对象带来的性能损耗。使用字符数组来代替字符串。
连接器实现了所有HTTP1.1的全部新特性。
1.1的新特性
持久连接:之前。无论何时连接服务器,将请求的资源返回后就断开连接。但网页上包含一些其他资源的,这一再次连接,开销很大。持久连接后,下载页面后,等待客户端请求的相关资源下载。使用同一个连接。
connection : keep-alive
块编码:持久连接后,服务器可以从多个资源发送流,客户端使用这一个连接发送多次请求。这样,发送方必须在每个请求或响应添加“content-length”头信息。接收方才知道如何解释字节信息。
1.0可以不写头信息,尽管在响应内容写,写完关闭。而客户端,接受,直到读方法返回-1。
1.1使用一个“transfer-encoding”特殊请求头。指明字节流会分块发送。每个块后面会跟一个回车/换行符。
例如: 1D\r\n +内容 1d是29,指明内容29个字节。 9\r\n + 内容
状态码100的使用:当客户端发送一个较长请求体,不确定服务器是否接受。会加上 Expect : 100-continue ,以检测是否服务器会接受,较长请求体。
服务接受到之后,如果可以接受,则返回 HTTP/1.1 100 Continue 响应头。
org.apache.catalina.Connector接口
关键的方法:set,getContainer()、createRequest()、createResponse()
setContainer()用于将连接器和某个servlet相关联(一对一的关系),get,返回关联的servlet。
org.apache.catalina.connector.http.HttpConnector实现了接口。(Lifecycle接口,维护每个实现了该接口的Catalinna组建的生命周期,创建后就应该调用initialize()与start())
1.创建服务器套接字
initialize()会调用一个私有方法open()(从ServerSocketFactory工厂取),返回一个ServerSocket实例。
2.维护HttpProcessor实例。
HttpConnector维护一个它的实例池,避免每次都创建。private Stack processors = new Stack();
创建的实例数量,由minProcessors和maxProcessors变量决定的。默认是5和20。如果超过最大限定数值。那么引入的HTTP请求被忽略。设置max为负数,则会不断的新建实例。
3.提供HTTP请求服务
该类,主要业务逻辑在run方法里。对于每个http请求,都会调私有方法createProcessor()获得一个实例。达到最大上限返回null,之后关闭套接字,不处理。
获得实例之后,将套接字传入,processor.assigin(socket),之后就开始解析。
HttpProcessor类
上一节已经有基本实现。他是一个线程类。在接收到socket就直接返回。不要等待解析完成。那么assigin需要异步处理。
run(){
while(!stop){
//接受套接字
Socket socket = await();//会阻塞知道获取新的socket(也就是说,直到HttpConnector调HttpProcessor的assigin方法之前)
if(socket==null)
continue;
//进行处理
process(socket);
//调用连接器的recycle(),将当前的HttpProcessor实例压回栈中
connector.recycle(this);
}
}
void recycle(HttpProcessor processor){
processors.push(processor);
}
await会阻塞,他和assigin()不是在同一个线程的。assign()是zai HttpConnector()的run方法中(连接线程),await是在HttpProcessor的run方法中(处理器现场)。
await(),我们通过available这个boolean变量和notifyAll()来实现,唤醒。
Synchronized void assign(Socket socket){
//如果那边在等待 available,值为false。等待这边变true
while(available){
wait();
}
this.socket = socket;
available= true;
notifyAll();
}
Synchronized Socket await(){
//如果为False,则需要等待,等待socket传过来,该变量则为true
while(!available){
wait();
}
Socket socket = this.socket; //这里使用局部变量,是因为,在当前socket处理完之前,可以继续接受对象。
available = false;
notifyAll();
return socket;
}
处理请求。
process(); 通过boolean ok = true;来表示处理过程中是否有错误。boolean finishResponse = true。表示是否应该调用Response接口的finishResponse()方法。 还有keepAlive是否持久连接。stopped表示HttpProcessor实例是否被连接器终止。
调用parseConnection()、parseRequest()和parseHeaders()解析引入的HTTP请求。
parseHeaders(),获取请求使用的协议,如果是HTTP1.0则将keepAlive设置为false。 如果发现“Expect: 100-continue”,则parseHeaders()中,将设置sendAck为true。
中间发生任何异常都会将ok变为false,之后完成解析,将request和response传给servlet,最后如果finishResponse 为true,调用request和response的finishResponse 方法,再将结果发送到客户端。
最后request.recycle()和response.recycle()将对象收回。
解析连接。
parseConnectionb() 会从套接字获取Internet地址,赋值给HttpRequestImpl对象。 还要检查是否使用代理。
private void parseConnection(Socket socket){
if(debug >= 2)
log();
//将地址赋值给request
((HttpRequestImpl)request).setInet(socket.getInetAddress());
//如果使用代理
if(proxyProt != 0)
request.setServerProt(proxyPort);
else
request.setServerProt(serverPort);
request.setSocket(socket);
}
解析请求头。
parseHeaders()方法使用,org.apache.catalina.conncetor.http包中的HttpHeader类(代表一个HTTP请求头),和DefaultHeader类。
HttpHeader类,使用字符数组,避免代价高昂的字符串操作。
DefaultHeader类,是一个final类,包含字符数组形式的标准HTTP请求。
static final char[] AUTHORIZATION_NAME ="authorization".toCharArray();