接着我们就来看看HttpProcessor的process()方法了,这个方法是处理请求的重头级函数!
首先要处理请求,要么首先要有获取资源的来源,那么在HttpProcessor类中需要HttpRequestImpl和HttpResponseImpl对象,因为这两个类是获取信息的来源!
那么首先看看HttpProcessor初始化HttpRequestImpl和HttpResponseImpl对象的?
在HttpConnector类中初始化HttpProcessor实例时,就会调用HttpProcessor的构造方法,看看其构造方法做了什么事?
org.apache.catalina.connector.http.HttpProcessor
/**
* The HTTP request object we will pass to our associated container.
*/
private HttpRequestImpl request = null;
/**
* The HTTP response object we will pass to our associated container.
*/
private HttpResponseImpl response = null;
// ----------------------------------------------------------- Constructors
/**
* Construct a new HttpProcessor associated with the specified connector.
*
* @param connector HttpConnector that owns this processor
* @param id Identifier of this HttpProcessor (unique per connector)
*/
public HttpProcessor(HttpConnector connector, int id) {
super();
this.connector = connector;
this.debug = connector.getDebug();
this.id = id;
this.proxyName = connector.getProxyName();
this.proxyPort = connector.getProxyPort();
this.request = (HttpRequestImpl) connector.createRequest();
this.response = (HttpResponseImpl) connector.createResponse();
this.serverPort = connector.getPort();
this.threadName =
"HttpProcessor[" + connector.getPort() + "][" + id + "]";
}
这个构造函数就创建了HttpRequestImpl和HttpResponseImpl实例!那么接着就看处理处理这个实例!
先分析一下调用过程,当Socket对象被赋值给HttpProcessor类后,HttpProcessor类的run()方法会调用process()方法,process()方法会执行以下3个操作:解析连接,解析请求,解析请求头。先给出tomcat4.0给出的整体的实现,然后再分小段来看!
org.apache.catalina.connector.http.HttpProcessor
/**
* The HTTP request object we will pass to our associated container.
*/
private HttpRequestImpl request = null;
/**
* The HTTP response object we will pass to our associated container.
*/
private HttpResponseImpl response = null;
/**
* Process an incoming HTTP request on the Socket that has been assigned
* to this Processor.Any exceptions that occur during processing must be
* swallowed and dealt with.
*
* @param socket The socket on which we are connected to the client
*/
private void process(Socket socket) {
boolean ok = true;
//表示在处理过程中是否有错误发生
boolean finishResponse = true;
//表示是否应该调用Response接口的finishResponse()方法
SocketInputStream input = null;
OutputStream output = null;
//socketInputStream实例用于包装套借字的输入流
// Construct and initialize the objects we will need
try {
input = new SocketInputStream(socket.getInputStream(),
connector.getBufferSize());
} catch (Exception e) {
log("process.create", e);
ok = false;
}
keepAlive = true;
//表示该链接是否是持久连接,//stopped表明HttpProcessor实例是否被连接器终止,如果stopped为真,那么process()也应该被终止
//stopped表示HttpProcessor实例是否被连接器终止,keepAlive表明该连接是否持久连接
while (!stopped && ok && keepAlive) {
finishResponse = true;
try {
//做一些初始化的任务
request.setStream(input);
request.setResponse(response);
output = socket.getOutputStream();
response.setStream(output);
response.setRequest(request);
((HttpServletResponse) response.getResponse()).setHeader
("Server", SERVER_INFO);
} catch (Exception e) {
log("process.create", e);
ok = false;
}
// Parse the incoming request
try {
if (ok) {
parseConnection(socket);
//获取请求所使用的协议,若是Http1.0,则将keepAlive设置为false,因为Http1.0不支持持久连接
parseRequest(input, output);
//解析请求
if (!request.getRequest().getProtocol()
.startsWith("HTTP/0"))
parseHeaders(input);
//若是在http请求头中找到"Expect:100-continue",则parseHeaders()方法将设置sendACK为true
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);
}
}
} catch (EOFException e) {//如果已经读到了数据末尾,但是连接断开了,如果再继续读取数据则会抛出此异常
// It's very likely to be a socket disconnect on either the
// client or the server
ok = false;
finishResponse = false;
} catch (ServletException e) {
ok = false;
try {
((HttpServletResponse) response.getResponse())
.sendError(HttpServletResponse.SC_BAD_REQUEST);
} catch (Exception f) {
;
}
} catch (InterruptedIOException e) {
if (debug > 1) {
try {
log("process.parse", e);
((HttpServletResponse) response.getResponse())
.sendError(HttpServletResponse.SC_BAD_REQUEST);
} catch (Exception f) {
;
}
}
ok = false;
} catch (Exception e) {
try {
log("process.parse", e);
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST);
} catch (Exception f) {
;
}
ok = false;
}
// Ask our Container to process this request
try {
((HttpServletResponse) response).setHeader
("Date", FastHttpDateFormat.getCurrentDate());
if (ok) {
connector.getContainer().invoke(request, response);
//解析完成,process()方法将request和response对象作为参数传入servlet容器的invoke方法,这个暂时不是这里的重点
}
} catch (ServletException e) {
log("process.invoke", e);
try {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (Exception f) {
;
}
ok = false;
} catch (InterruptedIOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
try {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (Exception f) {
;
}
ok = false;
}
// Finish up the handling of the request
if (finishResponse) {
try {
response.finishResponse();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
request.finishRequest();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
if (output != null)
output.flush();
} catch (IOException e) {
ok = false;
}
}
// We have to check if the connection closure has been requested
// by the application or the response stream (in case of HTTP/1.0
// and keep-alive).
if ( "close".equals(response.getHeader("Connection")) ) {
keepAlive = false;
}
// End of request processing
status = Constants.PROCESSOR_IDLE;
// Recycling the request and the response objects
request.recycle();
response.recycle();
}
try {
shutdownInput(input);
//会检查是否有未读完的字节
socket.close();
} catch (IOException e) {
;
} catch (Throwable e) {
log("process.invoke", e);
}
socket = null;
}
1,先分析这个方法中一些变量的含义:
process()方法使用一个布尔变量ok来处理过程中是否有错误发生,以及一个布尔变量finishResponse来表示是否应该调用Response接口的finishResponse()方法:
boolean ok = true;
boolean finishResponse = true;
此外,process()方法还是用了其他一些布尔类型的成员变量,如keepAlive,stopped和http11。keepAlive表示该连接是否是持久连接,stopped表示HttpProcessor实例是否被连接器终止,这样的话process()方法也应该被终止。http11表明HTTP请求是否是从支持HTTP1.1客户端发送来的。
2,看看这个input = new SocketInputStream(socket.getInputStream(),
connector.getBufferSize());
SocketInputStream实例用于包装套接字的输入流。要注意的是,SocketInputStream类的构造函数也接受以缓冲区的大小为参数,该参数来自连接器,而不是HttpProcessor类中的一个局部变量。这是因为,对默认连接器的用户来说,HttpProcessor是不可见的。通过在Connector借口中设置缓冲区大小,任何人都可以使用连接器来设置缓冲区的大小;
org.apache.catalina.connector.http.HttpProcessor
SocketInputStream input = null;
OutputStream output = null;
//socketInputStream实例用于包装套借字的输入流
// Construct and initialize the objects we will need
try {
input = new SocketInputStream(socket.getInputStream(),
connector.getBufferSize());
} catch (Exception e) {
log("process.create", e);
ok = false;
}
-----------------------------------------------------------------------------------------------------------------
org.apache.catalina.connector.http.HttpConnector
/**
* The input buffer size we should create on input streams.
*/
private int bufferSize = 2048;
/**
* Return the input buffer size for this Connector.
*/
public int getBufferSize() {
return (this.bufferSize);
}
/**
* Set the input buffer size for this Connector.
*
* @param bufferSize The new input buffer size.
*/
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
3,然后是一个while循环,在循环体内,不断地读取输入流,直到HttpProcessor实例终止,抛出一个异常,或连接断开。
keepAlive = true;
while(!stopped && ok && keepAlive){
....
}
4,在while循环内,process()方法会先将变量finishResponse的值设置为true,然后获取输出流对象,执行request和reponse对象的一些初始化操作:
org.apache.catalina.connector.http.HttpProcessor
finishResponse = true;
try {
//做一些初始化的任务
request.setStream(input);
request.setResponse(response);
output = socket.getOutputStream();
response.setStream(output);
response.setRequest(request);
((HttpServletResponse) response.getResponse()).setHeader
("Server", SERVER_INFO);
} catch (Exception e) {
log("process.create", e);
ok = false;
}
5,接着process()方法会调用parseConnection(),parseRequest()和parseHeaders()方法开始解析引入的HTTP请求。这三个方法的具体实现会在后面来分析,现在来分析这三个方法做了一些什么事情。
org.apache.catalina.connector.http.HttpProcessor,
try {
if (ok) {
parseConnection(socket);
//获取请求所使用的协议,若是Http1.0,则将keepAlive设置为false,因为Http1.0不支持持久连接
parseRequest(input, output);
//解析请求.............................
if (!request.getRequest().getProtocol()
.startsWith("HTTP/0"))
parseHeaders(input);
//若是在http请求头中找到"Expect:100-continue",则parseHeaders()方法将设置sendACK为true
parseConnection()方法获取请求所使用的协议,其值可以是HTTP0.9,HTTP1.0或HTTP1.1。若值为HTTP1.0,则将keepAlive设置为false,因为HTTP1.0不支持持久连接。若是在http请求中找到发现"Expect: 100-continue",则parseHeaders()方法将设置sendAck为sendAck为true
若请求协议为HTTP1.1,而且客户端也发出了"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的值,若其值为true,则发送下面格式的字符串:HTTP/1.1 100 Continue\r\n\r\n
6,在解析HTTP请求过程中,有可能会抛出很多种异常,发生任何一个异常都会将变量ok或finishResponse设置为false。完成解析后,process()方法将request和reprose对象作为参数传入servlet容器的invoke()方法。
try {
((HttpServletResponse) response).setHeader
("Date", FastHttpDateFormat.getCurrentDate());
if (ok) {
connector.getContainer().invoke(request, response);
//解析完成,process()方法将request和response对象作为参数传入servlet容器的invoke方法,这个暂时不是这里的重点
}
7,然后,若变量finishResponse()的值为true,则调用response对象的finishResponse()方法和request对象的finishRequest()方法,再将结果发送至多个客户端:
// Finish up the handling of the request
if (finishResponse) {
try {
response.finishResponse();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
request.finishRequest();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
if (output != null)
output.flush();
} catch (IOException e) {
ok = false;
}
}
8,while()循环的最后一部分检查response的头信息"Connection"是否在servlet中被设置为"close",或者协议是否是HTTP1.0(不支持持久连接)。若这两种情况中任意一种为真,则将keepAlive设置为false,最后将request对象和response对象回收:
// We have to check if the connection closure has been requested
// by the application or the response stream (in case of HTTP/1.0
// and keep-alive).
if ( "close".equals(response.getHeader("Connection")) ) {
keepAlive = false;
}
// End of request processing
status = Constants.PROCESSOR_IDLE;
// Recycling the request and the response objects
request.recycle();
response.recycle();
9,在这种情况下,若keepAlive为true,或者前面的解析工作中没有错误,或HttpProcessor实例没有被终止,则while循环则继续运行。否则调用shutdownInput()方法,并关闭套接字:
try {
shutdownInput(input);
//会检查是否有未读完的字节,若有,则它跳过这些字节
socket.close();
} catch (IOException e) {
;
} catch (Throwable e) {
log("process.invoke", e);
}
socket = null;
}
好了Process方法的大概就讲到了这里,后面对Process()方法里面的细节再进一步详细的介绍。
推荐阅读
- jar|springboot项目打成jar包和war包,并部署(快速打包部署)
- tomcat|tomcat配置多个项目的server.xml的配置
- intellij-idea|JavaWeb: IntelliJ IDEA集成开发环境下开发第一个servlet程序(JDBC)Tomcat
- Tomcat配置图片保存路径,图片不保存在项目路径下
- Git|Tomcat 自定义错误页面(例如404页面等等)
- eclipse 指定文件上传到tomcat的路径
- Tomcat|Tomat之——多项目jar包共享配置
- 技术学习|Tomcat 内存泄露问题
- Tomcat|解决Tomcat项目部署过程中的 内存溢出