为什么80%的码农都做不了架构师?>>>   

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 initializedpublic 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 {//停止flagboolean 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转交给HttpProcessorHttpProcessor 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 ();}}//解析RequestLineprivate void parseRequest(SocketInputStream input, OutputStream output){...}//解析Headersprivate 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 nameif (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);elseheader = "";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 URLif (!request.isRequestedSessionIdFromCookie()) {// Accept only the first session id cookierequest.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");}
}

转载于:https://my.oschina.net/itjava/blog/102428

《How Tomcat Works》读书笔记(三)Connector相关推荐

  1. 《How Tomcat Works》读书笔记(三)--Connector(连接器)

    <How Tomcat Works>读书笔记(三)--Connector(连接器) 这是<How Tomcat Works>第三四章的读书笔记.主要写了Tomcat4.0默认的 ...

  2. How tomcat works 读书笔记十四 服务器组件和服务组件

    之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...

  3. how tomcat works 读书笔记(一)----------一个简单的webserver

    http协议 若是两个人能正常的说话交流,那么他们间必然有一套统一的语言规则<在网络上server与client能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertex ...

  4. mysql数据库权威指南_MySQL_MySQL权威指南读书笔记(三),第二章:MYSQL数据库里面的数 - phpStudy...

    MySQL权威指南读书笔记(三) 第二章:MYSQL数据库里面的数据 用想用好MYSQL,就必须透彻理解MYSQL是如何看待和处理数据的.本章主要讨论了两个问题:一是SQL所能处理的数据值的类型:二是 ...

  5. 《编程之美》读书笔记(三):烙饼问题与搜索树

    <编程之美>读书笔记三:烙饼问题与搜索树 薛笛 EMail:jxuedi#gmail.com 前面已经写了一些关于烙饼问题的简单分析,但因为那天太累有些意犹未尽,今天再充实一些内容那这个问 ...

  6. TCPIP详解Protocol 读书笔记(三) IP协议讲解

    TCP/IP详解:Protocol 读书笔记(三) Chapter3 IP:网际协议 文章目录 TCP/IP详解:Protocol 读书笔记(三) Chapter3 IP:网际协议 IP协议 IP数据 ...

  7. 《大型网站技术架构》读书笔记三:大型网站核心架构要素

    来源:http://www.cnblogs.com/edisonchou/p/3806348.html 此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. ...

  8. 《淘宝技术这十年》读书笔记 (三). 创造技术TFS和Tair

    前面两篇文章介绍了淘宝的发展历程和Java时代的变迁:             <淘宝技术这十年>读书笔记 (一).淘宝网技术简介及来源             <淘宝技术这十年&g ...

  9. Spring揭秘 读书笔记 三 bean的scope与FactoryBean

    本书可作为王富强所著<<Spring揭秘>>一书的读书笔记  第四章 BeanFactory的xml之旅 bean的scope scope有时被翻译为"作用域&quo ...

  10. 《你的灯亮着吗》 读书笔记三

    紧接<你的灯亮着吗>读书笔记二 4.这是谁的问题? 当别人可以妥善解决自己的问题时,不要越俎代庖,如果这是他们的麻烦,就让它成为他们的麻烦,如果一个人处于解决问题的位置,却并不受问题困扰, ...

最新文章

  1. 浅谈程序员的“内卷化”
  2. php7 最新版本,总结PHP 7.x 各个版本的新特性
  3. pycharm怎么写yaml_K8S 如何面向 Yaml 编程
  4. Dubbo启动时检查
  5. php curl伪造referer与来源IP实例
  6. vue获取元素距离页面顶部的距离_VUE实时监听元素距离顶部高度的操作
  7. BZOJ 3039: 玉蟾宫( 悬线法 )
  8. C++笔记------模版
  9. Sublime 资源汇总
  10. 如果你是面试官,如何判断一个面试者的深度学习水平?
  11. 支付宝APP支付里设置应用网关和授权回调地址是不必填的
  12. 高通模式9008模式linux,重磅干货!高通9008模式与数据提取
  13. php使用加密狗,加密狗使用方法
  14. Crosses and Crosses POJ 3537
  15. git提交代码出现 fatal: The remote end hung up unexpectedly-错误处理
  16. 前后端分别实现集合根据中文拼音排序
  17. 哇咔咔!用Android手机控制电脑。
  18. SAP JCo应用方案
  19. 【Python】STEP 4
  20. 电路中极性与非极性电容并联的作用

热门文章

  1. c++ standard library_什么是C/C++的标准库?
  2. 买游戏来运营_「笔吧评测室」双十一快来了,买游戏本要做好心理准备
  3. Leetcode7 :整数反转(JAVA)
  4. 《系统集成项目管理工程师》必背100个知识点-22实施整体变更ITTO
  5. 项目立项管理:项目可行性分析和项目审批
  6. Android中通过自定义签名控件实现手写签名
  7. Docker中宿主机与容器之间互传文件(docker cp的方式)
  8. Winform中怎样跨窗体获取另一窗体的控件对象
  9. Flutter Raw Image Provider
  10. 服务器json文件怎么创建对象,如何从json文件(或xml文件)创建vb.net对象类