您的位置:首页 > 运维架构 > Tomcat

《How Tomcat Works》读书笔记(三)Connector

2013-01-16 11:38 423 查看
3.1 StringManager

为啥要先讲StringManager呢?
话说tomcat算是一个大型项目了(最新的tomcat7大概有35W行代码),因此处理报错信息就要狠小心,因为报错信息为开发者和系统管理员提供有用的线索。
tomcat把错误信息保存在properties文件中,项目又太大,不能只用一个文件,tomcat为每个需要错误信息的package都提供了一个properties文件。
每一个properties文件都是有一个StringManager的实例负责处理。于是tomcat启动后会存在很多StringManager实例。
每个package中的类共享一个StringManager实例。

public class StringManager {
//private构造器,单例
private StringManager(String packageName) {
...
}
//包名---StringManager实例映射
private static Hashtable<String, StringManager> managers =
new Hashtable<String, StringManager>();
//考虑了多线程安全,根据包名返回一个StringManager实例     public static final synchronized StringManager getManager(String packageName) {
StringManager mgr = managers.get(packageName);
if (mgr == null) {
mgr = new StringManager(packageName);
managers.put(packageName, mgr);
}
return mgr;
}
//根据errorCode拿到errorMessage
//e.g.  httpConnector.alreadyInitialized=HTTP connector has already been initialized
public String getString(String key) {
...
}
}
3.2 模块化

分为三个模块:connector , startup , core .
startup 模块只有一个类Bootstrap, 负责启动应用
connector 模块包含HttpConnector,HttpProcessor,HttpRequest和HttpResponse
core 模块包含ServletProcessor和StaticResourceProcessor

我们把《How Tomcat Works》读书笔记(二)中的HttpServer拆分成两个类:HttpConnector和HttpProcessor,把Request改成HttpRequest,Response改成HttpResponse。

HttpConnector:等待http请求、构建HttpProcessor,并把请求的socket转交给HttpProcessor

HttpProcessor:生成request、response对象,传递给ServletProcessor或StaticResourceProcessor

HttpRequest :implements javax.servlet.http.HttpServletRequest

HttpResponse:implements javax.servlet.http.HttpServletResponse

Bootstrap

public final class Bootstrap {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
//启动连接器
connector.start();
}
}

HttpConnector

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
*http连接器
**/
public class HttpConnector implements Runnable {
//停止flag
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) {
// 等待下一个到达ServerSocket的连接
Socket socket = null;
try {
socket = serverSocket.accept();
}catch (Exception e){
continue;
}
// 把socket转交给HttpProcessor
HttpProcessor processor = new HttpProcessor(this);
processor.process(socket);
}
}
public void start(){
Thread thread = new Thread(this);
thread.start ();
}
}

我们再来看一下HTTP请求的第一行:

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

绿色部分叫做query string

我们再来看一个HTTP请求对应的类:

这一行对应的类就是RequestLine

它下面的请求头对应的类就是HttpHeader

HttpProcessor

public class HttpProcessor{
HttpRequest request;
HttpResponse response;
private HttpRequestLine requestLine = new HttpRequestLine();

public void process(Socket socket) {
SocketInputStream input = null;
OutputStream output = null;
try {
input = new SocketInputStream(socket.getInputStream(), 2048);
output = socket.getOutputStream();
request = new HttpRequest(input);
response = new HttpResponse(output);
response.setRequest(request);
response.setHeader("Server", "Pyrmont Servlet Container");
parseRequest(input, output);
parseHeaders(input);

if (request.getRequestURI().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
}else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
socket.close();
} catch (Exception e) {
e.printStackTrace ();
}
}
//解析RequestLine
private void parseRequest(SocketInputStream input, OutputStream output){...}
//解析Headers
private void parseHeaders(SocketInputStream input,OutputStream output){...}
}

3.2.1 Parsing RequestLine

//从SocketInputStream中读入requestLine
input.readRequestLine(requestLine);

//获取http方法
String method = new String(requestLine.method, 0, requestLine.methodEnd);
//获取协议
String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);

//获取queryString并从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); }

//检查URI是不是绝对路径(with the HTTP protocol),如果是把URI转化成相对路径
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);
}
}
}

//检查jsessionid是否存在,如果存在,设置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);
}

// 纠正URI的错误(如把¥替换成/)
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 + "'");
}

3.2.2 Parsing Headers

//构造一个HttpHeader
HttpHeader header = new HttpHeader();

//从SocketInputStream中读出下一个Header
input.readHeader(header);

//判断下一个header是否存在
if (header.nameEnd == 0) {
if (header.valueEnd == 0) {
return;
}else {
throw new ServletException(sm.getString("httpProcessor.parseHeaders.colon"));    }
}

//获取下一个header的name、value
String name = new String(header.name, 0, header.nameEnd);
String value = new String(header.value, 0, header.valueEnd);

//加入到HttpRequest的HashMap<String,String> headers中
request.addHeader(name, value);
除了普通的header还存在一些需要特殊处理的header,如:

content-length header

cookie header

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

3.2.3 Parsing Cookies
下面看一个cookie header的示例 Cookie: userName=budi; password=pwd;
解析cookie用的是org.apache.catalina.util.RequestUtil这个类:

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 ()]));
}
在HttpProcessor中我们这样就使用它来解析cookie:

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

3.2.4 Obtaining Parameters

请求参数可能存在queryString和Http request body中。我们都要进行检查。

//解析queryString中的参数
String queryString = getQueryString();
try {
RequestUtil.parseParameters(results, queryString, encoding);
} catch (UnsupportedEncodingException e) {
;
}

//解析request body中的参数
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");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tomcat