创建Request对象
在tomcat默认连接器中request对象是用org.apache.catalina.Request接口来表示的。这个接口由RequestBase类实现,并且它同时也是HttpRequest.接口的父接口。最终的实现类是HttpRequestImpl,它实现了HttpRequest接口。这里还有两个门面类:RequestFacade和HttpRequestFacade。上图就是request接口以及其实现类的UML图。
类似,Response对象的UML图如下:
处理请求:
之前我们已经了解了如何创建Reuest和Response对象,接下来主要介绍HttpProcessor的process方法,这个方法在HttpProcessor被分配一个socket后由其run方法调用。
其处理过程大致如下:
- 解析connection
- 解析request
- 解析header
boolean ok = true;
boolean finishResponse = true;
process方法使用布尔值ok标注在方法执行过程中没有发生任何错误,布尔值finishResponse表示Response接口的finishResponse方法应该被调用。还有其它布尔值,如keepAlive,stopped,http11;keepAlive表示一个持久连接;stopped表示HttpProcessor实例已经被connector停止,process方法在这时也应该停止;http11表示这是一个来自HTTP1.1协议的请求。
SocketInputStream input = null;
OutputStream output = null;
// 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;
}
从前面的章节已经知道SocketInputStream类包装了一个socket的输入流,另外,SocketInputStream的构造方法中的buffer size是由connector传入的,并不是由HttpProcessor中的本地变量传入。这是因为对于默认连接器的使用都HttpProcessor是不可见的,把缓冲的大小放在connector中可以让任何人使用connector来设置其大小。
keepAlive = true;
while (!stopped && ok && keepAlive) {
...
}
接下来是一个while循环,这个循环会一直运行直到HttpProcessor停止、抛出异常或是connection被关闭。
在初始化request和response对象之后,process方法调用parseConnection、parseRequest和parseHeader来解析HTTP请求。
parseConnection方法:
private void parseConnection(Socket socket)
throws IOException, ServletException {
if (debug >= 2)
log(" parseConnection: address=" + socket.getInetAddress() +
", port=" + connector.getPort());
((HttpRequestImpl) request).setInet(socket.getInetAddress());
if (proxyPort != 0)
request.setServerPort(proxyPort);
else
request.setServerPort(serverPort);
request.setSocket(socket);
}
从socket中获取网络地址设置到HttpRequestImpl对象中,同时方法还检查是否使用了代理。
解析Request
同第三章
解析Header
在默认连接器中的parseHeader方法中,使用了HttpHeader和DefaultHeader类。HttpHeader类代表一个HTTP请求的header,和前面章节不同,HttpHeader中使用了char数组来减少使用String时高昂的代价。DefaultHeader是一个final类,它以char数组形式保存了HTTP请求header的信息。
简单的Container容器
public void invoke(Request request, Response response)
throws IOException, ServletException {
String servletName = ((HttpServletRequest) request).getRequestURI();
servletName = servletName.substring(servletName.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(WEB_ROOT);
String repository = (new URL("file", null,
classPath.getCanonicalPath() + File.separator)).toString();
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
} catch (IOException e) {
System.out.println(e.toString());
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
} catch (ClassNotFoundException e) {
System.out.println(e.toString());
}
Servlet servlet = null;
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((HttpServletRequest) request,
(HttpServletResponse) response);
} catch (Exception e) {
System.out.println(e.toString());
} catch (Throwable e) {
System.out.println(e.toString());
}
}
在ex04.pyrmont.startup.Bootstrap类启动程序时,首先会创建两个实例org.apache.catalina.connector.http.HttpConnector和SimpleContainer实例。调用connector的serContainer方法将SimpleContainer实例传递过去。由于connector继承了LifeCycle接口,所以接下来调用initialize和start方法来启动连接器