连接器

先说一句题外话,上一章中的servlet容器有一个问题:运行以下的servlet时,

public void service(ServletRequest req, ServletResponse resp) throws IOException{
        PrintWriter pw=resp.getWriter();
        pw.println("hello,this is row1");
        pw.print("hello,this is row2");
    }

只会显示:hello,this is row1。

为了解决这个问题,做了以下改动:首先,由一个普通的Request/Response对象变为规范的HttpServletRequest/HttpServletResponse

public class HttpResponse implements HttpServletResponse {
  
  OutputStream output;
  public HttpResponse(OutputStream output) {
      this.output = output;
  }

  /**
   * call this method to send headers and response to the output
   */
  public void finishResponse() {
    // sendHeaders();
    // Flush and close the appropriate output mechanism
    if (writer != null) {
      writer.flush();
      writer.close();
    }
  }

  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;
  }

}

而之前的实现为

public PrintWriter getWriter() throws IOException {
    // autoflush is true, println() will flush,
    // but print() will not.
    writer = new PrintWriter(output, true);
    return writer;
}

而且之前的ServletProcessor在调用完service方法后额外增加了一行:

((HttpResponse) response).finishResponse();

 

创建一个 HttpRequest 对象 

HttpRequest类的很多方法都留空(你需要等到第4章才会完全实现),但是servlet程序员 已经可以从到来的 HTTP 请求中获得头部,cookies 和参数。这三种类型的值被存储在下面几个 引用变量中:

protected HashMap headers = new HashMap(); 
protected ArrayList cookies = new ArrayList(); 
protected ParameterMap parameters = null; 

因为 HTTP 请求的解析是一项相当复杂的任务,分为以下内容:

  • 读取套接字的输入流
  • 解析请求行
  • 解析头部
  • 解析 cookies
  • 获取参数 

读取套接字的输入流 

SocketInputStream input = null; OutputStream output = null;
try {
input = new SocketInputStream(socket.getInputStream(), 2048);
...

拥有一个 SocketInputStream 是为了两个重要方法:readRequestLine和 readHeader,而在上一章中直接是直接使用的Socket的InputStream

解析请求行 

HttpProcessor 的 process 方法调用私有方法 parseRequest 用来解析请求行,例如一个 HTTP 请求的第一行。这里是一个请求行的例子:

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

请求行的第二部分是 URI 加上一个查询字符串。在上面的例子中,URI 是这样的:

/myApp/ModernServlet

不过,大多数情况下,URI 指向一个相对资源,URI 还可以是一个绝对值,就像下面所示:

http://www.brainysoftware.com/index.html?name=Tarzan 

在 servlet/JSP 编程中,参数名 jsessionid 是用来携带一 个会话标识符。会话标识符经常被作为 cookie 来嵌入,但是程序员可以选择把它嵌入到查询字符串去,例如,当浏览器的 cookie 被禁用的时候。

解析头部 

一个HTTP头部是用类HttpHeader来代表的。 

一些头部也需要某些属性的设置。例如,当 servlet 调用 javax.servlet.ServletRequest

的 getContentLength 方法的时候,content-length 头部的值将被返回。而包 cookies 的 cookie 头部将会给添加到 cookie 集合中。 

解析 Cookies 

Cookies 是作为一个 Http 请求头部通过浏览器来发送的。这样一个头部名为"cookie"并且 它的值是一些 cookie 名/值对。这里是一个包括两个 cookie:username 和 password 的 cookie 头部的例子。

Cookie: userName=budi; password=pwd;

另外系统会创造一个名为JSESSIONID的输出Cookie,或称为"Session Cookie",这个cookie也需要单独处理

获取参数 

参数可以在查询字符串或者请求内容里边找到。假如用户使用 GET 方法来请求 servlet 的话, 所有的参数将在查询字符串里边出现。 

在当用户使用POST方法发送请求的时候,内容长度大于零,并且内容类型是 application/x-www-form-urlencoded 的时候。从内容中解析参数

 

创建一个 HttpResponse 对象 

HttpResponse 类实现了 javax.servlet.http.HttpServletResponse。跟随它的是一个叫做 HttpResponseFacade 的 façade 类 

通过 OutputStreamWriter,写进去的字符通过一种特定的字符集被编码成字节。这种字符 集可以使用名字来设定,或者明确给出,或者使用平台可接受的默认字符集。write 方法的每次 调用都会导致在给定的字符上编码转换器的调用。在写入底层的输出流之前,生成的字节都会累 积到一个缓冲区中。缓冲区的大小可以自己设定,但是对大多数场景来说,默认的就足够大了。 注意的是,传递给 write 方法的字符是没有被缓冲的。 

总结

在本章中,你已经知道了连接器是如何工作的。建立起来的连接器是 Tomcat4的默认连接器的简化版 。正如你所知道的,因为默认连接器并不高效,所以已经被弃用了。例如,所有的 HTTP 请求头部都被解析了,即使它们没有在 servlet 中使用过。因此,默认连接器很慢,并且 已经被 Coyote 所代替了。Coyote 是一个更快的连接器,它的源代码可以在 Apache 软件基金会的网站中下载。不管怎样,默认连接器作为一个优秀的学习工具,将会在第4章中详细讨论。 

posted on 2016-05-04 19:57  cbwleft  阅读(71)  评论(0)    收藏  举报