- 浏览: 202633 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
qigeminghaofan:
第二个应用中说第一个不安全,熟悉servlet内部的程序员能向 ...
[How Tomcat Works]第2章 一个简单的Servlet容器 -
lliiqiang:
substring内存泄露问题只需要再加一个方法,让程序员明白 ...
优化变成了忧患:String.split引发的“内存泄露” -
tonghaoqi__:
必须点赞。。
如何控制War包访问Tomcat的内部实现类 -
jzzwy:
好文章 支持
Tomcat 5.5.26源代码分析——启动过程(一) -
wangzhewang:
牛!,希望更多分享!
Linux随机数发生器导致Apache进程全部Block的问题追查
译者 jarfield
博客 http://jarfield.iteye.com
概 述
就像《简介》中介绍的,Catalina
中有两个主要模
块:Connector
(连接器)和Container
(容器)。本章,你将编写一个连接
器
来增强第2章的应用,该连接器
能够创建更好的Request
和Response
对
象。符合Servlet 2.3
和2.4
规范的连接器
必须创建javax.servlet.http.HttpServletRequest
实例和javax.servlet.http.HttpServletResponse
实例,并将它们作
为参数传递给servlet
的service
方法。第2章的Servlet
容器只
能运行实现了javax.servlet.Servlet
接口的servlet
,并传递javax.servlet.ServletRequest
实
例和javax.servlet.ServletResponse
实例给servlet
的service
方
法。连接器
并不知道servlet
的类型(例如,是否实现了javax.servlet.Servlet
接
口, 继承了javax.servlet.GenericServlet
,或继承了javax.servlet.http.HttpServlet
), 因此它必须始终提供HttpServletRequest
实例和HttpServletResponse
实
例。
本章的应用程序中,连接器
解析HTTP
请求的headers
,
使得servlet
可以获得headers
、cookies
、参数名/值,
等等。我们也会完善第2章中Response
类的getWriter
方法,修正它的行为(译者注:第2
章中的实现是有问题的)。由于这些改进,我们可以从PrimitiveServlet
得
到完整的响应,同时也能够运行更加复杂的ModernServlet
。本章构建的连接器
是Tomcat 4
默认连接器
的一个简化版本,我们会在第4
章详细讨论Tomcat 4
的默认Connecotr
。虽然Tomcat
默
认连接器
在Tomcat
4
中已经不推荐使用了,但是它仍是一个很好的学习工具。在本章接下来的讨论中,凡是提到的连接器
,都是指本章构建的连接器
,而不是Tomcat
的默认连接器
。
提示:与上一章的
应用不同,本章应用中连接器
和容器是分离的。
本章应用的代码在ex03.pyrmont
包及其子包中。构成连接器
的类是ex03.pyrmont.connector
包
和ex03.pyrmont.connector.http
包的一部分。从本章开始,
每个应用都有一个bootstrap
类,用于启动整个应用。不过,目前还没有
停止应用的机制。应用一旦运行起来,你必须通过关闭控制台(Windows
平
台)或杀死进程(UNIX/Linux
平台)的粗鲁的方式来停止应用。
在解释应用之前,请允许我先介绍org.apache.catalina.util
包
中的StringManager
类。该类负责处理本应用及Catalina
自身各模块的错误消息的国际化。然后,我们会讨论整个应用。
StringManager 类
像Tomcat
这样的大型程序,都需要仔细地处理错误消息。在Tomcat
中,
错误消息对系统管理员和Servlet
程序员都很重要。例如,通过Tomcat
的错误日志,系统管理员可以轻松定位任何异常。Tomcat
为内部抛出的每个javax.servlet.ServletException
打
印出一条特定错误日志,这样Servlet
程序员就可以知道自己写的servlet
哪里出了问题。
Tomcat
采用的方法是将错误消息存储在一个属性(properties
)文件中,这样就可以方便地编辑错误消息。但是,Tomcat
有数百个类,如果将所有类的错误消息都存储在一个巨大的属性文件中,那么维护这些错误消息就是一个恶
梦。为了避免这个问题,Tomcat
为每个包定义了一个属性文件。例如,org.apache.catalina.connector
包中的属性文件包括了该包所有类抛
出的错误消息。每个属性文件都会被一个特定的org.apache.catalina.util.StringManager
实
例处理。Tomcat
运行的时侯,会有很多StringManager
实例,每个实例都会读取对应包中的属性文件。而且,由于Tomcat
十分流行,提供多语言版本的错误消息是很有意义的。目前,Tomcat
共支持三种语言。英语的属性文件名都是LocalStrings.properties
。其他两种语言是西班牙语和日语,其属性文件分别
为LocalStrings_es.properties
和LocalStrings_ja.properties
。
当类需要在属性文件中查找错误消息时,它首先获取一个StringManager
实
例。但是,同一个包中很多类都可能需要一个StringManager
实例,如果为每
个需要错误消息的类对象创建一个StringManager
实例,则是对资源的浪费。
因此StringManager
类被设计成,同一个包中所有类对象可以共享一个StringManager
实例。如果熟悉设计模式,你可能会猜到StringManager
是一个单例类(singleton class
)。StringManager
类
唯一的构造函数是私有的(private
),因此你不能使用new
关键字在类外部创建该类的实例。以包名为参数,调用StringManager
类的公开静态方法getManager
,
就可以获得一个StringManager
实例。每个实例被存储在一个Hashtable
中,key
就是包的名称。
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; }
提示:在附带的zip
文件中,可以找到一篇题为“The Singleton Pattern
”、关于单例模式的文章。
举个例子,为了使用ex03.pyrmont.connector.http
包中的StringManager
类,传递包名给StringManager
的getManager
方法:
StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");
在 ex03.pyrmont.connector.http 包 中,你可以找到三个属性文件:LocalStrings.properties 、LocalStrings_es.properties 和LocalStrings_ja.properties 。StringManager 实例根据应用程序运行时所在机器的区域(local ) 来决定使用哪个文件。如果你打开LocalStrings.properties ,非 注释的第一行应该是这样的:
httpConnector.alreadyInitialized=HTTP connector has already been initialized
要得到一条错误消息,你需要以错误码(error code
)为参数调用StringManager
类的getString
方
法。下面是该方法的多个重载之一:
public String getString(String key)
以“httpConnector.alreadyInitialized ” 为参数调用getString 方法,就会返回“HTTP connector has already been initialized ”。
应用程序
从本章开始,每章附带的应用程序被化分成模块。本章的应用包括三个模块:connector
、startup
和core
。
startup
模块只包括一个类:Bootstrap
,
其作用是启动整个应用。connector
模块的类可以分成5个类别:
- connector 和它的支持(supporting )类(HttpConnector 和HttpProcessor )
- 代表HTTP 请求的类(HttpRequest )及 其支持类
- 代表HTTP 响应的类(HttpResponse )及其支持类
- 门面(Facade )类(HttpRequestFacade 和HttpResponseFacade )
- Constant 类
core
模块包括两个类:ServletProcessor
和StaticResourceProcessor
。
Figure 3.1
是本应用的类图。为了让类图更具可读性,HttpRequest
和HttpResponse
相关的类都被省略了。我们后面讨论Request
和Response
对
象时,会给出更加详细的类图。
我们把Figure 3.1
和Figure 2.1
做个比较。第2
章的HttpServer
类被拆分成两个类:HttpConnector
和HttpProcessor
,Request
类被HttpRequest
类
替换,Response
类被HttpResponse
类
替换。而且,本章的应用使用了更多其他的类。
第2章中的HttpServer
类
负责等待HTTP
请求,创建请求对象和响应对象。本章应用中,等待HTTP
请求的任务交给了HttpConnector
实
例,创建请求对象和响应对象的任务分配给了HttpProcessor
实例。
本章中,HTTP
请求对象由实现了javax.servlet.http.HttpServletRequest
接口的HttpRequest
类来代表。HttpRequest
对
象被转型为HttpServletRequest
实例,并传递给servlet
的service
方
法。因此,每个HttpRequest
实例必须拥有适当的域,以便servlet
使用它们。需要赋给HttpRequest
对
象的值包括URI
、query string
、参数、cookies
和
其他headers
等等。因为连接器
不知道servlet
需要哪些值,所以它
必须解析所有能够从HTTP
请
求获得的值。但是,解析HTTP
请求会带来昂贵(开销巨大)的字符串操作和其
他操作。如果只解析servlet
需要的值,那么就可能节省大量的CPU
周期。例如,如果servlet
不
需要任何请求(也就是,不调用javax.servlet.http.HttpServletRequest
的getParameter
、getParameterMap
、
getParameterNames或getParameterValues
方法),连接器
就不需要从query
string
或HTTP request body
中解析出
请求参数。Tomcat
的默认连接器
(包括本章应用中的连接器
)尝试通过“直
到真正需要时才解析请求参数”的方式来提高效率。Tomcat
的默认连接器
和我们的连接器
使
用SocketInputStream
类从Socket
的InputStream
中读取字节流。SocketInputStream
实例包装了Socket
的getInputStream
返回的java.io.InputStream
实例。SocketInputStream
类提供了两个重要方法:readRequestLine
和readHeader
。readRequestLine
返
回HTTP
请求的第一行,即包括URI
、HTTP
方法(method
)和HTTP
版
本的那一行。处理套接字输入流中的字节流就意味着,从第一个字节读取到最后一个字节(从不回退),因此readRequestLine
必
须只能被调用一次,而且必须在readHeader
方法之前调用。每调用一次readHeader
就可以读取一个header
名
/值对,而且应该重复调用直到所有的headers
都被读取。readRequestLine
的返回值是一个HttpRequestLine
实
例,readHeader
的返回值是一个HttpHeader
对象。我们将在下面讨论HttpRequestLine
和HttpHeader
。
HttpProcessor
对象负责创建HttpRequest
实
例,因此必须填充HttpRequest
实例的每个成员变量。HttpProcess
类使用它的parse
方
法来解析HTTP
请求的request line
和headers
。parse
方法的返回被赋值给HttpProcessor
对
象的成员变量。但是,parse
方法并不解析query string
和request body
中
的请求参数。这个任务留给了HttpRequest
对象自己(译者注:这就是延迟解
析)。只有servlet
需要一个参数时,query stirng
或request
body
才会被解析。
在前一章基础上的另一个改进,就是引入了启动类ex03.pyrmont.startup.Bootstrap
来启动整个应用。
我们将在下面这些小节中,详细解释本章的应用:
- 启动应用
- 连接器
- 创建HttpRequest 对象
- 创建HttpResponse 对象
- 静态资源处理器和serlvet 处 理器
- 运行应用
启动应用
我们从ex03.pyrmont.startup.Bootstrap
类启动整个应用。Listing 3.1
列出了该类的代码。
Listing 3.1: The Bootstrap class
package ex03.pyrmont.startup; import ex03.pyrmont.connector.http.HttpConnector; public final class Bootstrap { public static void main(String[] args) { HttpConnector connector = new HttpConnector(); connector.start(); } }
Bootstrap
类的main
方
法创建了一个HttpConnector
实例,并调用了它的start
方法。Listing
3.2
列出了HttpConnector
类的代码。
Listing 3.2:
The HttpConnector class's start method
package ex03.pyrmont.connector.http; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; public class HttpConnector implements Runnable { boolean stopped; private String scheme = "http"; public String getScheme() { return scheme; } public void run() { ServerSocket serverSocket = null; int port = 8080; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } while (!stopped) { // Accept the next incoming connection from the server socket Socket socket = null; try { socket = serverSocket.accept(); } catch (Exception e) { continue; } // Hand this socket off to an HttpProcessor HttpProcessor processor = new HttpProcessor(this); processor.process(socket); } } public void start() { Thread thread = new Thread(this); thread.start (); } }
连接器
ex03.pyrmont.connector.http.HttpConnector
类
代表了连接器
,其职责是创建等待HTTP
请求的服务器套接字。Listing 3.2
给
出了该类的代码。
HttpConnector
类实现了java.lang.Runnable
接口,因此它可以被自己的线程使用。当启动应用时,HttpConnector
的一个实例被创建,并执行其run
方法。
提示:你可以阅读文章“
Working
with Threads
”来回忆如何创建
Java
线程。
run
方法包括了一个while
循
环,用来做下面的事情:
- 等待HTTP 请求
- 为 每个请求创建HttpProcessor 实例
- 调用HttpProcessor 的process 方 法
马上你就能看到,HttpConnector 类和ex02.pyrmont.HttpServer1 类非常相似。从java.net.ServerSocket 类的accept 方法获得一个socket 之后发生了变 化,HttpConnector 类创建了一个HttpProcessor 实例,并以socket 为 参数调用其process 方法。
提示: HttpConnector 类拥有另一个名为 getSchema 的方法,该方法返回网络请求的 schema ( HTTP )。
HttpProcessor 类 的process 方法接受HTTP 请 求的socket 为参数。对于每个HTTP 请求,process 方法会做如下处 理:
- 创建一个HttpRequest
对象
- 创 建一个HttpResponse 对象
- 解析HTTP 请求的第一行和headers , 并填充HttpRequest 对象
- 传递HttpRequest 对象和HttpResponse 对 象给ServletProcessor 或StaticResourceProcessor
就像第2 章里那样,ServletProcessor 调 用了被请求的servlet 的service 方法,StaticResourceProcessor 发 送静态资源的内容(给客户端)。
Listing 3.3 列 出了process 方法的代码。
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 (); } }
SocketInputStream input = null; OutputStream output = null; try { input = new SocketInputStream(socket.getInputStream(), 2048); output = socket.getOutputStream(); // Then, it creates an HttpRequest instance and an HttpResponse instance and assigns // the HttpRequest to the HttpResponse. // create HttpRequest object and parse request = new HttpRequest(input); // create HttpResponse object response = new HttpResponse(output); response.setRequest(request);
本章应用的HttpResponse 类比第2 章 的Response 类要复杂很多。举例来说,你可以通过调用HttpResponse 类的setHeader 方 法向客户端发送headers 。
response.setHeader("Server", "Pyrmont Servlet Container");
parseRequest(input, output); parseHeaders (input);
然后,process方法根据请求URI的模式(pattern),将HttpRequest对象和HttpResponse对像甩给(hand off ... to)一个 ServletProcessor对象和一个 StaticResourceProcessor对象。
if (request.getRequestURI().startsWith("/servlet/")) { ServletProcessor processor = new ServletProcessor(); processor.process(request, response); } else { StaticResourceProcessor processor = new StaticResourceProcessor(); processor.process(request, response); }
最后,process 方法关闭socket 。
socket.close();
protected StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");
HttpProcessor 类的私有方法——parseRequest 、parseHeaders 和normalize —— 被调用来帮助填充HttpRequest 对象。在下一节“创建HttpRequest 对象”,我们将讨论这些方法。
创建HttpRequest 对象
HttpRequest 类 实现了javax.servlet.http.HttpServletRequest 接 口。附带还有一个叫做HttpRequestFacade 的门面类。Figure 3.2 展现了HttpRequest 和 相关类的类图。HttpRequest 类 的许多方法都是留空的(等待第4章才会全部实现),但是servlet 程序员 已经可以从HTTP 请求中获得headers 、cookies 和请求参数。这 三种值被存储在下面的引用变量中:
protected HashMap headers = new HashMap(); protected ArrayList cookies = new ArrayList(); protected ParameterMap parameters = null;
提示:我们会在“获取参数”小节解释 ParameterMap 类。
因此,servlet 程序员可以从javax.servlet.http.HttpServletRequest 下面这些方法中获取正确的值:getCookies 、getDateHeader 、getHeader 、getHeaderNames 、getHeaders 、getParameter 、getPrameterMap 、getParameterNames 和getParameterValues 。正如你在HttpRequest类中看到的,一旦获得headers 、cookies 和 请求参数,相关方法的实现就很简单了。
不用说,这里主要的挑战就是解析HTTP请求和填充HttpRequest 对象。对于headers 和cookies ,HttpRequest 类 提供了addHeader 和addCookie 方 法,HttpProcessor 类的prseHeaders 就调用了这两个方法。 请求参数是在需要时才被HttpRequest 类 的parseParameters 方法解析的。本节所有的方法都会被讨论到。
由于解析HTTP 请求是一个非常复杂的任务,因此本节被分成下面几个小节:
- 读 取套接字 的输入流
- 解析请求行(request line )
- 解析headers
- 解析cookies
- 获
取请求参数
读取套接字的输入流
在第1
、2
章中,ex01.pyrmont.HttpRequest
类和ex02.pyrmont.HttpRequest
类已经做了一部分解析HTTP
请求的工作。通过调用java.io.InputStream
类
的read
方法,我们可以从请求行获得HTTP
方法、URI
和HTTP
版本:
byte[] buffer = new byte [2048]; try { // input is the InputStream from the socket. i = input.read(buffer); }
第1、2章的应用中,我们没有尝试进一步解析HTTP请求。但是在本章的应用中,我们有了
ex03.pyrmont.connector.http.SocketInputStream
类——org.apache.catalina.connector.http.SocketInputStream
类
的一个拷贝。该类提供了一些方法,这些方法不但可以获得请求行,还可以获得headers
。
要构造SocketInputStream
的实例,我们需要传递两个参数:InputStream
对象,指定SocketInputStream
实
例缓冲区大小的整数。在本应用中,我们在ex03.pyrmont.connector.http.HttpProcessor
类
的process
方法中创建了一个SocketInputStream
实
例,代码片段如下所示:
SocketInputStream input = null; OutputStream output = null; try { input = new SocketInputStream(socket.getInputStream(), 2048); ...
正如前面提到的,使用SocketInputStream 类的原因是为了使用它的两 个重要方法:readRequestLine 和readHeader 。继续往下读。
解析请求行
HttpProcessor 类的process 方 法调用私有方法parseRequest 来解析请求行,即HTTP 请求的第一行。这里给出请求行的一个例子:
GET /myApp/ModernServlet?userName=tarzan&password=pwd HTTP/1.1
请求行
的第二部分是URI
和可选的query string
。在上面的例子中,URI是:
/myApp/ModernServlet
然后,问号之后的部分都是query stirng
。因此,query string
就是:
userName=tarzan&password=pwd
query string
可
以包含0
或多个参数。在上面的例子中,有两个参数名/值对:username/tarzan
和password/pwd
。在Servlet/JSP
编
程中,jsessionid
参数用来携带会话标识(session
identity)。会话标识通常嵌入在cookies
中,但是程序员可以选
择将会话标识嵌入在query string
中,例如在浏览器禁止cookie
的情况下。
当parseRequest
方法被HttpProcessor
类
的process
方法调用时,变量request
已
经指向了一个HttpRequest
实例。parseRequest
方法解析了请求行,获得了几个值,并将它们赋给HttpRequest
对
象。现在,我们来看看Listing 3.4
中parseRequest
方法的代码。
Listing 3.4: The parseRequest method in the HttpProcessor class
private void parseRequest(SocketInputStream input, OutputStream output) 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); }
如果找到jessionid
,也意味着会话标识由query string
来承载,而不在cookie
中。
因此,传递true
给request
对
象的setRequestSessionURL
方法。否则,传递false
给setRequestSessionURL
方
法,传递null
给setRequestSessionId
方
法。
这时,uri
的值已经不包含jsessionid
。接着,parseRequest
方
法传递uri
给normalize
方
法,以纠正“异常(abnormal
)”的URI。例如,任何\将被替换成
/。如果uri
的格式是正确的,或者异常已被纠正,normalize
方法就返回原来的uri
,
或者被纠正的URI
。如果uri
不
能被纠正,normalize
方法会认为uri
不合法,并返回null
。在这种情况下(normalize
方法返回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
,parseRequest
方法就抛出一个异常:
if (normalizedUri == null) { throw new ServletException("Invalid URI: " + uri + "'"); }
解 析Headers
HttpHeader 类描述了HTTP 头部。第4 章 将详细解释该类,现在我们只要知道下面几点就足够了:
- 通过该类的无参构造函数创建HttpHeader 实例。
- 一旦有了HttpHeader 实 例,你可以把它传递给SocketInputStream 的readHeader 方法。如果有header 可 以读,那么readHeader 方法会相应地填充HttpHeader 对象。如果没有header 可 以读,HttpHeader 的nameEnd 和valueEnd 域都被设置0 。
- 要 获得header 的名称和值,可以使用下面的代码:
String name = new String(header.name, 0, header.nameEnd); String value = new String(header.value, 0, header.valueEnd);
HttpHeader header = new HttpHeader(); // Read the next header input.readHeader(header);
然后,通过测试 HttpHeader 实例的nameEnd 和valueEnd 域来判断 输入流中有没有更多的header 可以读取:
if (header.nameEnd == 0) { if (header.valueEnd == 0) { return; } else { throw new ServletException (sm.getString("httpProcessor.parseHeaders.colon")); } }
如果有下一个header ,就可以获取header 的名称和值:
String name = new String(header.name, 0, header.nameEnd); String value = new String(header.value, 0, header.valueEnd);
一旦获得了header 的名称和值,就调用HttpRequest 对 象的addHeader 方法,将它们添加到用于存储header 的HashMap 中:
request.addHeader(name, value);
有的header 还需要设置一些属性。例如,当servlet 调 用javax.servlet.ServletRequest 的getContentLength 方法时,就返回content-length 的值。包含cookie 的cookie header 需要被添加到cookie 集合(collection )中。 于是,我们还需要做下面的处理:
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); }
Cookie 的解析在下一节“解析Cookies ”中讨论。
解析Cookies
Cookies 是作为HTTP 请求header 被 浏览器发送的。这种header 的名称是"cookie ",值是cookie 的 名/值对。这里有个例子,包含username 和password 两个cookie 的header :Cookie: userName=budi; password=pwd;
对Cookie 的解析,是通过org.apache.catalina.util.RequestUtil 类 的parseCookieHeader 方法完成的。该方法接受cookie header ,返回一个javax.servlet.http.Cookie 数 组。该数组元素的个数就是cookie header 中cookie 名/值对的数量。Listing 3.5 给出了parseCookieHeader 方法的代码。
Listing 3.5: The org.apache.catalina.util.RequestUtil class's parseCookieHeader method
public static Cookie[] parseCookieHeader(String header) { if ((header == null) || (header.length() < 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 ()])); }
这里是HttpProcessor 类parseHeader 方法中,负责处理cookies 的 那部分代码:
else if (header.equals(DefaultHeaders.COOKIE_NAME)) { Cookie cookies[] = RequestUtil.ParseCookieHeader (value); for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals("jsessionid")) { // Override anything requested in the URL if (!request.isRequestedSessionIdFromCookie()) { // Accept only the first session id cookie request.setRequestedSessionId(cookies[i].getValue()); request.setRequestedSessionCookie(true); request.setRequestedSessionURL(false); } } request.addCookie(cookies[i]); } }
剩余内容见:[How Tomcat Works]第3章 连接器(二)
评论
发表评论
-
如何控制War包访问Tomcat的内部实现类
2010-12-14 21:56 6317Tomcat默认部署了Manager应 ... -
[How Tomcat Works]第4章 Tomcat默认连接器
2010-06-29 20:17 8548译者 jarfield 博 客 ht ... -
[How Tomcat Works]第3章 连接器(二)
2010-04-10 22:50 7839译者 jarfield 博客 http://j ... -
Tomcat 5.5.26源代码分析——启动过程(二)
2010-03-25 14:38 7780init方法 Catalina_Home和Cat ... -
Tomcat 5.5.26源代码分析——启动过程(一)
2010-03-25 14:30 5033main方法 第一阶段 第二阶段 ... -
[How Tomcat Works]第2章 一个简单的Servlet容器
2010-03-14 00:43 4145译者 jarfield 博客 http: ... -
[How Tomcat Works]第1章 一个简单的Web服务器
2010-03-02 23:09 5523译者 jarfield 博客 http:// ... -
Tomcat 5.5.26的网络构建指南
2010-02-27 00:44 33481. 安装JDK ... -
[How Tomcat Works]第0章 介绍
2010-02-24 19:09 3223译者 jarfield 博客 ... -
Eclipse远程调试Tomcat
2010-01-24 20:24 4825最近,一直在研究Tomcat的工作内幕,主要的方法就是参考《H ...
相关推荐
《How Tomcat Works》中文版一书详细剖析了Tomcat服务器的内部工作机制。该书基于Tomcat 4.1.12和5.0.18两个版本,深入讲解了其servlet容器的架构和运作原理,尤其是代号为Catalina的核心组件。 Tomcat是一个开源的...
《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,中文版的提供使得国内开发者能够更方便地理解这一流行的开源Java Servlet容器。这本书不仅涵盖了Tomcat的基础知识,还详细解析了其内部机制,对于...
1. **架构概述**:Tomcat的架构基于服务器-客户端模型,主要由Catalina(Servlet容器)、 Coyote(HTTP/HTTPS连接器)和Jasper(JSP引擎)三个主要组件构成。Catalina处理Servlet,Coyote处理网络通信,Jasper编译和...
《How Tomcat Works》是一本深入解析Apache Tomcat工作原理的书籍,同时也包含了源码,为读者提供了理论与实践相结合的深入学习体验。Tomcat是一款广泛使用的开源Java Servlet容器,它是Apache软件基金会 Jakarta...
《译How Tomcat Works(第二章)》这篇文章主要讲解了Apache Tomcat服务器的工作原理,它是一个开源的Java Servlet容器,广泛用于部署Web应用程序。在这一章中,我们将深入探讨Tomcat如何处理HTTP请求,以及其内部架构...
《How Tomcat Works中文版》这本书是一本深入探讨Apache Tomcat服务器工作原理的专著。Apache Tomcat服务器,或简称为Tomcat,是世界上广泛使用的Java Servlet容器和JavaServer Pages(JSP)引擎,负责处理基于Java...
《How Tomcat Works》是一本针对Apache Tomcat服务器内部工作机制进行深入剖析的专业书籍。本书详细介绍了Tomcat 4.1.12和5.0.18两个版本的内部结构与运作原理,尤其着重于解释Catalina——Tomcat的Servlet容器的...
总的来说,《How Tomcat Works》是一本深度解析Tomcat的权威指南,无论你是初学者还是经验丰富的开发者,都能从中获得宝贵的洞见。通过学习这本书,你将能够更有效地管理和维护你的Tomcat服务器,同时提高你的Web...
《How Tomcat Works》这本书是理解Apache Tomcat服务器工作原理的重要资源,第三章主要探讨了Tomcat的架构和核心组件。以下是对这部分内容的详细解读: Tomcat作为一款开源的Java Servlet容器,其核心功能是解析...
《How Tomcat Works》这本书深入浅出地介绍了Apache Tomcat这款广泛应用的Java Servlet容器的工作原理。Tomcat作为开源软件,是许多Web应用的基础,尤其在轻量级开发和测试环境中非常常见。以下是对Tomcat核心知识点...
《HowTomcatWorks》是一本深入解析Apache Tomcat工作原理的书籍,中文版的发布使得更多的中国开发者能够理解和掌握这款广泛应用的开源Java Servlet容器的工作机制。Tomcat是Apache软件基金会Jakarta项目的一部分,它...
《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,包含了中英文两个版本。这本书对于理解Java Servlet和JavaServer Pages(JSP)容器的运作方式具有极高的价值,特别是对于那些想要深入理解Web应用...
《how tomcat works》是一本深入探讨Apache Tomcat内部工作原理的专业书籍。Apache Tomcat是一个开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages技术规范,提供了Java Web服务器的功能。对于Java ...
《How Tomcat Works》是一本深入解析Apache Tomcat服务器内部工作原理的重要参考资料,它提供了对Tomcat架构的全面理解,包括其设计、配置和优化。这本书的中文版和英文版都为读者提供了便利,无论你是母语为中文...
《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的专业书籍,对于任何希望深入了解Java Servlet和JavaServer Pages (JSP)容器的人来说,都是一份宝贵的资源。Tomcat作为最流行的开源Servlet容器,其内部...