tomcat 将一个包中所有类使用的错误信息存储在 properties 文件中,每个包有一个 properties 文件。每个 properties 文件都是用 org.apache.catalina.util.StringManager 类的一个实例来处理的,通一个包下的许多类使用同一个 StringManager 实例(单例模式)
例如,要想从 ex.03.pyrmont.connector.http 包下的类中使用 StringManager ,需要将包名字符串作为参数传到 StringManager 类的 getManager() 方法中:
StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");
在 ex.03.pyrmont.connector.http 包下,可以找到三个 properties 文件,LocalString.properties, LocalString_es.properties 和 LocalString_ja.properties 。StringManager 实例会根据运行该程序的服务器的语言环境来选择使用哪个文件。若打开 LocalString.properties 文件,第一个非注释行的内容如下:
httpConnector.alreadyInitialized = HTTP connector has already been initialized
调用 StringManager 类的 getString() 方法,该方法传入错误码 “httpConnector.alreadyInitialized”,得到错误信息“HTTP connector has already been initialized”。
等待 HTTP 请求的工作由 HttpConnector 实例完成。
创建 Request 和 Response 对象的工作由 HttpProcessor 实例完成。
javax.servlet.http.HttpServletRequest
public interface HttpServletRequest extends ServletRequest
上图中的 HttpRequest 类实现 javax.servlet.http.HttpServletRequest 接口, HttpRequest 对象会被转型为 HttpServletRequest 对象,然后传递给被调用的 servlet 实例的 service() 方法。因此,必须正确地设置每个 HttpRequest 实例地成员变量供 servlet 实例使用。需要设置地值包括:URI、查询字符串、参数、Cookie 和其它一些请求头信息。若是来凝结其仅仅解析会被 servlet 实例用到的值会节省很多 CPU 周期。在这些参数被 servlet 实例真正调用前,tomcat 默认连接器包括本章中的连接器是不会解析他们的。
SocketInputStream 实例时 java.io.InputStream 实例的包装类,提供了两个重要方法:
-
- readRequestLine():返回一个 HTTP 请求第一行的内容,包含了 请求方法、URI、HTTP 版本信息。返回值是一个 HttpRequestLine 实例。
- readHeader():每次调用都会返回一个 名/值 对,所以应该重复调用此方法,知道读取了所有请求头信息。返回值是一个 HttpHeader 对象。
套接字的输入流中处理字节流是指读取从第一个字节到最后一个字节的内容,因此 readRequestLine() 方法必须在 readHeader() 之前调用。
HttpConnector 类实现了 java.lang.Runable, 当启动应用时,创建 HttpConnector 的一个实例并执行它的 run() 方法
run() 方法包含一个 while 循环,在循环中执行如下三个操作:
-
- 等待 HTTP 请求
- 为每个请求创建一个 HttpProcessor 实例
- 调用 HttpProcessor 对象的 process(Socket socket)方法
HttpProcessor 类的 process() 方法接收来自传入的 HTTP 请求的套接字。对于每个传入的 HTTP 请求,他要完成 4 个操作:
-
- 创建一个 HttpRequest 对象
- 创建一个 HttpResponse 对象
- 解析 HTTP 请求的第 1 行内容和请求头信息,填充 HttpRequest 对象
- 将 HttpRequest 对象和 HttpResponse 对象传递给 servletProcessor 或 StaticResourceProcessor 的 process 方法。servletProcessor 类会调用请求的 servlet 实例的 service 方法,StaticResourceProcessor 类会将请求的静态资源发送给客户端。
public void process (Socket socket) { SocketInputStream input = null; OutputStream output = null; try { input = new SocketInputStream(socket.getInputStream, 2048); output = socket.getOutputStream(); // create HttpRequest object and parse request = new HttpRequest(input); //create HttpResponse object response = new HttpResponse(output); response.setRequst(request); response.setHeader("Server", "Pyrmont Servlet Container"); parseRequest(input, output); parseHeader(input); //check if this is a request for a servlet or a static resource if (request.getRequestURI().startsWith("/servlet/")) { ServletProcessor processor = new ServletProcessor(); processor.process(request, response); } else { StaticResourceProcessor processor = new StaticResoureProcessor(); processor.process(request, response); } //Close the socket socket.close(); //no shutdown for this application } catch (Exception e) { e.printStackTracec(); } }
解析 HTTP 请求
1、读取套接字的输入流
之所以使用 SocketInputStream 类就是为了调用其 readRequestLine() 方法和 readHeader() 方法。
2、解析请求行
GET /myApp/chat/session/state?custId=1032785&pwd=1234 HTTP/1.1
URI 是 /myApp/chat/session/state,问号后面的部分就是查询字符串,查询字符串可以包含 0 个或多个请求参数。先调用 SocketInputStream 类的 readRequestLine() 方法。
3、解析请求头
while 循环不断调用 SocketInputStream 类的 readHeader() 方法。
4、解析 Cookie
Cookie: userName=budi;password=pwd;
Cookie 是由浏览器作为 HTTP 请求头的一部分发送的。这样请求头的名称是 "cookie"。上面这个 Cookie 请求头的例子中包含两个 Cookie : userName 和 password。解析后返回 javax.servlet.http.Cookie 类型的一个数组,数组中的元素个数与 Cookie 请求头中 名/值 对的数目相同。
5、获取参数
参数可以出现在查询字符串或请求体中。若 GET 请求 servlet,全在查询字符串中。若 POST ,则请求体中也可能会有参数。放在特殊的 HashMap 类:org.apache.catalina.util.ParameterMap 。其中有一个名为 locked 的布尔变量。只有当 locked 值为 false 时,才可以对 ParameterMap 对象中的 名/值对 进行添加、更新或删除操作。否则会抛出 IllegalStateException。但是可以在任何时候读取该值。用 org.apache.catalina.util.RequestUtil 的 parseParameters 方法完成解析。最后返回 ParameterMap 时,要将结果的 locked 设为 true 锁住,使其内容保持一致,再赋值。
在 servlet 中,可以使用 java.io.PrintWriter 对象向输出流中写 字符。可以使用任意编码格式。当在向浏览器中发送字符时,实际上都是字节流