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

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

    博客分类:
  • j2ee
阅读更多
Parsing Headers

一个HttpHeader类代表了一个HTTP的头部信息。这个类将在第四章讲解。现在,我们了解以下内容就足够:

 你可以通过使用它的无参构造函数来创建一个HttpHeader实例。
 一旦你有一个HttpHeader实例,你可以把它传递给SocketInputStream的readHeader方法。如果有头部信息可读的话,readHeader方法将相应地填充好HttpHeader对象。如果没有头部信息可读,HttpHeader实例的nameEnd和valueEnd字段都将为0。
 获取头部信息的名字和值,使用下面的方法:
String name = new String (header.name, 0, header.nameEnd);
String value = new String(header.value, 0, header.valueEnd);

parseHeader方法包含一个while循环持续从SocketInputStream读取头部信息,直到读取完所有的头部信息。这个循环在创建一个HttpHeader实例,并把它传递给SocketInputStream类的readHeader后开始执行:

  HttpHeader header = new HttpHeader();
  // Read the next header
  input.readHeader(header);

然后,你可以测试在输入流中是否有下一个头部信息可读。使用HttpHeader实例的nameEnd和valueEnd字段:

  if (header.nameEnd == 0) {
        if (header.valueEnd == 0) {
        return;
      } else {
          throw new ServletException (sm.getString("httpProcessor.parseHeaders.colon"));
      }
  }

如果有下一个头部信息,这个头部信息的name和value可以被获取:

String name = new String(header.name, 0, header.nameEnd);
String value = new String(header.value, 0, header.valueEnd);

一旦你得到了头部name和value。你调用addHeader方法把它添加到HttpRequest对象中的头部信息HashMap中。

request.addHeader(name, value);

一些头部信息也需要设置一些属性。例如:当servlet调用javax.servlet.ServletRequest的getContentLength方法,content-length头部信息的值被返回,cookie头部信息包含cookies被添加到cookie集合中。下面是处理过程:
if (name.equals("cookie")) { 
    ... // process cookies here 
} 
else if (name.equals("content-length")) { 
    int n = -1; 
    try { 
      n = Integer.parseInt (value); 
    } 
    catch (Exception e) { 
      throw new ServletException(sm.getString( 
        "httpProcessor.parseHeaders.contentLength")); 
    } 
    request.setContentLength(n); 
}
else if (name.equals("content-type")) { 
     request.setContentType(value); 
}


Parsing Cookies

Cookies被浏览器作为HTTP请求头部信息发送。这样的一个头部信息有一个name为“cookie”和value为cookie值的name/value键值对。下面有一个例子:

Cookie: userName=budi; password=pwd;

Cookie的解析是通过使用org.apache.catalina.util.RequestUtil类的parseCookieHeader方法完成的。这个方法接收cookie头部信息和返回一个javax.servlet.http.Cookie类型的数组。这个数组中元素的个数和cookie的name/value键值对的个数是相等的。

Listing 3.5: The org.apache.catalina.util.RequestUtil class's parseCookieHeader method   
 
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 ()])); 
}


Obtaining Parameters

在你的servlet需要调用javax.servlet.http.HttpServletRequest的getParameter,getParameterMap getParameterNames, 或者getParameterValues方法来获取一个或者全部参数之前,你并没有解析查询字符串或者HTTP请求体来获取参数。因此,HttpRequest中四个方法的实现总是以调用parseParameter方法开始。

这些参数只需要被解析一次,也只可能被解析一次,因为如果这些参数在请求体中被找到话,参数解析让SocketInputStream读取字节流,一直读取到字节流末端。HttpRequest类使用一个叫做parsed的boolean类型值的变量来表明这个参数是否被解析过。

参数可以在查询字符串或者请求体中找到。如果用户请求一个servlet使用GET方法,所有参数在查询字符串中。如果使用POST方法的话,你也可以在请求体中找到。所有的name/value键值对都被存储在一个HashMap中。Servlet程序员可以像使用一个Map一样获得参数。注意,servlet程序员不允许改变参数值。所以这里使用了一个特殊的HashMap:org.apache.catalina.util.ParameterMap.

这个ParameterMap类,继承自java.util.HashMap,使用一个叫locked的boolean类型的变量。如果locked的值为false的时候,name/value键值对只能被添加,更新或移除。如果locked的值为true是,在执行以上name/value键值对操作时,会抛出一个IllegalStateException异常。你可以在任何时候读取里面的值。ParameterMap类在下面给出,它重写了里面的方法来添加,更新和移除值。这些方法只有在locked变量值为false时才可以调用。

Listing 3.6: The org.apache.Catalina.util.ParameterMap class.   
 
package org.apache.catalina.util; 
import java.util.HashMap; 
import java.util.Map; 
 
public final class ParameterMap extends HashMap { 
   public ParameterMap() { 
     super (); 
   } 
   public ParameterMap(int initialCapacity) { 
     super(initialCapacity);  } 
   public ParameterMap(int initialCapacity, float loadFactor) { 
     super(initialCapacity, loadFactor); 
   } 
   public ParameterMap(Map map) { 
     super(map); 
   } 
   private boolean locked = false; 
   public boolean isLocked() { 
     return (this.locked); 
   } 
   public void setLocked(boolean locked) { 
     this.locked = locked; 
   } 
   private static final StringManager sm = 
     StringManager.getManager("org.apache.catalina.util"); 
   public void clear() { 
     if (locked) 
       throw new IllegalStateException  (sm.getString("parameterMap.locked")); 
     super.clear(); 
   } 
   public Object put(Object key, Object value) { 
     if (locked) 
       throw new IllegalStateException 
         (sm.getString("parameterMap.locked")); 
     return (super.put(key, value)); 
   } 
   public void putAll(Map map) { 
     if (locked) 
       throw new IllegalStateException 
         (sm.getString("parameterMap.locked")); 
     super.putAll(map); 
   } 
  public Object remove(Object key) { 
     if (locked) 
       throw new IllegalStateException 
         (sm.getString("parameterMap.locked")); 
     return (super.remove(key)); 
   }
}

现在我们来看看parseParameters方法是怎么工作的。

因为参数可以存在于查询字符串或者HTTP请求体中,parseParameters方法会检查查询字符串和请求体。一旦解析后,参数就可以在对象变量参数中被找到,所以这方法是通过检查parsed的boolean值:这值是true的话说明已经被解析过了。

if (parsed)
return;

然后,parseParameters方法创建一个叫做results的ParameterMap,把results指向参数。如果参数是null的话,就会创建一个新的ParameterMap。

ParameterMap results = parameters;
if (results == null)
    results = new ParameterMap();

然后。parseParameters方法打开parameterMap的锁来允许操作这个parameterMap:

results.setLocked(false);

接下来,parseParameters方法检查编码格式,如果编码格式是null,则制定一个默认的格式。

String encoding = getCharacterEncoding();
if (encoding == null)
encoding = "ISO-8859-1";

之后,parseParameters方法解析查询字符串。通过使用org.apache.Catalina.util.RequestUtil的parseParameters方法完成解析参数。

// Parse any parameters specified in the query string
String queryString = getQueryString();
try {
      RequestUtil.parseParameters(results, queryString, encoding);
    } catch (UnsupportedEncodingException e) {
      ;
}

接下来。如果用户使用POST方法发送请求时,这方法就会试图查看HTTP请求体中是否包含参数。内容长度大于0,内容类型是application/x-www-form-urlencoded。下面是解析请求体的代码:
  // Parse any parameters specified in the input stream 
    String contentType = getContentType(); 
    if (contentType == null) 
      contentType = ""; 
    int semicolon = contentType.indexOf(';'); 
    if (semicolon >= 0) { 
      contentType = contentType.substring (0, semicolon).trim(); 
    } 
    else { 
      contentType = contentType.trim(); 
    } 
    if ("POST".equals(getMethod()) && (getContentLength() > 0) 
      && "application/x-www-form-urlencoded".equals(contentType)) { 
      try { 
        int max = getContentLength();
int len = 0;
  byte buf[] = new byte[getContentLength()]; 
        ServletInputStream is = getInputStream(); 
        while (len < max) { 
          int next = is.read(buf, len, max - len); 
          if (next < 0 ) { 
            break; 
          } 
 
          len += next; 
        } 
        is.close(); 
        if (len < max) { 
          throw new RuntimeException("Content length mismatch"); 
        } 
        RequestUtil.parseParameters(results, buf, encoding); 
      } 
      catch (UnsupportedEncodingException ue) {    
; 
      } 
      catch (IOException e) { 
        throw new RuntimeException("Content read fail"); 
      } 
}

最后,parseParameters方法锁住ParameterMap。设置parsed为true,分配results给参数。

// Store the final results
results.setLocked(true);
parsed = true;
parameters = results;

Creating a HttpResponse Object

HttpResponse类实现了javax.servlet.http.HttpServletResponse接口。
有一个叫做HttpResponseFacade 的facade类。你使用的HttpResponse类只有一部分功能,例如:它的getWriter方法返回一个java.io.PrintWriter对象,但是当它的print方法被调用时不会自动刷新。在这章的应用程序中修正这个问题。要知道怎么修正,你需要知道这个Writer是怎样的。

在一个servlet内,你使用PrintWriter来写字符。你可以使用你想要的任何编码格式。字符将会以字节流的形式发送到浏览器。第二章ex02.pyrmont.HttpResponse类的getWriter方法:

  public PrintWriter getWriter() {
     // if autoflush is true, println() will flush,
     // but print() will not.
     // the output argument is an OutputStream
     writer = new PrintWriter(output, true);
     return writer;
   }

看,我们怎么创建一个PrintWriter对象?通过传递一个java.io.OutputStream实例。你传递到PrintWriter 的print或println方法的任何内容将会被转换成字节流,然后通过底层的(underlying)OutputStream发送出去。

在这章你可以使用一个ex03.pyrmont.connector.ResponseStream类的实例作为OutputStream给PrintWriter。注意ResponseStream类是间接地从java.io.OutputStream类中获取。

你也可以让ex03.pyrmont.connector.ResponseWriter类继承PrintWriter类。ResponseWriter类重写所有的print和println方法,让所有调用这些方法都自动刷新output到底层OutputStream。所以,我们使用一个带有底层的ResponseStream 对象的ResponseWriter实例。

我们可以通过传递一个ResponseStream对象的实例来初始化ResponseWriter类。但是,我们使用一个java.io.OutputStreamWriter对象来服务,就像是当做一个连接ResponseWriter对象和ResponseStream对象的桥。

有了OutputStreamWriter,字符被指定的字符集编码成字节。字符集可以用名字来指定,或者可能明确的给出,或是平台的默认许可的字符集。每一个write方法的调用都会引起编码转换,转换成指定的字符。这些所有被处理后的字节在写到底层输出流之前都在一个buffer中存储起来。buffer的大小可以被指定,但是默认的大小已经适合大多数的情况了。注意:传递给write方法的字符没有被缓冲(buffered)。下面是getWriter方法:

public PrintWriter getWriter() throws IOException {
ResponseStream newStream = new ResponseStream(this);
    newStream.setCommit(false);
    OutputStreamWriter osr = new OutputStreamWriter(newStream, getCharacterEncoding());
    writer = new ResponseWriter(osr);
    return writer;
  }

Static Resource Processor and Servlet Processor

ServletProcessor类和第二章的ex02.pyrmont.ServletProcessor类相似。他们都只有一个方法:process。但是,ex03.pyrmont.connector.ServletProcessor中的process方法接收一个HttpRequest和一个HttpResponse代替了Request和Response实例。下面是process方法:

public void process(HttpRequest request, HttpResponse response) {

此外,process方法使用HttpRequestFacade和HttpResponseFacade的facade类。同样的,它调用servlet的service方法之后调用HttpResponse类的finishResponse方法。

servlet = (Servlet) myClass.newInstance();      
HttpRequestFacade requestPacade = new HttpRequestFacade(request);
    HttpResponseFacade responseFacade = new HttpResponseFacade(response);
    servlet.service(requestFacade, responseFacade);
    ((HttpResponse) response).finishResponse();

StaticResourceProcessor类和ex02.pyrmont.StaticResourceProcessor类完全一样。

Running the Application

运行这个应用程序。http://localhost:8080/servlet/PrimitiveServlet
你可以看到结果:

Hello. Roses are red.
Violets are blue.

注意:第二章中没有打印出第二行的信息Violets are blue.
                                                                                    

第三章 完
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删除 查看此书详细信息请在美国亚马逊官网搜索此书

    matlab Embedded Coder Getting Started Guide.pdf

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

    一维码第三方CODER

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

    phpcoder.rar

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

    MediaCoder答题器

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

    mediacoder专业版

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

    simulink hdl coder 用户手册pdf

    #### 三、Simulink HDL Coder 的工作流程 1. **模型设计**:使用 Simulink 进行系统级的设计和仿真,创建包含算法逻辑的模型。 2. **代码生成配置**:根据目标 FPGA 设备和性能需求,配置代码生成选项,如选择...

    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,...

    matlab coder基本函数教程

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

    MediaCoder

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

    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