`
dicmo
  • 浏览: 68359 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Coder 爱翻译 How Tomcat Works 第三章 第二部分

    博客分类:
  • j2ee
阅读更多
The Connector(连接器)

HttpConnector类代表了一个负责创建一个等待HTTP请求的服务器socket连接器。
HttpConnector类实现了java.lang.Runnable接口,所以它可以当做自己的一个独立的线程。当你启动这个应用程序,一个HttpConnector的实例就被创建,然后它执行它的run方法。

run方法包含一个while循环来处理下面的事情:
 等待HTTP请求
 为每一个请求创建一个HttpProcessor实例
 调用这个HttpProcessor的process方法

你可以看到,这HttpConnector类和之前的HttpServer1类很相像。除了当从java.net.ServerSocket的accept方法获得了一个socket后,一个HttpProcessor实例被创建,和调用process方法,传递socket。

注意:HttpConnector类的另外一个方法getScheme是返回scheme(HTTP)。

HttpProcessor类的process方法接收一个从HTTP请求来的socket。对于每一个来访的HTTP请求,做了一下事情:
 创建一个HttpRequest对象。
 创建一个HttpResponse对象
 解析HTTP请求的第一行、头信息和填充HttpRequest对象。
 传递HttpRequest和HttpResponse对象给ServletProcessor或StaticResourceProcessor。ServletProcessor调用被请求的servlet的service方法。StaticResourceProcessor发送静态资源内容。
Listing 3.3: The HttpProcessor class's process method. 

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.setRequest(request);
response.setHeader("Server", "Pyrmont Servlet Container"); 

      parseRequest(input, output); 
      parseHeaders(input); 
      //check if this is a request for a servlet or a static resource 
      //a request for a servlet begins with "/servlet/" 
      if (request.getRequestURI().startsWith("/servlet/")) { 
          ServletProcessor processor = new ServletProcessor(); 
         processor.process(request, response); 
      } 
      else { 
          StaticResourceProcessor processor = new 
             StaticResourceProcessor(); 
         processor.process(request, response); 
      } 
      // Close the socket 
      socket.close(); 
      // no shutdown for this application 
   } 
   catch (Exception e) { 
      e.printStackTrace (); 
   } 
}

Process方法在获取了socket的输入流和输出流后开始执行。注意:在这个方法我们使用继承自java.io.InputStream的SocketInputStream类。
   SocketInputStream input = null; 
      OutputStream output = null; 
      try { 
          input = new SocketInputStream(socket.getInputStream(), 2048); 
          output = socket.getOutputStream();

然后,它创建一个HttpRequest实例和一个HttpResponse实例,并把HttpRequest指配给HttpResponse。
// create HttpRequest object and parse 
request = new HttpRequest(input);
// create HttpResponse object 
response = new HttpResponse(output); 
response.setRequest(request);

HttpResponse类在这章中的应用比前面第二章的Response类更加复杂。其一:你可以通过调用它的setHeader方法来发送头部信息到客户端。
response.setHeader("Server", "Pyrmont Servlet Container");

接下来,process方法调用HttpProcessor类的两个private方法来解析请求。
parseRequest(input, output); 
parseHeaders (input);

后来,把HttpRequest和HttpReponse对象交给ServletProcessor还是StaticResourceProcessor处理取决于请求的URI。
if (request.getRequestURI().startsWith("/servlet/")) { 
ServletProcessor processor = new ServletProcessor(); 
    processor.process(request, response); 
} else { 
StaticResourceProcessor processor = new StaticResourceProcessor(); 
    processor.process(request, response); 
}

最后,关闭socket。
socket.close();

注意HttpProcessor类使用org.apache.catalina.util.StringManager类来发送错误信息。
protected StringManager sm = 
   StringManager.getManager("ex03.pyrmont.connector.http");

HttpProcessor类中的private方法—parseRequest,parseHeader和normalize被调用来填充HttpRequest。这些方法将在下面部分讨论。

Creating an HttpRequest Object

HttpRequest类实现了javax.servlet.http.HttpServletRequest接口。
在HttpRequest类有许多方法是空的。但是servlet程序员可以获得头部信息,cookies和HTTP请求的参数。
protected HashMap headers = new HashMap(); 
protected ArrayList cookies = new ArrayList(); 
protected ParameterMap parameters = null;

ParameterMap类将在“Obtaining Parameters”中讲解。

此外,一个servlet程序员可从javax.servlet.http.HttpServletRequest:getCookies,getDateHeader,
getHeader, getHeaderNames, getHeaders, getParameter, getPrameterMap, getParameterNames, 和getParameterValues方法获得正确的返回值。一旦你得到headers, cookies, 和被正确值填充的parameter时,相关方法的实现就很简单了。你可以看HttpRequest类。

不用说,这里最大的问题是解析HTTP请求和填充HttpRequest对象。对于headers 和cookies,HttpRequest类提供了addHeader和addCookie方法,可以在HttpProcessor的parseHeaders方法调用。参数在它需要的时候才会被解析,使用HttpRequest类的parseParameters方法。所有方法将在下面部分讲解。

HTTP请求解析是一个相当复杂的工作。把它分成下面几个部分:

 读取socket的输入流  - Reading the socket's input stream
 解析请求行          - Parsing the request line
 解析头部信息        - Parsing headers
 解cookie            - Parsing cookies
 获取参数            - Obtaining parameters  

Reading the Socket's Input Stream

在第一章和第二章你做了请求解析。ex01.pyrmont.HttpRequest和ex02.pyrmont.HttpRequest 类。你获得请求行,获取方法(GET…),URI和HTTP版本。
byte[] buffer = new byte [2048]; 
try { 
    // input is the InputStream from the socket. 
    i = input.read(buffer); 
}

你没有试图解析更多请求内容在前两个应用中。在这章的应用的,你使用ex03.pyrmont.connector.http.SocketInputStream类,一个org.apache.catalina.connector.http.
SocketInputStream类的复制。这个类不仅提供了获取请求行的方法,还有请求头部信息的方法。

你通过传递一个InputStream和代表buffer大小的integer整型数来构建一个SocketInputStream实例。在这个应用中,你在ex03.pyrmont.connector.http.HttpProcessor的process方法中创建一个SocketInputStream对象。下面是代码片段:
  SocketInputStream input = null; 
     OutputStream output = null; 
     try { 
       input = new SocketInputStream(socket.getInputStream(), 2048); 
      ...

在前面提到的,拥有一个SocketInputStream的原因是2个很重要的方法:readRequestLine和readHeader。

Parsing the Request Line

HttpProcessor的process方法调用private的parseRequest方法来解析请求行。例如:HTTP请求的第一行:
GET /myApp/ModernServlet?userName=tarzan&password=pwd HTTP/1.1

请求行的第二部分是URI加上可选择的查询字符串(optional query string)。例如:
/myApp/ModernServlet

查询字符串例子:
userName=tarzan&password=pwd

查询字符串可以获取0个或者更多的参数。在上面的例子中,有两个name/value的成对参数。userName/ tarzan和password/ pwd。在servlet/jsp编程中,参数名jsessionid被用来存储一个session的标识符。Session的标识符通常被嵌入到cookies,但是程序员可以选择把session标识符嵌入到查询字符串中。例如如果浏览器关闭了对cookies的支持。

当parseParameter方法在HttpProcessor类的process方法里调用时,请求变量指向一个HttpRequest实例。parseRequest方法解析请求行,获取一些值,并把这些值分配给HttpRequest对象。现在,我们来看看parseRequest方法:
Listing 3.4: The parseRequest method in the HttpProcessor class   
 
private void parseRequest(SocketInputStream input, OutputStre
  throws IOException, ServletException { 
   // Parse the incoming request line 
   input.readRequestLine(requestLine); 
   String method = 
     new String(requestLine.method, 0, requestLine.methodEnd
   String uri = null; 
   String protocol = new String(requestLine.protocol, 0, 
     requestLine.protocolEnd); 
  // Validate the incoming request line 
   if (method, length () < 1) { 
     throw new ServletException("Missing HTTP request method"); 
   } 
   else if (requestLine.uriEnd < 1) { 
     throw new ServletException("Missing HTTP request URI"); 
   } 
   // Parse any query parameters out of the request URI 
   int question = requestLine.indexOf("?"); 
   if (question >= 0) { 
     request.setQueryString(new String(requestLine.uri, question + 1, 
       requestLine.uriEnd - question - 1)); 
     uri = new String(requestLine.uri, 0, question); 
   } 
   else { 
     request.setQueryString(null); 
     uri = new String(requestLine.uri, 0, requestLine.uriEnd); 
   }  
   // Checking for an absolute URI (with the HTTP protocol) 
   if (!uri.startsWith("/")) { 
     int pos = uri.indexOf("://"); 
     // Parsing out protocol and host name 
     if (pos != -1) { 
       pos = uri.indexOf('/', pos + 3); 
       if (pos == -1) { 
         uri = ""; 
       } 
       else { 
         uri = uri.substring(pos); 
       } 
     } 
   } 
 
   // Parse any requested session ID out of the request URI 
   String match = ";jsessionid="; 
   int semicolon = uri.indexOf(match); 
   if (semicolon >= 0) { 
     String rest = uri.substring(semicolon + match,length()); 
     int semicolon2 = rest.indexOf(';'); 
     if (semicolon2 >= 0) { 
       request.setRequestedSessionId(rest.substring(0, semicolon2)); 
       rest = rest.substring(semicolon2); 
     } 
     else { 
       request.setRequestedSessionId(rest); 
       rest = ""; 
     } 
     request.setRequestedSessionURL(true); 
     uri = uri.substring(0, semicolon) + rest; 
   }   
   else { 
      request.setRequestedSessionId(null); 
     request.setRequestedSessionURL(false); 
   } 
 
   // Normalize URI (using String operations at the moment) 
   String normalizedUri = normalize(uri); 
   // Set the corresponding request properties 
   ((HttpRequest) request).setMethod(method); 
   request.setProtocol(protocol); 
   if (normalizedUri != null) { 
     ((HttpRequest) request).setRequestURI(normalizedUri); 
   } 
   else { 
     ((HttpRequest) request).setRequestURI(uri); 
   } 
   if (normalizedUri == null) { 
     throw new ServletException("Invalid URI: " + uri + "'"); 
   } 
}

ParseRequest方法在SocketInputStream类的readRequestLine方法调用后执行。
input.readRequestLine(requestLine);

requestLine是一个HttpProcessor 内部的HttpRequestLine的实例。
private HttpRequestLine requestLine = new HttpRequestLine();

调用它的readRequestLine方法告诉SocketInputStream来填充HttpRequestLine实例。

接下来,parseRequest方法获取了请求行的方法(GET…),URI和协议。
String method = new String(requestLine.method, 0, requestLine.methodEnd); 
String uri = null; 
String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);

但是,这里可能在URI后面会有查询字符串。如果存在的话,查询字符串被一个“?”分割。此外,parseRequest方法试图获取查询字符串和调用setQueryString方法来填充HttpRequest对象。

 // Parse any query parameters out of the request URI 
     int question = requestLine.indexOf("?"); 
     if (question >= 0) { // there is a query string. 
       request.setQueryString(new String(requestLine.uri, question + 1, 
         requestLine.uriEnd - question - 1)); 
       uri = new String(requestLine.uri, 0, question); 
     } 
     else { 
       request.setQueryString (null); 
       uri = new String(requestLine.uri, 0, requestLine.uriEnd); 
      }

但是,大多数是一个URI指向一个相关资源,一个URI也可以是一个绝对的值,例如:
http://www.brainysoftware.com/index.html?name=Tarzan

parseRequest方法也会检查:
// Checking for an absolute URI (with the HTTP protocol) 
     if (!uri.startsWith("/")) { 
       // not starting with /, this is an absolute URI 
       int pos = uri.indexOf("://"); 
       // Parsing out protocol and host name 
       if (pos != -1) { 
         pos = uri.indexOf('/', pos + 3); 
         if (pos == -1) { 
           uri = ""; 
         } 
         else { 
           uri = uri.substring(pos); 
         } 
       } 
     }

然后,查询字符串也可以包含一个session标识符,通过jsessionid的参数名表明。parseRequest方法的也检查一个session的标识符。如果在查询字符串中找到jsessionid,这个方法获得session标识符,通过调用它的setRequestedSessionId方法来把这个值分配给HttpRequest实例。
// Parse any requested session ID out of the request URI 
     String match = ";jsessionid="; 
     int semicolon = uri.indexOf(match); 
     if (semicolon >= 0) { 
       String rest = uri.substring(semicolon + match.length()); 
       int semicolon2 = rest.indexOf(';'); 
       if (semicolon2 >= 0) { 
         request.setRequestedSessionId(rest.substring(0, semicolon2)); 
         rest = rest.substring(semicolon2); 
       } 
 
       else { 
         request.setRequestedSessionId(rest); 
         rest = ""; 
       } 
       request.setRequestedSessionURL (true); 
       uri = uri.substring(0, semicolon) + rest; 
     } 
     else { 
       request.setRequestedSessionId(null); 
       request.setRequestedSessionURL(false); 
     }

如果找到jsessionid,这也意味着session标识符在一个查询字符串中存储着。而不是在一个cookie中。传递true给request的setRequestSessionURL方法。反之,传递false给setRequestSessionURL方法,再传递null给setRequestSessionId方法。
这时,uri的值被jsessionid抽离出来。

然后,parseRequest方法传递uri给normalize方法来修正一个“异常”的URI。例如:任何“\”会被替换成“/”。如果uri格式正确或者这个异常被修正。normalize返回这个正确的uri或者被修正后的uri。如果URI不能被修正,它将会被认为不合法且normalize返回null。这种情况下(返回null),parseRequest方法会在方法最后抛出一个异常。

最后,parseRequest方法给HttpRequest对象设置属性:
  ((HttpRequest) request).setMethod(method); 
     request.setProtocol(protocol); 
     if (normalizedUri != null) { 
       ((HttpRequest) request).setRequestURI(normalizedUri); 
     } 
     else { 
       ((HttpRequest) request).setRequestURI(uri); 
     }

此外,如果从normalize方法的返回值是null,方法抛出一个异常:
if (normalizedUri == null) { 
       throw new ServletException("Invalid URI: " + uri + "'"); 
    }

1
0
分享到:
评论

相关推荐

    tomcat原理解析书(how-tomcat-works)中文版

    适合读者 1.jsp/servlet 开发人员,想了解 tomcat 内部机制的 coder;...2.想加入 tomcat 开发团队的 coder; 3.web 开发人员,但对软件开发很有兴趣的 coder; 4.想要对 tomcat 进行定制的 coder。

    Bad Programming Practices 101 Become a Better Coder by Learning How (Not) epub

    Bad Programming Practices 101 Become a Better Coder by Learning How (Not) to Program 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书

    phpcoder.rar

    - **插件扩展**:PHPCoder支持第三方插件,可以通过社区下载安装增强功能。 - **代码格式化**:使用“编辑”&gt;“格式化代码”对代码进行整理,保持良好的编码风格。 - **搜索与替换**:利用查找和替换功能,可以在...

    matlab Embedded Coder Getting Started Guide.pdf

    Embedded Coder用于产生嵌入式处理器、目标快速原型板和大规模生产中使用的微处理器的可读的、紧凑的、快速的C和C++代码。...你可以将第三方开发环境引入到工程构建过程中,以产生用于在嵌入式系统上应用的可执行文件。

    MediaCoder答题器

    6. 插件扩展:MediaCoder有一个开放的插件架构,用户可以通过安装第三方插件来增加新的编码解码器、滤镜和其他功能,扩展其应用范围。 7. 用户友好:MediaCoder的界面简洁直观,即便是对编码不熟悉的用户也能快速...

    mediacoder专业版

    mediacoder 5685专业版,无普通版的限制

    simulink hdl coder 用户手册pdf

    #### 二、Simulink HDL Coder 的主要功能特点 1. **模型到 HDL 的转换**:Simulink HDL Coder 支持将 Simulink 模型转换成 VHDL 或 Verilog HDL 代码。这些模型可以包括数学运算、信号处理、通信协议等多种类型的...

    Mediacoder基础教程

    **Mediacoder基础教程** Mediacoder是一款强大的多媒体编码工具,专为音频和视频转换而设计,支持多种格式,如MP4、AVI、MKV等。本教程将深入讲解如何利用Mediacoder进行视频压制,优化视频质量,同时合理权衡码率...

    The Clean Coder

    Martin, "The Clean Coder: A Code of Conduct for Professional Programmers" Prentice Hall | 2011 | ISBN: 0137081073 | 256 pages | PDF | 6 MB Programmers who endure and succeed amidst swirling ...

    CoolCoder 类生成工具

    虽然CoolCoder能够自动化大部分工作,但开发者依然可以自定义模板,对生成的代码进行微调,以满足特定项目的需求。这种灵活性确保了生成的代码既高效又符合项目风格。 6. **提高开发效率**: 使用CoolCoder,...

    MediaCoder

    MediaCoder是一款功能强大的多媒体转换工具,它支持广泛的音频和视频编码格式,使用户能够轻松地在不同设备之间转换媒体文件。这款软件适用于个人用户、专业音频和视频制作人员,以及那些希望在各种设备上享受多媒体...

    matlab coder基本函数教程

    这在某些情况下非常有用,特别是当原始 M 文件包含敏感信息且不希望直接暴露给第三方时。 ```matlab coder.allowpcode('plain') ``` #### 外部函数调用 - **coder.ceval 和 coder.extrinsic**:这两个函数用于...

    一维码第三方CODER

    总之,一维码第三方CODER服务是IT行业中不可或缺的一部分,它们为企业和开发者提供了强大而灵活的工具,用于管理和追踪物品信息。了解并熟练运用这类服务,可以极大地提升工作效率和数据管理的准确性。

    php coder编辑器

    PHPCoder用于快速开发和调试PHP应用程序,它很容易扩展和定制,完全能够符合开发者的个性要求.PHPCoder是一个非常实用的,功能强大的编程环境,而且它是免费的!

    HDL-Coder详细教程

    ### HDL-Coder详细教程知识点概述 #### 一、生成HDL代码前的准备工作 在开始从Simulink模型生成HDL代码之前,需要完成一系列的准备工作,确保模型能够顺利生成高质量的代码。 ##### 1.1 使用`hdlsetup`进行模型...

    MediaCoder.5755专业破解版

    MediaCoder行业版一款针对VOD及KTV视频点播行业开发的,用于转换和处理带有多音轨内容的视频节目的软件。它具备业界领先的视频编码引擎,在高性能转码的同时保持高画质,并通过丰富的视频滤镜增强画面视觉效果。作为...

    MediaCoder使用说明文档

    MediaCoder使用说明文档, mediaCoder usermanual,

    MatlabCoder使用-Matlab Coder的基本使用.pdf

    Matlab Coder是Mathworks公司推出的一款用于将Matlab代码转换成高效C代码的工具。从2004年开始,Matlab陆续在Simulink中添加了Embeded Matlab Function模块,2007年在Real-Time Workshop中引入了emlc函数(现在的...

    Embedded Coder.rar

    texasinstrumentsc2000.mlpkginstall 支持TI的C2000系列工具包,要求MATLAB R2017a及其以上版本。 安装方法:打开matlab,调整路径到mlpkginstall文件所在目录;在current folder窗口里双击mlpkginstall文件即可开始...

Global site tag (gtag.js) - Google Analytics