tomcat学习(3) 连接器

连接器概述  

3.1 概述

    tomcat由两大模块组成:连接器和容器。一个支持servlet2.3和2.4规范的连接器必须要负责创建javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse实例,并将它们作为参数传递给要调用的某个的servlet的service方法。在第2章中的servlet容器仅仅能运行实现了javax.servlet.Servlet接口,并想service方法中传入了javax.servlet.ServletRequest和javax.servlet.ServletResponse实例的servlet。由于连接器并不知道servlet的具体类型(例如,该servlet是否javax.servlet.Servlet接口,还是继承自javax.servlet.GenericServlet类,或继承自javax.servlet.http.HttpServlet类),因此连接器总是传入HttpServletRequest和HttpServletResponse的实例对象。

    了解连接器之前,需要了解catalina下面的工具类StringManager类.

3.2 StringManager

    对于tomcat的大型应用程序必须小心的处理错误信息。对于servlet程序员来说,在抛出每一个javax.servlet,servletException异常中,tomcat都会发送一条特殊的错误信息。

         tomcat将错误信息写在一个properties文件中,这样便于读取和编辑。但若是将所有类的错误信息都写在一个properties文件,优惠导致文件太大,不便于读写。为避免这种情况,tomcat将properties文件按照不同的包进行划分,每个包下都有自己的properties文件。例如,org.apache.catalina.connector包下的properties文件包含了该包下所有的类中可能抛出的错误信息。每个properties文件都由一个org.apache.catalina.util.StringManager实例来处理。在tomcat运行时,会建立很多StringManager类的实例,每个实例对应一个properties文件。

StringManager被设计成单例对象

private static Hashtable managers = new Hashtable();   
public synchronized static StringManager   
    getManager(String packageName) {   
   StringManager mgr = (StringManager)managers.get(packageName);    if (mgr == null) {   
     mgr = new StringManager(packageName);   
     managers.put(packageName, mgr);   
   }   
   return mgr;   
}  

StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http"); 

3.2 Application

当前的应用程序可分为3个模块:connector、startup、core。

         startup模块仅包括一个StartUp类,负责启动应用程序。

         connector模块的类可分为以下5个部分:

         连接器及其支持类(HttpConnector和HttpProcessor);

         表示http请求的类(HttpRequest)及其支持类;

         表示http响应的类(HttpResponse)及其支持类;

         外观装饰类(HttpRequestFacade和HttpResponseFacade);

         常量类。

core模块包括ServletProcessor类和StaticResourceProcessor类。

程序的大体图如下



相比前面的学习,HttpServer被分成了HttpConnector和HttpProcessor两个类,Request和Response被HttpRequest和HttpResponse代替。

在上一章,HttpServer负责等待http请求,并创建Request和Response两个对象。在本程序中,等待请求的任务交给了HttpConnector完成,创建request和response交给HttpProcessor去完成。http请求用HttpRequest对象表示,该类实现了javax.servlet.http.HttpServletRequest接口。一个HttpRequest对象在传给servlet的service方法前,会被转型为HttpServletRequest对象。

tomcat的默认connector和本程序的connector通过SocketInputStream类来读取字节流,可通过socket的getInputStream方法来获取该对象。它有两个重要的方法readRequestLine和readHeader。readRequestLine方法返回一个http请求的第一行,包括uri,请求方法和http协议版本。从socket的inputStream中处理字节流意味着要从头读到尾(即不能返回来再读前面的内容),因此,readRequestLine方法一定要在readHeader方法前调用。readRequestLine方法返回的是HttpRequestLine对象,readHeader方法返回的是HttpHeader对象(key-value形式)。获取HttpHeader对象时,应重复调用readHeader方法,直到再也无法获取到。

HttpProcessor对象负责创建HttpRequest对象,并填充它的成员变量。在其parse方法中,将请求行(request line)和请求头(request header)信息填充到HttpRequest对象中,但并不会填充请求体(request body)和查询字符串(query string)。


3.3.1 启动

在Bootstrap类的main方法内实例化一个HttpConnector类的对象,并调用其start方法就可以启动应用程序。


3.3.2  connector
HttpConnector类实现了java.lang.Runnable接口,这样它可以专注于自己的线程。启动应用程序时,会创建一个HttpConnector对象,其run方法会被调用。其run方法中是一个循环体,执行以下三件事:


         等待http请求;

         为每个请求创建一个HttpPorcessor对象;
         调用HttpProcessor对象的process方法。
         HttpProcessor类的process方法从http请求中获取socket。对每个http请求,它要做一下三件事:
         创建一个HttpRequest对象和一个HttpResponse对象;
         处理请求行(request line)和请求头(request headers),填充HttpRequest对象;

         将HttpRequest对象和HttpResponse对象传给ServletProcessor或StaticResourceProcessor的process方法。

3.3.2 创建HttpRequest对象

HttpRequest类实现了javax.servlet.http.HttpServletRequest接口。其伴随的外观类是HttpRequestFacade。日uml图如下所示:


其中HttpRequest很多方法都是空方法,但已经可以从http请求中获取headers,cookies和参数等信息,三种信息的存储方式分别为HashMap、ArrayList和ParameterMap

3.3.3.1 SocketInputStream 类

    该类提供了请求行(request line) 和请求头(request header) 的方法

 请求行:

    HttpProcessor的process调用其私有方法parseRequest来解析请求行(requst line)。如下

    

  GET /myApp/ModernServlet?userName=tarzan&password=pwd HTTP/1.1

         注意:“GET”后面和“HTTP”前面各有一个空格。

         请求行的第2部分是uri加上查询字符串。在上面的例子中,uri是:

/myApp/ModernServlet

问号后面的都是查询字符串,这里是:

userName=tarzan&password=pwd

在servlet/jsp编程中,参数jsessionid通常是嵌入到cookie中的,也可以将其嵌入到查询字符串中。parseRequest方法的具体内容参见代码。


3.3.3.2  解析请求头

请求头(request header)由HttpHeader对象表示。可以通过HttpHeader无参构造建立对象,并将其作为参数传给SocketInputStream的readHeader方法,该方法会自动填充HttpHeader对象。parseHeader方法有一个循环体,不断的从SocketInputStream 中获取header信息,直到读取完。

String name = new String(header.name, 0, header.nameEnd);   
String value = new String(header.value, 0, header.valueEnd);  
获取到header的name和value后,要将其填充到HttpRequest的header属性(hashMap类型)中:  
request.addHeader(name, value);  

其中某些header要设置到request对象的属性中,如contentLength等。 


3.3.3.3 解析cookie

    cookie 是由浏览器作为请求头 一部分发送的,请求头是一个cookie,是一个键值对

             如下: Cookie:userName=userName;password=pwd

            通过一下代码解析

            public static Cookie[] parseCookieHeader(String header) {   
if ((header == null) || (header.length 0 < 1) )   
    return (new Cookie[0]);   
        ArrayList cookies = new ArrayList();   
while (header.length() > 0) {   
            int semicolon = header.indexOf(';');   
            if (semicolon < 0)   
    semicolon = header.length();   
    if (semicolon == 0)   
    break;   
    String token = header.substring(0, semicolon);   
    if (semicolon < header.length())   
       header = header.substring(semicolon + 1);   
    else   
                header = "";   
    try {   
       int equals = token.indexOf('=');   
               if (equals > 0) {   
       String name = token.substring(0, equals).trim();   
                   String value = token.substring(equals+1).trim();   
       cookies.add(new Cookie(name, value));   
                }   
} catch (Throwable e) { ; }   
}   
return ((Cookie[]) cookies.toArray (new Cookie [cookies.size ()]));   

}  


3.3.3.4    获取参数

  

在调用javax.servlet.http.HttpServletRequest的getParameter、getParameterMap、getParameterNames或getParameterValues方法之前,都不会涉及到对查询字符串或http请求体的解析。因此,这四个方法的实现都是先调用parseParameter方法。

         参数只会被解析一次,因为,HttpRequest类会设置一个标志位表明是否已经完成参数解析了。参数可以出现在查询字符串或请求体中。若用户使用的GET方法,则所有的参数都会在查询字符串中;若是使用的POST方法,则请求体中也可能会有参数。所有的key-value的参数对都会存储在HashMap中,其中的值是不可修改的。tomcat中使用的是一个特殊的hashmap类,org.apache.catalina.util.ParameterMap。

         ParameterMap类继承自java.util.HashMap,使用一个标志位来表示锁定。如果该标志位为false,则可以对其中的key-value进行添加、修改、删除操作,否则,执行这些操作时,会抛出IllegalStateException异常。代码如下:


3.3.3.5     创建HttpResponse

   



猜你喜欢

转载自blog.csdn.net/Dalton2017/article/details/80675851