├── .classpath
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── README.md
├── lib
├── commons-beanutils.jar
├── commons-collections.jar
├── commons-daemon.jar
├── commons-digester.jar
├── commons-logging.jar
├── commons-modeler.jar
├── jaas.jar
├── jakarta-regexp-1.2.jar
├── jcert.jar
├── jnet.jar
├── jsse.jar
├── mx4j.jar
├── naming-common.jar
├── naming-factory.jar
├── naming-resources.jar
├── servlet.jar
├── tomcat-util.jar
├── xercesImpl.jar
└── xmlParserAPIs.jar
├── src
├── ex01
│ └── WebServer
│ │ ├── HttpServer.java
│ │ ├── Request.java
│ │ └── Response.java
├── ex02
│ ├── ServletContainer1
│ │ ├── Constants.java
│ │ ├── HttpServer.java
│ │ ├── Request.java
│ │ ├── Response.java
│ │ ├── ServletProcessor.java
│ │ ├── StaticResourceProcessor.java
│ │ └── servletContainer.md
│ └── ServletContainer2
│ │ ├── Constants.java
│ │ ├── HttpServer2.java
│ │ ├── Request.java
│ │ ├── RequestFacade.java
│ │ ├── Response.java
│ │ ├── ResponseFacade.java
│ │ ├── ServletProcessor2.java
│ │ └── StaticResourceProcessor.java
└── ex03
│ └── connector
│ ├── RequestStream.java
│ ├── ResponseStream.java
│ ├── ResponseWriter.java
│ ├── ServletProcessor.java
│ ├── StaticResourceProcessor.java
│ ├── StringManager.java
│ ├── http
│ ├── Constants.java
│ ├── HttpConnector.java
│ ├── HttpHeader.java
│ ├── HttpProcessor.java
│ ├── HttpRequest.java
│ ├── HttpRequestLine.java
│ ├── HttpResponse.java
│ ├── RequestFacade.java
│ ├── ResponseFacade.java
│ └── SocketInputStream.java
│ └── startup
│ └── Boostrap.java
└── webroot
├── PrimitiveServlet.class
├── PrimitiveServlet.java
├── images
└── logo.gif
└── index.html
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | HowTomcatWorks
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.8
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.8
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HowTomcatWorks
2 |
3 | # 目录
4 |
5 | [一、简单的Web服务器](#一简单的web服务器)
6 |
7 | - 构成
8 | - 类说明
9 | - 运行
10 |
11 | [二、简单的Servlet容器](#二简单的servlet容器)
12 |
13 | - 类图
14 | - 类说明
15 | - 运行
16 |
17 | [三、连接器](#三连接器)
18 |
19 | - 作用
20 |
21 |
22 | - 类说明
23 |
24 | - connector
25 |
26 | - 。。。
27 |
28 |
29 | - http
30 | - startup
31 |
32 | ## 一、简单的Web服务器
33 |
34 | ### 构成
35 | 我们的服务器程序在ex01包里,由下面的三个java文件组成:
36 | - HttpServer
37 | - Request
38 | - Response
39 |
40 | ### 类说明
41 | 1. HttpServer: 代表一个web服务器,能提供公共静态final变量`WEB_ROOT`所在目录和其中子目录的所有静态资源
42 | - await():使用while循环接收来自客户端的请求,当请求为`SHUTDOWN`时终止循环
43 | 2. Request:代表一个HTTP请求,从负责与客户端通信的Socket中传递过来的`InputStream`构造成这个类的一个实例
44 | - parse():读取HTTP里的请求
45 | - parseUri():从parse后的字符串中解析uri
46 | 3. Response: 代表一个HTTP响应,通过传递由套接字获得的`OutputStream`和`HttpServer`的`await()`来构造
47 | - sendStaticResource():用来发送一个静态资源,没有则打印错误码
48 |
49 | ### 运行
50 |
51 | 1. 启动HttpServer类
52 | 2. 打开你的浏览器并在地址栏或网址框中敲入下面的命令:http://localhost:8080/index.html
53 | 3. 结果:
54 | - 控制台
55 |
56 | ```
57 | GET /index.html HTTP/1.1
58 | Host: localhost:8080
59 | Connection: keep-alive
60 | Upgrade-Insecure-Requests: 1
61 | User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
62 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
63 | DNT: 1
64 | Accept-Encoding: gzip, deflate, sdch, br
65 | Accept-Language: zh-CN,zh;q=0.8
66 | Cookie: oracle.uix=0^^GMT+8:00^p; _ga=GA1.1.1778589621.1515157486; Hm_lvt_512065947708a980c982b4401d14c2f5=1515157487,1516670331
67 | ```
68 |
69 | - 浏览器
70 |
71 |
72 | 
73 |
74 | ***
75 |
76 | ## 二、简单的Servlet容器
77 | ### 类图
78 | 类图大致如下:
79 | 
80 |
81 | ### 类说明
82 | #### ex01
83 | 1. HttpServer1:和上面的HttpServer差不多,不过同时支持静态资源和Servlet
84 | - await():可以将请求分发给`StaticResourceProcessor`和`ServletProcessor`,如果字符串包含`/servlet/`,请求将分发给后者
85 | 2. Request:继承自`ServletRequest`,代表一个Request对象并传给service方法
86 | - parse():读取HTTP里的请求
87 | - parseUri():从解析后的字符串中解析uri
88 | 3. Response:继承自`ServletResponse`,代表一个Response对象并传给service方法
89 | - sendStaticResource():用来发送一个静态资源
90 | - getWriter():重写方法,使用了`PrintWriter`
91 | 4. ServletProcessor1:加载Servlet
92 | - process():获取URI,创建一个类加载器并告诉类加载器加载的位置,即`Constants`指向的目录
93 | 5. StaticResourceProcessor:提供静态资源
94 | - process():调用Response的`sendStaticResource()`
95 | 6. Constants:指向静态资源保存的位置
96 |
97 | #### ex02 改进版
98 | 在`ex01`中,我们直接使用Request和Response直接向上转型为`ServletRequest `和 `ServletResponse`,这会危害到安全性。知道这点的程序员可以将`ServletRequest `和 `ServletResponse`向下转型为Request和Response,并调用他们的方法,如Request的`parseUri()`和Response的`sendStaticResource()`。一个解决方法是将它们的方法设为私有的,但这在内部就不可见了。当然可以设为默认修饰的,这样就内部可见了。但我们使用另一种更优雅的解决方法:**Facade模式**,又称门面模式、外观模式。
99 |
100 | 
101 | 1. RequestFacade:构造了一个 `RequestFacade` 对象并把它传递给 `service() `方法,而不是向下转换Request 对象为` ServletRequest `对象并传递给 service 方法。Servlet 程序员仍然可以向下转换`ServletRequest `实例为 `RequestFacade`,不过它们只可以访问 `ServletRequest` 接口里边的公共方法。现在 `parseUri()` 方法就是安全的了 。
102 |
103 | - RequestFacade(Request request) :将传入的Request赋值给私有的`ServletRequest`对象
104 |
105 | 2. ResponseFacade:同上
106 |
107 | ### 运行
108 |
109 | 1. 启动HttpServer类
110 | 2. 打开你的浏览器并在地址栏或网址框中敲入下面的命令:http://localhost:8080/servlet/PrimitiveServlet
111 | 3. 结果:
112 | - 控制台
113 |
114 | ```
115 | GET /servlet/PrimitiveServlet HTTP/1.1
116 | Host: localhost:8080
117 | Connection: keep-alive
118 | Upgrade-Insecure-Requests: 1
119 | User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
120 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
121 | DNT: 1
122 | Accept-Encoding: gzip, deflate, sdch, br
123 | Accept-Language: zh-CN,zh;q=0.8
124 | Cookie: oracle.uix=0^^GMT+8:00^p; _ga=GA1.1.1778589621.1515157486; Hm_lvt_512065947708a980c982b4401d14c2f5=1515157487,1516670331
125 |
126 |
127 | from service
128 | ```
129 |
130 | ***
131 |
132 | ## 三、连接器
133 |
134 | Catalina 中有两个主要的模块:`连接器`和`容器`
135 |
136 | ### 作用
137 |
138 | 解析HTTP请求头部并让Servlet可以获得头部,cookie,参数名/值等。
139 |
140 | ### 类说明
141 |
142 | StringManager:为每个包都分配一个属性文件,用来存储错误信息,一个StringManager实例被一个包下的文件共享,这是一个单例类
143 |
144 | - getManager():获取一个StringManager实例
145 |
146 | - getString():返回一个错误信息
147 |
148 |
149 | ServletProcessor :加载Servlet
150 |
151 | - process():获取URI,创建一个类加载器并告诉类加载器加载的位置,并调用Servlet的`service`方法
152 |
153 | StaticResourceProcessor :提供静态资源请求
154 |
155 | - process():调用`HttpResponse` 的`sendStaticResource()`
156 |
157 | RequestStream:继承自`ServletInputStream `
158 |
159 | ResponseStream :继承自`ServletOutputStream `
160 |
161 | ResponseWriter :一个自动刷新的`PrintWriter`
162 |
163 | #### http
164 |
165 | 1. HttpConnector :连接器,继承Runable,创建一个套接字等待HTTP请求
166 | - run():等待HTTP请求,为每个请求创建`HttpProcessor`实例,并调用`HttpProcessor`的`process`方法
167 | - start():启动一个新线程
168 | 2. HttpProcessor :
169 | - process():
170 | - 创建一个`HttpRequest`对象
171 | - 创建一个`HttpResponse`对象
172 | - 解析的HTTP请求的第一行和头部,并放入`HttpRequest`对象
173 | - 解析`HttpRequest`和`HttpResponse`对象到`ServletProcessor `或`StaticResourceProcessor `
174 | - parseHeaders():解析请求头部
175 | - parseRequest():解析请求行
176 | - normalize():规范化地址
177 | 3. HttpRequest:HTTP请求类
178 | - 读取套接字的输入流
179 | - 解析请求行
180 | - 解析头部
181 | - 解析cookies
182 | - 获取参数
183 | 4. HttpResponse:HTTP响应类
184 | 5. HttpHeader:HTTP头部类
185 | 6. RequestFacade:`HttpRequest`门面类
186 | 7. ResponseFacade:`HttpResponse`门面类
187 | 8. HttpRequestLine :HTTP请求行类
188 | 9. SocketInputStream :扩展InputStream以获得更高效的读取行
189 | 10. Constants:指向静态资源保存的位置
190 |
191 | #### startup
192 |
193 | - boostrap:启动连接器
194 |
195 |
196 |
--------------------------------------------------------------------------------
/lib/commons-beanutils.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/commons-beanutils.jar
--------------------------------------------------------------------------------
/lib/commons-collections.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/commons-collections.jar
--------------------------------------------------------------------------------
/lib/commons-daemon.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/commons-daemon.jar
--------------------------------------------------------------------------------
/lib/commons-digester.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/commons-digester.jar
--------------------------------------------------------------------------------
/lib/commons-logging.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/commons-logging.jar
--------------------------------------------------------------------------------
/lib/commons-modeler.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/commons-modeler.jar
--------------------------------------------------------------------------------
/lib/jaas.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/jaas.jar
--------------------------------------------------------------------------------
/lib/jakarta-regexp-1.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/jakarta-regexp-1.2.jar
--------------------------------------------------------------------------------
/lib/jcert.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/jcert.jar
--------------------------------------------------------------------------------
/lib/jnet.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/jnet.jar
--------------------------------------------------------------------------------
/lib/jsse.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/jsse.jar
--------------------------------------------------------------------------------
/lib/mx4j.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/mx4j.jar
--------------------------------------------------------------------------------
/lib/naming-common.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/naming-common.jar
--------------------------------------------------------------------------------
/lib/naming-factory.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/naming-factory.jar
--------------------------------------------------------------------------------
/lib/naming-resources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/naming-resources.jar
--------------------------------------------------------------------------------
/lib/servlet.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/servlet.jar
--------------------------------------------------------------------------------
/lib/tomcat-util.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/tomcat-util.jar
--------------------------------------------------------------------------------
/lib/xercesImpl.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/xercesImpl.jar
--------------------------------------------------------------------------------
/lib/xmlParserAPIs.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoldenLiang/HowTomcatWorks/50c76d90d098377af0799a7306595cb37432a3a6/lib/xmlParserAPIs.jar
--------------------------------------------------------------------------------
/src/ex01/WebServer/HttpServer.java:
--------------------------------------------------------------------------------
1 | package ex01.WebServer;
2 | import java.io.File;
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.OutputStream;
6 | import java.net.InetAddress;
7 | import java.net.ServerSocket;
8 | import java.net.Socket;
9 |
10 | /**
11 | * @author lc
12 | * 一个简单的 Web 服务器
13 | */
14 | public class HttpServer {
15 |
16 | public static final String WEB_ROOT =
17 | System.getProperty("user.dir") + File.separator + "webroot";
18 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
19 | private boolean shutdown = false;
20 |
21 | public static void main(String[] args) {
22 | HttpServer server = new HttpServer();
23 | server.await();
24 | }
25 |
26 | public void await() {
27 | ServerSocket serverSocket = null;
28 | int port = 8080;
29 | try {
30 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
31 | } catch (IOException e) {
32 | e.printStackTrace();
33 | System.exit(1);
34 | }
35 | //循环等待请求
36 | while(!shutdown) {
37 | Socket socket = null;
38 | InputStream input = null;
39 | OutputStream output = null;
40 | try {
41 | socket = serverSocket.accept();
42 | input = socket.getInputStream();
43 | output = socket.getOutputStream();
44 |
45 | //创建请求对象并解析请求数据
46 | Request request = new Request(input);
47 | request.parse();
48 |
49 | //创建响应对象
50 | Response response = new Response(output);
51 | response.setRequest(request);
52 | response.sendStaticResource();
53 |
54 | //关闭Socket
55 | socket.close();
56 | //检测请求是不是SHUTDOWN命令
57 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
58 | } catch (Exception e) {
59 | e.printStackTrace();
60 | continue;
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/ex01/WebServer/Request.java:
--------------------------------------------------------------------------------
1 | package ex01.WebServer;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 |
6 | /**
7 | * @author lc
8 | * HTTP 请求
9 | */
10 | public class Request {
11 |
12 | private InputStream input;
13 | private String uri;
14 |
15 | public Request(InputStream input) {
16 | this.input = input;
17 | }
18 |
19 | public void parse() {
20 | //
21 | StringBuffer request = new StringBuffer(20);
22 | int i;
23 | byte[] buffer = new byte[2048];
24 | try {
25 | i = input.read(buffer);
26 | } catch (IOException e) {
27 | e.printStackTrace();
28 | i = -1;
29 | }
30 | for(int j = 0; j < i; j++) {
31 | request.append((char)buffer[j]);
32 | }
33 | System.out.println(request.toString());
34 | uri = parseUri(request.toString());
35 | }
36 |
37 | private String parseUri(String requestString) {
38 | int index1, index2;
39 | index1 = requestString.indexOf(' ');
40 | if(index1 != -1) {
41 | index2 = requestString.indexOf(' ', index1 + 1);
42 | if(index2 > index1)
43 | return requestString.substring(index1 + 1, index2);
44 | }
45 | return null;
46 | }
47 |
48 | public String getUri() {
49 | return uri;
50 | }
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/ex01/WebServer/Response.java:
--------------------------------------------------------------------------------
1 | package ex01.WebServer;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.IOException;
6 | import java.io.OutputStream;
7 |
8 | /**
9 | * @author lc
10 | * HTTP 响应
11 | */
12 | public class Response {
13 |
14 | private static final int BUFFER_SIZE = 1024;
15 | Request request;
16 | OutputStream output;
17 |
18 | public Response(OutputStream output) {
19 | this.output = output;
20 | }
21 |
22 | public void setRequest(Request request) {
23 | this.request = request;
24 | }
25 |
26 | /**
27 | * @throws IOException
28 | * 发送一个静态资源,例如一个 HTML 文件
29 | */
30 | public void sendStaticResource() throws IOException {
31 | byte[] bytes = new byte[BUFFER_SIZE];
32 | FileInputStream fis = null;
33 | try {
34 | File file = new File(HttpServer.WEB_ROOT, request.getUri());
35 | //将原始数据发送给浏览器
36 | if(file.exists()) {
37 | fis = new FileInputStream(file);
38 | int ch = fis.read(bytes, 0, BUFFER_SIZE);
39 | while(ch != -1) {
40 | output.write(bytes, 0, ch);
41 | ch = fis.read(bytes, 0, BUFFER_SIZE);
42 | }
43 | } else {
44 | //文件不存在
45 | String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
46 | "Content-Type: text/html\r\n" +
47 | "Content-Length: 23\r\n" +
48 | "\r\n" +
49 | "
File Not Found
";
50 | output.write(errorMessage.getBytes());
51 | }
52 |
53 | } catch (Exception e) {
54 | System.out.println(e.toString());
55 | } finally {
56 | if(fis != null)
57 | fis.close();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer1/Constants.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer1;
2 |
3 | import java.io.File;
4 |
5 | public class Constants {
6 |
7 | public static final String WEB_ROOT =
8 | System.getProperty("user.dir") + File.separator + "webroot";
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer1/HttpServer.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer1;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.OutputStream;
6 | import java.net.InetAddress;
7 | import java.net.ServerSocket;
8 | import java.net.Socket;
9 |
10 | /**
11 | * 类似于 ex01 里边的简单服务器应用程序的HttpServer 类。不过,在这个应用程序里边
12 | * HttpServer 类可以同时提供静态资源和 servlet
13 | * @author lc
14 | */
15 | public class HttpServer {
16 |
17 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
18 | private boolean shutdown = false;
19 |
20 | public static void main(String[] args) {
21 | HttpServer server = new HttpServer();
22 | server.await();
23 | }
24 |
25 | /**
26 | * 请求可以分发给一个 StaticResourceProcessor 或者一个 ServletProcessor。
27 | * 假如 URI 包括字符串/servlet/的话,请求将会转发到后面去。
28 | * 不然的话,请求将会传递给 StaticResourceProcessor 实例 instance
29 | */
30 | public void await() {
31 | ServerSocket serverSocket = null;
32 | int port = 8080;
33 | try {
34 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | System.exit(1);
38 | }
39 | //循环等待请求
40 | while(!shutdown) {
41 | Socket socket = null;
42 | InputStream input = null;
43 | OutputStream output = null;
44 | try {
45 | socket = serverSocket.accept();
46 | input = socket.getInputStream();
47 | output = socket.getOutputStream();
48 |
49 | //创建请求对象并解析
50 | Request request = new Request(input);
51 | request.parse();
52 |
53 | //创建一个响应对象
54 | Response response = new Response(output);
55 | response.setRequest(request);
56 |
57 | //检查是否是 servlet 请求或者是
58 | //静态资源
59 | //一个以"/servlet/" 开头的 servlet 请求
60 | if(request.getUri().startsWith("/servlet/")) {
61 | ServletProcessor processor = new ServletProcessor();
62 | processor.process(request, response);
63 | } else {
64 | StaticResourceProcessor processor = new StaticResourceProcessor();
65 | processor.process(request, response);
66 | }
67 |
68 | //关闭 socket
69 | socket.close();
70 | //检查 URI 是否是 shutdown 命令
71 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
72 | } catch (Exception e) {
73 | e.printStackTrace();
74 | System.exit(1);
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer1/Request.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer1;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.UnsupportedEncodingException;
7 | import java.util.Enumeration;
8 | import java.util.Locale;
9 | import java.util.Map;
10 |
11 | import javax.servlet.AsyncContext;
12 | import javax.servlet.DispatcherType;
13 | import javax.servlet.RequestDispatcher;
14 | import javax.servlet.ServletContext;
15 | import javax.servlet.ServletInputStream;
16 | import javax.servlet.ServletRequest;
17 | import javax.servlet.ServletResponse;
18 |
19 | /**
20 | * 代表一个 request 对象并被传递给 servlet 的 service 方法
21 | * 实现 javax.servlet.ServletRequest 接口,仅实现部分方法
22 | * @author lc
23 | */
24 | public class Request implements ServletRequest {
25 |
26 | private InputStream input;
27 | private String uri;
28 |
29 | public Request(InputStream input) {
30 | this.input = input;
31 | }
32 |
33 | public String getUri() {
34 | return uri;
35 | }
36 |
37 | public void parse() {
38 | //从 socket 读取字符
39 | StringBuffer request = new StringBuffer(2048);
40 | int i;
41 | byte[] buffer = new byte[2048];
42 | try {
43 | i = input.read(buffer);
44 | } catch (IOException e) {
45 | e.printStackTrace();
46 | i = -1;
47 | }
48 | for(int j = 0; j < i; j++) {
49 | request.append((char)buffer[j]);
50 | }
51 | System.out.println(request.toString());
52 | uri = parseUri(request.toString());
53 | }
54 |
55 | private String parseUri(String requestString) {
56 | int index1, index2;
57 | index1 = requestString.indexOf(" ");
58 | if(index1 != -1) {
59 | index2 = requestString.indexOf(" ", index1 + 1);
60 | if(index2 > index1) {
61 | return requestString.substring(index1 + 1, index2);
62 | }
63 | }
64 | return null;
65 | }
66 |
67 | @Override
68 | public AsyncContext getAsyncContext() {
69 | return null;
70 | }
71 |
72 | @Override
73 | public Object getAttribute(String arg0) {
74 | return null;
75 | }
76 |
77 | @Override
78 | public Enumeration getAttributeNames() {
79 | return null;
80 | }
81 |
82 | @Override
83 | public String getCharacterEncoding() {
84 | return null;
85 | }
86 |
87 | @Override
88 | public int getContentLength() {
89 | return 0;
90 | }
91 |
92 | @Override
93 | public long getContentLengthLong() {
94 | return 0;
95 | }
96 |
97 | @Override
98 | public String getContentType() {
99 | return null;
100 | }
101 |
102 | @Override
103 | public DispatcherType getDispatcherType() {
104 | return null;
105 | }
106 |
107 | @Override
108 | public ServletInputStream getInputStream() throws IOException {
109 | return null;
110 | }
111 |
112 | @Override
113 | public String getLocalAddr() {
114 | return null;
115 | }
116 |
117 | @Override
118 | public String getLocalName() {
119 | return null;
120 | }
121 |
122 | @Override
123 | public int getLocalPort() {
124 | return 0;
125 | }
126 |
127 | @Override
128 | public Locale getLocale() {
129 | return null;
130 | }
131 |
132 | @Override
133 | public Enumeration getLocales() {
134 | return null;
135 | }
136 |
137 | @Override
138 | public String getParameter(String arg0) {
139 | return null;
140 | }
141 |
142 | @Override
143 | public Map getParameterMap() {
144 | return null;
145 | }
146 |
147 | @Override
148 | public Enumeration getParameterNames() {
149 | return null;
150 | }
151 |
152 | @Override
153 | public String[] getParameterValues(String arg0) {
154 | return null;
155 | }
156 |
157 | @Override
158 | public String getProtocol() {
159 | return null;
160 | }
161 |
162 | @Override
163 | public BufferedReader getReader() throws IOException {
164 | return null;
165 | }
166 |
167 | @Override
168 | public String getRealPath(String arg0) {
169 | return null;
170 | }
171 |
172 | @Override
173 | public String getRemoteAddr() {
174 | return null;
175 | }
176 |
177 | @Override
178 | public String getRemoteHost() {
179 | return null;
180 | }
181 |
182 | @Override
183 | public int getRemotePort() {
184 | return 0;
185 | }
186 |
187 | @Override
188 | public RequestDispatcher getRequestDispatcher(String arg0) {
189 | return null;
190 | }
191 |
192 | @Override
193 | public String getScheme() {
194 | return null;
195 | }
196 |
197 | @Override
198 | public String getServerName() {
199 | return null;
200 | }
201 |
202 | @Override
203 | public int getServerPort() {
204 | return 0;
205 | }
206 |
207 | @Override
208 | public ServletContext getServletContext() {
209 | return null;
210 | }
211 |
212 | @Override
213 | public boolean isAsyncStarted() {
214 | return false;
215 | }
216 |
217 | @Override
218 | public boolean isAsyncSupported() {
219 | return false;
220 | }
221 |
222 | @Override
223 | public boolean isSecure() {
224 | return false;
225 | }
226 |
227 | @Override
228 | public void removeAttribute(String arg0) {
229 |
230 | }
231 |
232 | @Override
233 | public void setAttribute(String arg0, Object arg1) {
234 |
235 | }
236 |
237 | @Override
238 | public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException {
239 |
240 | }
241 |
242 | @Override
243 | public AsyncContext startAsync() throws IllegalStateException {
244 | return null;
245 | }
246 |
247 | @Override
248 | public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1) throws IllegalStateException {
249 | return null;
250 | }
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer1/Response.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer1;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileNotFoundException;
6 | import java.io.IOException;
7 | import java.io.OutputStream;
8 | import java.io.PrintWriter;
9 | import java.util.Locale;
10 |
11 | import javax.servlet.ServletOutputStream;
12 | import javax.servlet.ServletResponse;
13 |
14 | /**
15 | * 实现了 javax.servlet.ServletResponse
16 | * @author lc
17 | */
18 | public class Response implements ServletResponse {
19 |
20 | private static final int BUFFER_SIZE = 1024;
21 | Request request;
22 | OutputStream output;
23 | PrintWriter writer;
24 |
25 | public Response(OutputStream output) {
26 | this.output = output;
27 | }
28 |
29 | public void setRequest(Request request) {
30 | this.request = request;
31 | }
32 |
33 | //这个方法用于服务静态页面
34 | public void sendStaticResource() throws IOException {
35 | byte[] bytes = new byte[BUFFER_SIZE];
36 | FileInputStream fis = null;
37 | try {
38 | File file = new File(Constants.WEB_ROOT, request.getUri());
39 | fis = new FileInputStream(file);
40 |
41 | /*Http Response 数据结构
42 | * 协议 状态码 描述 例如:(HTTP 200 OK)
43 | * *(( general-header | response-header | entity-header ) CRLF)
44 | * CRLF
45 | * [ message-body ]
46 | * Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
47 | */
48 | int ch = fis.read(bytes, 0, BUFFER_SIZE);
49 | while(ch != -1) {
50 | output.write(bytes, 0, ch);
51 | ch = fis.read(bytes, 0, BUFFER_SIZE);
52 | }
53 | } catch (FileNotFoundException e) {
54 | String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
55 | "Content-Type: text/html\r\n" +
56 | "Content-Length: 23\r\n" +
57 | "\r\n" +
58 | "File Not Found
";
59 | output.write(errorMessage.getBytes());
60 | } finally {
61 | if(fis != null) {
62 | fis.close();
63 | }
64 | }
65 | }
66 |
67 | @Override
68 | public PrintWriter getWriter() throws IOException {
69 | //autoflush 为 true, println() 会刷新,但 print() 不会
70 | //因此,任何 print 方法的调用都会发生在 servlet 的 service 方法的最后一行,输出将不会
71 | //被发送到浏览器。这个缺点将会在下一个应用程序中修复
72 | writer = new PrintWriter(output, true);
73 | return writer;
74 | }
75 |
76 | @Override
77 | public void flushBuffer() throws IOException {
78 |
79 | }
80 |
81 | @Override
82 | public int getBufferSize() {
83 | return 0;
84 | }
85 |
86 | @Override
87 | public String getCharacterEncoding() {
88 | return null;
89 | }
90 |
91 | @Override
92 | public String getContentType() {
93 | return null;
94 | }
95 |
96 | @Override
97 | public Locale getLocale() {
98 | return null;
99 | }
100 |
101 | @Override
102 | public ServletOutputStream getOutputStream() throws IOException {
103 | return null;
104 | }
105 |
106 | @Override
107 | public boolean isCommitted() {
108 | return false;
109 | }
110 |
111 | @Override
112 | public void reset() {
113 |
114 | }
115 |
116 | @Override
117 | public void resetBuffer() {
118 |
119 | }
120 |
121 | @Override
122 | public void setBufferSize(int arg0) {
123 |
124 | }
125 |
126 | @Override
127 | public void setCharacterEncoding(String arg0) {
128 |
129 | }
130 |
131 | @Override
132 | public void setContentLength(int arg0) {
133 |
134 | }
135 |
136 | @Override
137 | public void setContentLengthLong(long arg0) {
138 |
139 | }
140 |
141 | @Override
142 | public void setContentType(String arg0) {
143 |
144 | }
145 |
146 | @Override
147 | public void setLocale(Locale arg0) {
148 |
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer1/ServletProcessor.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer1;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URL;
6 | import java.net.URLClassLoader;
7 | import java.net.URLStreamHandler;
8 |
9 | import javax.servlet.Servlet;
10 |
11 | public class ServletProcessor {
12 |
13 | /**
14 | * process 方法加载 servlet
15 | * @param request
16 | * @param response
17 | */
18 | public void process(Request request, Response response) {
19 |
20 | String uri = request.getUri();
21 | // URI 是以下形式的:/servlet/servletName 其中,servletName 是 servlet 类的名字
22 | String servletName = uri.substring(uri.lastIndexOf("/") + 1);
23 | URLClassLoader loader = null;
24 |
25 | try {
26 | //创建 URLClassLoader 并告诉这个类加载器要加载的类的位置
27 | //对于这个 servlet 容器,类加载器直接在 Constants 指向的目录里边查找
28 | URL[] urls = new URL[1];
29 | URLStreamHandler streamHandler = null;
30 | File classPath = new File(Constants.WEB_ROOT);
31 |
32 | String repository = (new URL("file", null, classPath.getCanonicalPath() +
33 | File.separator)).toString();
34 | urls[0] = new URL(null, repository, streamHandler);
35 |
36 | loader = new URLClassLoader(urls);
37 | } catch (IOException e) {
38 | System.out.println(e.toString());
39 | }
40 |
41 | Class myClass = null;
42 | try {
43 | myClass = loader.loadClass(servletName);
44 | } catch(ClassNotFoundException e) {
45 | e.printStackTrace();
46 | }
47 |
48 | //创建一个 servlet 类加载器的实例, 把它向下转型为 javax.servlet.Servlet,
49 | //并调用 servlet 的 service 方法
50 | Servlet servlet = null;
51 | try {
52 | servlet = (Servlet) myClass.newInstance();
53 | servlet.service(request, response);
54 | } catch (Exception e) {
55 | System.out.println(e.toString());
56 | } catch (Throwable e) {
57 | System.out.println(e.toString());
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer1/StaticResourceProcessor.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer1;
2 |
3 | import java.io.IOException;
4 |
5 | /**
6 | * 提供静态资源请求
7 | * @author lc
8 | */
9 | public class StaticResourceProcessor {
10 |
11 | public void process(Request request, Response response) {
12 | try {
13 | response.sendStaticResource();
14 | } catch (IOException e) {
15 | e.printStackTrace();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer1/servletContainer.md:
--------------------------------------------------------------------------------
1 | 一个全功能的 servlet 容器会为 servlet 的每个 HTTP 请求做下面一些工作:
2 | - 当第一次调用 servlet 的时候,加载该 servlet 类并调用 servlet 的 init 方法(仅仅一次)。
3 | - 对每次请求,构造一个` javax.servlet.ServletRequest `实例和一个
4 | `javax.servlet.ServletResponse `实例。
5 | 调用 servlet 的 service 方法,同时传递 `ServletRequest `和 `ServletResponse `对象。
6 | - 当 servlet 类被关闭的时候,调用 servlet 的 destroy 方法并卸载 servlet 类。
7 | 本章的第一个 servlet 容器不是全功能的。因此,她不能运行什么除了非常简单的 servlet,
8 | 而且也不调用 servlet 的 init 方法和 destroy 方法。相反它做了下面的事情:
9 | - 等待 HTTP 请求。 构造一个` ServletRequest `对象和一个 `ServletResponse `对象。
10 | - 假如该请求需要一个静态资源的话,调用 `StaticResourceProcessor `实例的 process 方
11 | 法,同时传递` ServletRequest `和 `ServletResponse `对象。
12 | - 假如该请求需要一个 servlet 的话,加载 servlet 类并调用 servlet 的 service 方法,
13 | 同时传递` ServletRequest `和 `ServletResponse` 对象。
14 |
15 | 第一个 servlet 容器不是全功能的。因此,她不能运行什么除了非常简单的 servlet,
16 | 而且也不调用 servlet 的` init `方法和 destroy 方法。相反它做了下面的事情:
17 | - 等待 HTTP 请求。 构造一个` ServletRequest `对象和一个 `ServletResponse `对象。
18 | - 假如该请求需要一个静态资源的话,调用 `StaticResourceProcessor` 实例的 process 方
19 | 法,同时传递` ServletRequest `和 `ServletResponse `对象。
20 | - 假如该请求需要一个 servlet 的话,加载 servlet 类并调用 servlet 的 service 方法,
21 | 同时传递` ServletRequest` 和 `ServletResponse `对象。
22 | 注意:在这个 servlet 容器中,每一次 servlet 被请求的时候, servlet 类都会被加载。
23 |
24 | http://machineName:port/staticResource 可以请求一个静态资源。
25 | 为了请求一个 servlet,你可以使用下面的 URL:
26 | http://machineName:port/servlet/servletClass
27 | 因此,假如你在本地请求一个名为 `PrimitiveServlet `的 servlet,你在浏览器的地址栏或
28 | 者网址框中敲入:
29 | http://localhost:8080/servlet/PrimitiveServlet
30 | servlet 容器可以就提供 PrimitiveServlet 了。不过,假如你调用其他 servlet,如
31 | ModernServlet, servlet 容器将会抛出一个异常。
32 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/Constants.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.File;
4 |
5 | public class Constants {
6 |
7 | public static final String WEB_ROOT =
8 | System.getProperty("user.dir") + File.separator + "webroot";
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/HttpServer2.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.OutputStream;
6 | import java.net.InetAddress;
7 | import java.net.ServerSocket;
8 | import java.net.Socket;
9 |
10 | /**
11 | * 类似于 ex01 里边的简单服务器应用程序的HttpServer 类。不过,在这个应用程序里边
12 | * HttpServer 类可以同时提供静态资源和 servlet
13 | * @author lc
14 | */
15 | public class HttpServer2 {
16 |
17 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
18 | private boolean shutdown = false;
19 |
20 | public static void main(String[] args) {
21 | HttpServer2 server = new HttpServer2();
22 | server.await();
23 | }
24 |
25 | /**
26 | * 请求可以分发给一个 StaticResourceProcessor 或者一个 ServletProcessor。
27 | * 假如 URI 包括字符串/servlet/的话,请求将会转发到后面去。
28 | * 不然的话,请求将会传递给 StaticResourceProcessor 实例 instance
29 | */
30 | public void await() {
31 | ServerSocket serverSocket = null;
32 | int port = 8080;
33 | try {
34 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | System.exit(1);
38 | }
39 | //循环等待请求
40 | while(!shutdown) {
41 | Socket socket = null;
42 | InputStream input = null;
43 | OutputStream output = null;
44 | try {
45 | socket = serverSocket.accept();
46 | input = socket.getInputStream();
47 | output = socket.getOutputStream();
48 |
49 | //创建请求对象并解析
50 | Request request = new Request(input);
51 | request.parse();
52 |
53 | //创建一个响应对象
54 | Response response = new Response(output);
55 | response.setRequest(request);
56 |
57 | //检查是否是 servlet 请求或者是
58 | //静态资源
59 | //一个以"/servlet/" 开头的 servlet 请求
60 | if(request.getUri().startsWith("/servlet/")) {
61 | ServletProcessor2 processor = new ServletProcessor2();
62 | processor.process(request, response);
63 | } else {
64 | StaticResourceProcessor processor = new StaticResourceProcessor();
65 | processor.process(request, response);
66 | }
67 |
68 | //关闭 socket
69 | socket.close();
70 | //检查 URI 是否是 shutdown 命令
71 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
72 | } catch (Exception e) {
73 | e.printStackTrace();
74 | System.exit(1);
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/Request.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.UnsupportedEncodingException;
7 | import java.util.Enumeration;
8 | import java.util.Locale;
9 | import java.util.Map;
10 |
11 | import javax.servlet.AsyncContext;
12 | import javax.servlet.DispatcherType;
13 | import javax.servlet.RequestDispatcher;
14 | import javax.servlet.ServletContext;
15 | import javax.servlet.ServletInputStream;
16 | import javax.servlet.ServletRequest;
17 | import javax.servlet.ServletResponse;
18 |
19 | /**
20 | * 代表一个 request 对象并被传递给 servlet 的 service 方法
21 | * 实现 javax.servlet.ServletRequest 接口,仅实现部分方法
22 | * @author lc
23 | */
24 | public class Request implements ServletRequest {
25 |
26 | private InputStream input;
27 | private String uri;
28 |
29 | public Request(InputStream input) {
30 | this.input = input;
31 | }
32 |
33 | public String getUri() {
34 | return uri;
35 | }
36 |
37 | public void parse() {
38 | //从 socket 读取字符
39 | StringBuffer request = new StringBuffer(2048);
40 | int i;
41 | byte[] buffer = new byte[2048];
42 | try {
43 | i = input.read(buffer);
44 | } catch (IOException e) {
45 | e.printStackTrace();
46 | i = -1;
47 | }
48 | for(int j = 0; j < i; j++) {
49 | request.append((char)buffer[j]);
50 | }
51 | System.out.println(request.toString());
52 | uri = parseUri(request.toString());
53 | }
54 |
55 | private String parseUri(String requestString) {
56 | int index1, index2;
57 | index1 = requestString.indexOf(" ");
58 | if(index1 != -1) {
59 | index2 = requestString.indexOf(" ", index1 + 1);
60 | if(index2 > index1) {
61 | return requestString.substring(index1 + 1, index2);
62 | }
63 | }
64 | return null;
65 | }
66 |
67 | @Override
68 | public AsyncContext getAsyncContext() {
69 | return null;
70 | }
71 |
72 | @Override
73 | public Object getAttribute(String arg0) {
74 | return null;
75 | }
76 |
77 | @Override
78 | public Enumeration getAttributeNames() {
79 | return null;
80 | }
81 |
82 | @Override
83 | public String getCharacterEncoding() {
84 | return null;
85 | }
86 |
87 | @Override
88 | public int getContentLength() {
89 | return 0;
90 | }
91 |
92 | @Override
93 | public long getContentLengthLong() {
94 | return 0;
95 | }
96 |
97 | @Override
98 | public String getContentType() {
99 | return null;
100 | }
101 |
102 | @Override
103 | public DispatcherType getDispatcherType() {
104 | return null;
105 | }
106 |
107 | @Override
108 | public ServletInputStream getInputStream() throws IOException {
109 | return null;
110 | }
111 |
112 | @Override
113 | public String getLocalAddr() {
114 | return null;
115 | }
116 |
117 | @Override
118 | public String getLocalName() {
119 | return null;
120 | }
121 |
122 | @Override
123 | public int getLocalPort() {
124 | return 0;
125 | }
126 |
127 | @Override
128 | public Locale getLocale() {
129 | return null;
130 | }
131 |
132 | @Override
133 | public Enumeration getLocales() {
134 | return null;
135 | }
136 |
137 | @Override
138 | public String getParameter(String arg0) {
139 | return null;
140 | }
141 |
142 | @Override
143 | public Map getParameterMap() {
144 | return null;
145 | }
146 |
147 | @Override
148 | public Enumeration getParameterNames() {
149 | return null;
150 | }
151 |
152 | @Override
153 | public String[] getParameterValues(String arg0) {
154 | return null;
155 | }
156 |
157 | @Override
158 | public String getProtocol() {
159 | return null;
160 | }
161 |
162 | @Override
163 | public BufferedReader getReader() throws IOException {
164 | return null;
165 | }
166 |
167 | @Override
168 | public String getRealPath(String arg0) {
169 | return null;
170 | }
171 |
172 | @Override
173 | public String getRemoteAddr() {
174 | return null;
175 | }
176 |
177 | @Override
178 | public String getRemoteHost() {
179 | return null;
180 | }
181 |
182 | @Override
183 | public int getRemotePort() {
184 | return 0;
185 | }
186 |
187 | @Override
188 | public RequestDispatcher getRequestDispatcher(String arg0) {
189 | return null;
190 | }
191 |
192 | @Override
193 | public String getScheme() {
194 | return null;
195 | }
196 |
197 | @Override
198 | public String getServerName() {
199 | return null;
200 | }
201 |
202 | @Override
203 | public int getServerPort() {
204 | return 0;
205 | }
206 |
207 | @Override
208 | public ServletContext getServletContext() {
209 | return null;
210 | }
211 |
212 | @Override
213 | public boolean isAsyncStarted() {
214 | return false;
215 | }
216 |
217 | @Override
218 | public boolean isAsyncSupported() {
219 | return false;
220 | }
221 |
222 | @Override
223 | public boolean isSecure() {
224 | return false;
225 | }
226 |
227 | @Override
228 | public void removeAttribute(String arg0) {
229 |
230 | }
231 |
232 | @Override
233 | public void setAttribute(String arg0, Object arg1) {
234 |
235 | }
236 |
237 | @Override
238 | public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException {
239 |
240 | }
241 |
242 | @Override
243 | public AsyncContext startAsync() throws IllegalStateException {
244 | return null;
245 | }
246 |
247 | @Override
248 | public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1) throws IllegalStateException {
249 | return null;
250 | }
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/RequestFacade.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.UnsupportedEncodingException;
6 | import java.util.Enumeration;
7 | import java.util.Locale;
8 | import java.util.Map;
9 |
10 | import javax.servlet.RequestDispatcher;
11 | import javax.servlet.ServletContext;
12 | import javax.servlet.ServletInputStream;
13 | import javax.servlet.ServletRequest;
14 | import javax.servlet.ServletResponse;
15 |
16 |
17 | public class RequestFacade implements ServletRequest {
18 |
19 | private ServletRequest request = null;
20 |
21 | public RequestFacade(Request request) {
22 | this.request = request;
23 | }
24 |
25 | @Override
26 | public Object getAttribute(String attribute) {
27 | return request.getAttribute(attribute);
28 | }
29 |
30 | @Override
31 | public Enumeration getAttributeNames() {
32 | return request.getAttributeNames();
33 | }
34 |
35 | @Override
36 | public String getCharacterEncoding() {
37 | return null;
38 | }
39 |
40 | @Override
41 | public int getContentLength() {
42 | return 0;
43 | }
44 |
45 | public long getContentLengthLong() {
46 | return 0;
47 | }
48 |
49 | @Override
50 | public String getContentType() {
51 | return null;
52 | }
53 |
54 |
55 | @Override
56 | public ServletInputStream getInputStream() throws IOException {
57 | // TODO Auto-generated method stub
58 | return null;
59 | }
60 |
61 | @Override
62 | public Locale getLocale() {
63 | // TODO Auto-generated method stub
64 | return null;
65 | }
66 |
67 | @Override
68 | public Enumeration getLocales() {
69 | // TODO Auto-generated method stub
70 | return null;
71 | }
72 |
73 | @Override
74 | public String getParameter(String arg0) {
75 | // TODO Auto-generated method stub
76 | return null;
77 | }
78 |
79 | @Override
80 | public Map getParameterMap() {
81 | // TODO Auto-generated method stub
82 | return null;
83 | }
84 |
85 | @Override
86 | public Enumeration getParameterNames() {
87 | // TODO Auto-generated method stub
88 | return null;
89 | }
90 |
91 | @Override
92 | public String[] getParameterValues(String arg0) {
93 | // TODO Auto-generated method stub
94 | return null;
95 | }
96 |
97 | @Override
98 | public String getProtocol() {
99 | // TODO Auto-generated method stub
100 | return null;
101 | }
102 |
103 | @Override
104 | public BufferedReader getReader() throws IOException {
105 | // TODO Auto-generated method stub
106 | return null;
107 | }
108 |
109 | @Override
110 | public String getRealPath(String arg0) {
111 | // TODO Auto-generated method stub
112 | return null;
113 | }
114 |
115 | @Override
116 | public String getRemoteAddr() {
117 | // TODO Auto-generated method stub
118 | return null;
119 | }
120 |
121 | @Override
122 | public String getRemoteHost() {
123 | // TODO Auto-generated method stub
124 | return null;
125 | }
126 |
127 | @Override
128 | public RequestDispatcher getRequestDispatcher(String arg0) {
129 | // TODO Auto-generated method stub
130 | return null;
131 | }
132 |
133 | @Override
134 | public String getScheme() {
135 | // TODO Auto-generated method stub
136 | return null;
137 | }
138 |
139 | @Override
140 | public String getServerName() {
141 | // TODO Auto-generated method stub
142 | return null;
143 | }
144 |
145 | @Override
146 | public int getServerPort() {
147 | // TODO Auto-generated method stub
148 | return 0;
149 | }
150 |
151 | @Override
152 | public boolean isSecure() {
153 | // TODO Auto-generated method stub
154 | return false;
155 | }
156 |
157 | @Override
158 | public void removeAttribute(String arg0) {
159 | // TODO Auto-generated method stub
160 |
161 | }
162 |
163 | @Override
164 | public void setAttribute(String arg0, Object arg1) {
165 | // TODO Auto-generated method stub
166 |
167 | }
168 |
169 | @Override
170 | public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException {
171 | // TODO Auto-generated method stub
172 |
173 | }
174 |
175 | }
176 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/Response.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileNotFoundException;
6 | import java.io.IOException;
7 | import java.io.OutputStream;
8 | import java.io.PrintWriter;
9 | import java.util.Locale;
10 |
11 | import javax.servlet.ServletOutputStream;
12 | import javax.servlet.ServletResponse;
13 |
14 | /**
15 | * 实现了 javax.servlet.ServletResponse
16 | * @author lc
17 | */
18 | public class Response implements ServletResponse {
19 |
20 | private static final int BUFFER_SIZE = 1024;
21 | Request request;
22 | OutputStream output;
23 | PrintWriter writer;
24 |
25 | public Response(OutputStream output) {
26 | this.output = output;
27 | }
28 |
29 | public void setRequest(Request request) {
30 | this.request = request;
31 | }
32 |
33 | //这个方法用于服务静态页面
34 | public void sendStaticResource() throws IOException {
35 | byte[] bytes = new byte[BUFFER_SIZE];
36 | FileInputStream fis = null;
37 | try {
38 | File file = new File(Constants.WEB_ROOT, request.getUri());
39 | fis = new FileInputStream(file);
40 |
41 | /*Http Response 数据结构
42 | * 协议 状态码 描述 例如:(HTTP 200 OK)
43 | * *(( general-header | response-header | entity-header ) CRLF)
44 | * CRLF
45 | * [ message-body ]
46 | * Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
47 | */
48 | int ch = fis.read(bytes, 0, BUFFER_SIZE);
49 | while(ch != -1) {
50 | output.write(bytes, 0, ch);
51 | ch = fis.read(bytes, 0, BUFFER_SIZE);
52 | }
53 | } catch (FileNotFoundException e) {
54 | String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
55 | "Content-Type: text/html\r\n" +
56 | "Content-Length: 23\r\n" +
57 | "\r\n" +
58 | "File Not Found
";
59 | output.write(errorMessage.getBytes());
60 | } finally {
61 | if(fis != null) {
62 | fis.close();
63 | }
64 | }
65 | }
66 |
67 | @Override
68 | public PrintWriter getWriter() throws IOException {
69 | //autoflush 为 true, println() 会刷新,但 print() 不会
70 | //因此,任何 print 方法的调用都会发生在 servlet 的 service 方法的最后一行,输出将不会
71 | writer = new PrintWriter(output, true);
72 | return writer;
73 | }
74 |
75 | @Override
76 | public void flushBuffer() throws IOException {
77 |
78 | }
79 |
80 | @Override
81 | public int getBufferSize() {
82 | return 0;
83 | }
84 |
85 | @Override
86 | public String getCharacterEncoding() {
87 | return null;
88 | }
89 |
90 | @Override
91 | public String getContentType() {
92 | return null;
93 | }
94 |
95 | @Override
96 | public Locale getLocale() {
97 | return null;
98 | }
99 |
100 | @Override
101 | public ServletOutputStream getOutputStream() throws IOException {
102 | return null;
103 | }
104 |
105 | @Override
106 | public boolean isCommitted() {
107 | return false;
108 | }
109 |
110 | @Override
111 | public void reset() {
112 |
113 | }
114 |
115 | @Override
116 | public void resetBuffer() {
117 |
118 | }
119 |
120 | @Override
121 | public void setBufferSize(int arg0) {
122 |
123 | }
124 |
125 | @Override
126 | public void setCharacterEncoding(String arg0) {
127 |
128 | }
129 |
130 | @Override
131 | public void setContentLength(int arg0) {
132 |
133 | }
134 |
135 | @Override
136 | public void setContentLengthLong(long arg0) {
137 |
138 | }
139 |
140 | @Override
141 | public void setContentType(String arg0) {
142 |
143 | }
144 |
145 | @Override
146 | public void setLocale(Locale arg0) {
147 |
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/ResponseFacade.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.IOException;
4 | import java.io.PrintWriter;
5 | import java.util.Locale;
6 |
7 | import javax.servlet.ServletOutputStream;
8 | import javax.servlet.ServletResponse;
9 |
10 | public class ResponseFacade implements ServletResponse {
11 |
12 | private Response response;
13 |
14 | public ResponseFacade(Response response) {
15 | this.response = response;
16 | }
17 |
18 | @Override
19 | public void flushBuffer() throws IOException {
20 | response.flushBuffer();
21 | }
22 |
23 | @Override
24 | public int getBufferSize() {
25 | return response.getBufferSize();
26 | }
27 |
28 | @Override
29 | public String getCharacterEncoding() {
30 | return response.getCharacterEncoding();
31 | }
32 |
33 | @Override
34 | public String getContentType() {
35 | return response.getContentType();
36 | }
37 |
38 | @Override
39 | public Locale getLocale() {
40 | return response.getLocale();
41 | }
42 |
43 | @Override
44 | public ServletOutputStream getOutputStream() throws IOException {
45 | return response.getOutputStream();
46 | }
47 |
48 | @Override
49 | public PrintWriter getWriter() throws IOException {
50 | return response.getWriter();
51 | }
52 |
53 | @Override
54 | public boolean isCommitted() {
55 | return response.isCommitted();
56 | }
57 |
58 | @Override
59 | public void reset() {
60 | response.reset();
61 | }
62 |
63 | @Override
64 | public void resetBuffer() {
65 | // TODO Auto-generated method stub
66 |
67 | }
68 |
69 | @Override
70 | public void setBufferSize(int arg0) {
71 | // TODO Auto-generated method stub
72 |
73 | }
74 |
75 | @Override
76 | public void setCharacterEncoding(String arg0) {
77 | // TODO Auto-generated method stub
78 |
79 | }
80 |
81 | @Override
82 | public void setContentLength(int arg0) {
83 | // TODO Auto-generated method stub
84 |
85 | }
86 |
87 | @Override
88 | public void setContentLengthLong(long arg0) {
89 | // TODO Auto-generated method stub
90 |
91 | }
92 |
93 | @Override
94 | public void setContentType(String arg0) {
95 | // TODO Auto-generated method stub
96 |
97 | }
98 |
99 | @Override
100 | public void setLocale(Locale arg0) {
101 | // TODO Auto-generated method stub
102 |
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/ServletProcessor2.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URL;
6 | import java.net.URLClassLoader;
7 | import java.net.URLStreamHandler;
8 | import javax.servlet.Servlet;
9 |
10 | public class ServletProcessor2 {
11 |
12 | /**
13 | * process 方法加载 servlet
14 | * @param request
15 | * @param response
16 | */
17 | public void process(Request request, Response response) {
18 |
19 | String uri = request.getUri();
20 | // URI 是以下形式的:/servlet/servletName 其中,servletName 是 servlet 类的名字
21 | String servletName = uri.substring(uri.lastIndexOf("/") + 1);
22 | URLClassLoader loader = null;
23 |
24 | try {
25 | //创建 URLClassLoader 并告诉这个类加载器要加载的类的位置
26 | //对于这个 servlet 容器,类加载器直接在 Constants 指向的目录里边查找
27 | URL[] urls = new URL[1];
28 | URLStreamHandler streamHandler = null;
29 | File classPath = new File(Constants.WEB_ROOT);
30 |
31 | String repository = (new URL("file", null, classPath.getCanonicalPath() +
32 | File.separator)).toString();
33 | urls[0] = new URL(null, repository, streamHandler);
34 |
35 | loader = new URLClassLoader(urls);
36 | } catch (IOException e) {
37 | e.printStackTrace();
38 | }
39 |
40 | Class myClass = null;
41 | try {
42 | myClass = loader.loadClass(servletName);
43 | } catch(ClassNotFoundException e) {
44 | e.printStackTrace();
45 | }
46 |
47 | //创建一个 servlet 类加载器的实例, 把它向下转型为 javax.servlet.Servlet,
48 | //并调用 servlet 的 service 方法
49 | Servlet servlet = null;
50 | RequestFacade requestFacade = new RequestFacade(request);
51 | ResponseFacade responseFacade = new ResponseFacade(response);
52 |
53 | try {
54 | servlet = (Servlet) myClass.newInstance();
55 | servlet.service(requestFacade, responseFacade);
56 | } catch (Exception e) {
57 | e.printStackTrace();
58 | } catch (Throwable e) {
59 | e.printStackTrace();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/ex02/ServletContainer2/StaticResourceProcessor.java:
--------------------------------------------------------------------------------
1 | package ex02.ServletContainer2;
2 |
3 | import java.io.IOException;
4 |
5 | /**
6 | * 提供静态资源请求
7 | * @author lc
8 | */
9 | public class StaticResourceProcessor {
10 |
11 | public void process(Request request, Response response) {
12 | try {
13 | response.sendStaticResource();
14 | } catch (IOException e) {
15 | e.printStackTrace();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/ex03/connector/RequestStream.java:
--------------------------------------------------------------------------------
1 | package ex03.connector;
2 |
3 | import ex03.connector.http.Constants;
4 | import ex03.connector.http.HttpRequest;
5 | import java.io.InputStream;
6 | import java.io.IOException;
7 |
8 | import javax.servlet.ReadListener;
9 | import javax.servlet.ServletInputStream;
10 |
11 | /**
12 | * Convenience implementation of ServletInputStream that works with
13 | * the standard implementations of Request. If the content length has
14 | * been set on our associated Request, this implementation will enforce
15 | * not reading more than that many bytes on the underlying stream.
16 | *
17 | * @author Craig R. McClanahan
18 | * @version $Revision: 1.6 $ $Date: 2002/03/18 07:15:39 $
19 | * @deprecated
20 | */
21 |
22 | public class RequestStream
23 | extends ServletInputStream {
24 |
25 |
26 | // ----------------------------------------------------------- Constructors
27 |
28 |
29 | /**
30 | * Construct a servlet input stream associated with the specified Request.
31 | *
32 | * @param request The associated request
33 | * @throws IOException
34 | */
35 | public RequestStream(HttpRequest request) throws IOException {
36 |
37 | super();
38 | closed = false;
39 | count = 0;
40 | length = request.getContentLength();
41 | stream = request.getInputStream();
42 |
43 | }
44 |
45 |
46 | // ----------------------------------------------------- Instance Variables
47 |
48 |
49 | /**
50 | * Has this stream been closed?
51 | */
52 | protected boolean closed = false;
53 |
54 |
55 | /**
56 | * The number of bytes which have already been returned by this stream.
57 | */
58 | protected int count = 0;
59 |
60 |
61 | /**
62 | * The content length past which we will not read, or -1 if there is
63 | * no defined content length.
64 | */
65 | protected int length = -1;
66 |
67 |
68 | /**
69 | * The localized strings for this package.
70 | */
71 | protected static StringManager sm =
72 | StringManager.getManager(Constants.Package);
73 |
74 |
75 | /**
76 | * The underlying input stream from which we should read data.
77 | */
78 | protected InputStream stream = null;
79 |
80 |
81 | // --------------------------------------------------------- Public Methods
82 |
83 |
84 | /**
85 | * Close this input stream. No physical level I-O is performed, but
86 | * any further attempt to read from this stream will throw an IOException.
87 | * If a content length has been set but not all of the bytes have yet been
88 | * consumed, the remaining bytes will be swallowed.
89 | */
90 | public void close() throws IOException {
91 |
92 | if (closed)
93 | throw new IOException(sm.getString("requestStream.close.closed"));
94 |
95 | if (length > 0) {
96 | while (count < length) {
97 | int b = read();
98 | if (b < 0)
99 | break;
100 | }
101 | }
102 |
103 | closed = true;
104 |
105 | }
106 |
107 |
108 |
109 | /**
110 | * Read and return a single byte from this input stream, or -1 if end of
111 | * file has been encountered.
112 | *
113 | * @exception IOException if an input/output error occurs
114 | */
115 | public int read() throws IOException {
116 |
117 | // Has this stream been closed?
118 | if (closed)
119 | throw new IOException(sm.getString("requestStream.read.closed"));
120 |
121 | // Have we read the specified content length already?
122 | if ((length >= 0) && (count >= length))
123 | return (-1); // End of file indicator
124 |
125 | // Read and count the next byte, then return it
126 | int b = stream.read();
127 | if (b >= 0)
128 | count++;
129 | return (b);
130 |
131 | }
132 |
133 |
134 | /**
135 | * Read some number of bytes from the input stream, and store them
136 | * into the buffer array b. The number of bytes actually read is
137 | * returned as an integer. This method blocks until input data is
138 | * available, end of file is detected, or an exception is thrown.
139 | *
140 | * @param b The buffer into which the data is read
141 | *
142 | * @exception IOException if an input/output error occurs
143 | */
144 | public int read(byte b[]) throws IOException {
145 |
146 | return (read(b, 0, b.length));
147 |
148 | }
149 |
150 |
151 | /**
152 | * Read up to len
bytes of data from the input stream
153 | * into an array of bytes. An attempt is made to read as many as
154 | * len
bytes, but a smaller number may be read,
155 | * possibly zero. The number of bytes actually read is returned as
156 | * an integer. This method blocks until input data is available,
157 | * end of file is detected, or an exception is thrown.
158 | *
159 | * @param b The buffer into which the data is read
160 | * @param off The start offset into array b
at which
161 | * the data is written
162 | * @param len The maximum number of bytes to read
163 | *
164 | * @exception IOException if an input/output error occurs
165 | */
166 | public int read(byte b[], int off, int len) throws IOException {
167 |
168 | int toRead = len;
169 | if (length > 0) {
170 | if (count >= length)
171 | return (-1);
172 | if ((count + len) > length)
173 | toRead = length - count;
174 | }
175 | int actuallyRead = super.read(b, off, toRead);
176 | return (actuallyRead);
177 |
178 | }
179 |
180 |
181 |
182 | @Override
183 | public boolean isFinished() {
184 | // TODO Auto-generated method stub
185 | return false;
186 | }
187 |
188 |
189 |
190 | @Override
191 | public boolean isReady() {
192 | // TODO Auto-generated method stub
193 | return false;
194 | }
195 |
196 |
197 |
198 | @Override
199 | public void setReadListener(ReadListener arg0) {
200 | // TODO Auto-generated method stub
201 |
202 | }
203 |
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/src/ex03/connector/ResponseStream.java:
--------------------------------------------------------------------------------
1 | package ex03.connector;
2 |
3 | import ex03.connector.http.HttpResponse;
4 | import java.io.IOException;
5 | import java.io.OutputStream;
6 | import javax.servlet.ServletOutputStream;
7 | import javax.servlet.WriteListener;
8 |
9 | /**
10 | * Convenience implementation of ServletOutputStream that works with
11 | * the standard ResponseBase implementation of Response. If the content
12 | * length has been set on our associated Response, this implementation will
13 | * enforce not writing more than that many bytes on the underlying stream.
14 | *
15 | * @author Craig R. McClanahan
16 | * @version $Revision: 1.6 $ $Date: 2002/03/18 07:15:39 $
17 | * @deprecated
18 | */
19 |
20 | public class ResponseStream extends ServletOutputStream {
21 |
22 |
23 | // ----------------------------------------------------------- Constructors
24 |
25 |
26 | /**
27 | * Construct a servlet output stream associated with the specified Request.
28 | *
29 | * @param response The associated response
30 | */
31 | public ResponseStream(HttpResponse response) {
32 |
33 | super();
34 | closed = false;
35 | commit = false;
36 | count = 0;
37 | this.response = response;
38 | // this.stream = response.getStream();
39 |
40 | }
41 |
42 |
43 | // ----------------------------------------------------- Instance Variables
44 |
45 |
46 | /**
47 | * Has this stream been closed?
48 | */
49 | protected boolean closed = false;
50 |
51 |
52 | /**
53 | * Should we commit the response when we are flushed?
54 | */
55 | protected boolean commit = false;
56 |
57 |
58 | /**
59 | * The number of bytes which have already been written to this stream.
60 | */
61 | protected int count = 0;
62 |
63 |
64 | /**
65 | * The content length past which we will not write, or -1 if there is
66 | * no defined content length.
67 | */
68 | protected int length = -1;
69 |
70 |
71 | /**
72 | * The Response with which this input stream is associated.
73 | */
74 | protected HttpResponse response = null;
75 |
76 |
77 | /**
78 | * The underlying output stream to which we should write data.
79 | */
80 | protected OutputStream stream = null;
81 |
82 |
83 | // ------------------------------------------------------------- Properties
84 |
85 |
86 | /**
87 | * [Package Private] Return the "commit response on flush" flag.
88 | */
89 | public boolean getCommit() {
90 |
91 | return (this.commit);
92 |
93 | }
94 |
95 |
96 | /**
97 | * [Package Private] Set the "commit response on flush" flag.
98 | *
99 | * @param commit The new commit flag
100 | */
101 | public void setCommit(boolean commit) {
102 |
103 | this.commit = commit;
104 |
105 | }
106 |
107 |
108 | // --------------------------------------------------------- Public Methods
109 |
110 |
111 | /**
112 | * Close this output stream, causing any buffered data to be flushed and
113 | * any further output data to throw an IOException.
114 | */
115 | public void close() throws IOException {
116 | if (closed)
117 | throw new IOException("responseStream.close.closed");
118 | response.flushBuffer();
119 | closed = true;
120 | }
121 |
122 |
123 | /**
124 | * Flush any buffered data for this output stream, which also causes the
125 | * response to be committed.
126 | */
127 | public void flush() throws IOException {
128 | if (closed)
129 | throw new IOException("responseStream.flush.closed");
130 | if (commit)
131 | response.flushBuffer();
132 |
133 | }
134 |
135 |
136 | /**
137 | * Write the specified byte to our output stream.
138 | *
139 | * @param b The byte to be written
140 | *
141 | * @exception IOException if an input/output error occurs
142 | */
143 | public void write(int b) throws IOException {
144 |
145 | if (closed)
146 | throw new IOException("responseStream.write.closed");
147 |
148 | if ((length > 0) && (count >= length))
149 | throw new IOException("responseStream.write.count");
150 |
151 | response.write(b);
152 | count++;
153 |
154 | }
155 |
156 |
157 | public void write(byte b[]) throws IOException {
158 | write(b, 0, b.length);
159 |
160 | }
161 |
162 |
163 | public void write(byte b[], int off, int len) throws IOException {
164 | if (closed)
165 | throw new IOException("responseStream.write.closed");
166 |
167 | int actual = len;
168 | if ((length > 0) && ((count + len) >= length))
169 | actual = length - count;
170 | response.write(b, off, actual);
171 | count += actual;
172 | if (actual < len)
173 | throw new IOException("responseStream.write.count");
174 |
175 | }
176 |
177 |
178 | // -------------------------------------------------------- Package Methods
179 |
180 |
181 | /**
182 | * Has this response stream been closed?
183 | */
184 | boolean closed() {
185 | return (this.closed);
186 |
187 | }
188 |
189 |
190 | /**
191 | * Reset the count of bytes written to this stream to zero.
192 | */
193 | void reset() {
194 |
195 | count = 0;
196 |
197 | }
198 |
199 |
200 | @Override
201 | public boolean isReady() {
202 | // TODO Auto-generated method stub
203 | return false;
204 | }
205 |
206 |
207 | @Override
208 | public void setWriteListener(WriteListener arg0) {
209 | // TODO Auto-generated method stub
210 |
211 | }
212 |
213 |
214 | }
215 |
216 |
--------------------------------------------------------------------------------
/src/ex03/connector/ResponseWriter.java:
--------------------------------------------------------------------------------
1 | package ex03.connector;
2 |
3 | import java.io.OutputStreamWriter;
4 | import java.io.PrintWriter;
5 |
6 | /**
7 | * A subclass of PrintWriter that automatically flushes each time
8 | * a print() or println() method is called.
9 | */
10 |
11 | public class ResponseWriter extends PrintWriter {
12 |
13 | public ResponseWriter(OutputStreamWriter writer) {
14 | super(writer);
15 | }
16 |
17 | public void print(boolean b) {
18 | super.print(b);
19 | super.flush();
20 | }
21 |
22 | public void print(char c) {
23 | super.print(c);
24 | super.flush();
25 | }
26 |
27 | public void print(char ca[]) {
28 | super.print(ca);
29 | super.flush();
30 | }
31 |
32 | public void print(double d) {
33 | super.print(d);
34 | super.flush();
35 | }
36 |
37 | public void print(float f) {
38 | super.print(f);
39 | super.flush();
40 | }
41 |
42 | public void print(int i) {
43 | super.print(i);
44 | super.flush();
45 | }
46 |
47 | public void print(long l) {
48 | super.print(l);
49 | super.flush();
50 | }
51 |
52 | public void print(Object o) {
53 | super.print(o);
54 | super.flush();
55 | }
56 |
57 | public void print(String s) {
58 | super.print(s);
59 | super.flush();
60 | }
61 |
62 | public void println() {
63 | super.println();
64 | super.flush();
65 | }
66 |
67 | public void println(boolean b) {
68 | super.println(b);
69 | super.flush();
70 | }
71 |
72 | public void println(char c) {
73 | super.println(c);
74 | super.flush();
75 | }
76 |
77 | public void println(char ca[]) {
78 | super.println(ca);
79 | super.flush();
80 | }
81 |
82 | public void println(double d) {
83 | super.println(d);
84 | super.flush();
85 | }
86 |
87 | public void println(float f) {
88 | super.println(f);
89 | super.flush();
90 | }
91 |
92 | public void println(int i) {
93 | super.println(i);
94 | super.flush();
95 | }
96 |
97 | public void println(long l) {
98 | super.println(l);
99 | super.flush();
100 | }
101 |
102 | public void println(Object o) {
103 | super.println(o);
104 | super.flush();
105 | }
106 |
107 | public void println(String s) {
108 | super.println(s);
109 | super.flush();
110 | }
111 |
112 | public void write(char c) {
113 | super.write(c);
114 | super.flush();
115 | }
116 |
117 | public void write(char ca[]) {
118 | super.write(ca);
119 | super.flush();
120 | }
121 |
122 | public void write(char ca[], int off, int len) {
123 | super.write(ca, off, len);
124 | super.flush();
125 | }
126 |
127 | public void write(String s) {
128 | super.write(s);
129 | super.flush();
130 | }
131 |
132 | public void write(String s, int off, int len) {
133 | super.write(s, off, len);
134 | super.flush();
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/ex03/connector/ServletProcessor.java:
--------------------------------------------------------------------------------
1 | package ex03.connector;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URL;
6 | import java.net.URLClassLoader;
7 | import java.net.URLStreamHandler;
8 |
9 | import javax.servlet.Servlet;
10 |
11 | import ex03.connector.http.Constants;
12 | import ex03.connector.http.HttpRequest;
13 | import ex03.connector.http.HttpResponse;
14 |
15 | public class ServletProcessor {
16 |
17 | /**
18 | * process 方法加载 servlet
19 | * @param request
20 | * @param response
21 | */
22 | public void process(HttpRequest request, HttpResponse response) {
23 |
24 | String uri = request.getRequestURI();
25 | // URI 是以下形式的:/servlet/servletName 其中,servletName 是 servlet 类的名字
26 | String servletName = uri.substring(uri.lastIndexOf("/") + 1);
27 | URLClassLoader loader = null;
28 |
29 | try {
30 | //创建 URLClassLoader 并告诉这个类加载器要加载的类的位置
31 | //对于这个 servlet 容器,类加载器直接在 Constants 指向的目录里边查找
32 | URL[] urls = new URL[1];
33 | URLStreamHandler streamHandler = null;
34 | File classPath = new File(Constants.WEB_ROOT);
35 |
36 | String repository = (new URL("file", null, classPath.getCanonicalPath() +
37 | File.separator)).toString();
38 | urls[0] = new URL(null, repository, streamHandler);
39 |
40 | loader = new URLClassLoader(urls);
41 | } catch (IOException e) {
42 | System.out.println(e.toString());
43 | }
44 |
45 | Class myClass = null;
46 | try {
47 | myClass = loader.loadClass(servletName);
48 | } catch(ClassNotFoundException e) {
49 | e.printStackTrace();
50 | }
51 |
52 | //创建一个 servlet 类加载器的实例, 把它向下转型为 javax.servlet.Servlet,
53 | //并调用 servlet 的 service 方法
54 | Servlet servlet = null;
55 | try {
56 | servlet = (Servlet) myClass.newInstance();
57 | servlet.service(request, response);
58 | ((HttpResponse)response).finishResponse();
59 | } catch (Exception e) {
60 | System.out.println(e.toString());
61 | } catch (Throwable e) {
62 | System.out.println(e.toString());
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/ex03/connector/StaticResourceProcessor.java:
--------------------------------------------------------------------------------
1 | package ex03.connector;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URL;
6 | import java.net.URLClassLoader;
7 | import java.net.URLStreamHandler;
8 |
9 | import javax.servlet.Servlet;
10 |
11 | import ex03.connector.http.Constants;
12 | import ex03.connector.http.HttpRequest;
13 | import ex03.connector.http.HttpResponse;
14 |
15 | /**
16 | * 提供静态资源请求
17 | * @author lc
18 | */
19 | public class StaticResourceProcessor {
20 |
21 | public void process(HttpRequest request, HttpResponse response) {
22 | try {
23 | response.sendStaticResource();
24 | }
25 | catch (IOException e) {
26 | e.printStackTrace();
27 | }
28 | }
29 |
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/src/ex03/connector/StringManager.java:
--------------------------------------------------------------------------------
1 | package ex03.connector;
2 |
3 | import java.util.Hashtable;
4 |
5 | /**
6 | * 单例
7 | * 处理错误信息
8 | * @author lc
9 | */
10 | public class StringManager {
11 |
12 | private static Hashtable managers = new Hashtable();
13 | private String packageName;
14 |
15 | private StringManager(String packageName) {
16 | this.packageName = packageName;
17 | }
18 |
19 | public synchronized static StringManager getManager(String packageName) {
20 | StringManager mgr = (StringManager) managers.get(packageName);
21 | if(mgr == null) {
22 | mgr = new StringManager(packageName);
23 | managers.put(packageName, mgr);
24 | }
25 | return mgr;
26 | }
27 |
28 | public String getString(String string) {
29 | return "HTTP connector has already been initialized";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/ex03/connector/http/Constants.java:
--------------------------------------------------------------------------------
1 | package ex03.connector.http;
2 |
3 | import java.io.File;
4 |
5 | public class Constants {
6 |
7 | public static String Package = "ex03.connector.http";
8 | public static final String WEB_ROOT =
9 | System.getProperty("user.dir") + File.separator + "webroot";
10 | }
11 |
--------------------------------------------------------------------------------
/src/ex03/connector/http/HttpConnector.java:
--------------------------------------------------------------------------------
1 | package ex03.connector.http;
2 |
3 | import java.io.IOException;
4 | import java.net.InetAddress;
5 | import java.net.ServerSocket;
6 | import java.net.Socket;
7 |
8 | /**
9 | * 连接器,创建一个套接字等待前来的 HTTP 请求
10 | * @author lc
11 | */
12 | public class HttpConnector implements Runnable {
13 |
14 | boolean stopped;
15 | private String scheme = "http";
16 |
17 | /**
18 | * @return HTTP
19 | */
20 | public String getScheme() {
21 | return scheme;
22 | }
23 |
24 | /*
25 | * 等待 HTTP 请求
26 | * 为每个请求创建个 HttpProcessor 实例
27 | * 调用 HttpProcessor 的 process 方法
28 | */
29 | @Override
30 | public void run() {
31 | ServerSocket serverSocket = null;
32 | int port = 8080;
33 | try {
34 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | System.exit(1);
38 | }
39 | while(!stopped) {
40 | //允许下一个来自server socket 的连接
41 | Socket socket = null;
42 | try {
43 | socket = serverSocket.accept();
44 | } catch (Exception e) {
45 | continue;
46 | }
47 | //把这个套接字交给一个HttpProcessor
48 | HttpProcessor processor = new HttpProcessor(this);
49 | processor.process(socket);
50 | }
51 | }
52 |
53 | public void start() {
54 | Thread thread = new Thread(this);
55 | thread.start();
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/ex03/connector/http/HttpHeader.java:
--------------------------------------------------------------------------------
1 | package ex03.connector.http;
2 |
3 | /**
4 | * HTTP 头部类
5 | * @author lc
6 | */
7 | public class HttpHeader {
8 |
9 | public static final int INTIAL_NAME_SIZE = 32;
10 | public static final int INTIAL_VALUE_SIZE = 64;
11 | public static final int MAX_VALUE_SIZE = 128;
12 | public static final int MAX_NAME_SIZE = 4096;
13 |
14 | public HttpHeader() {
15 | this(new char[INTIAL_NAME_SIZE], 0, new char[INTIAL_VALUE_SIZE], 0);
16 | }
17 |
18 | public HttpHeader(char[] name, int nameEnd, char[] value, int valueEnd) {
19 | super();
20 | this.name = name;
21 | this.nameEnd = nameEnd;
22 | this.value = value;
23 | this.valueEnd = valueEnd;
24 | }
25 |
26 | public char[] name;
27 | public int nameEnd;
28 | public char[] value;
29 | public int valueEnd;
30 | protected int hashCode = 0;
31 |
32 | public void recycle() {
33 | nameEnd = 0;
34 | valueEnd = 0;
35 | hashCode = 0;
36 | }
37 |
38 | /**
39 | * 测试头部的名称是否与给定的字符数组相同
40 | * 所有的字符都必须是小写的
41 | */
42 | public boolean equals(char[] buf, int end) {
43 | if(end != nameEnd)
44 | return false;
45 | for(int i = 0; i < end; i++) {
46 | if(buf[i] != name[i])
47 | return false;
48 | }
49 | return true;
50 | }
51 |
52 | /**
53 | * 测试标题的名称是否等于给定的字符串
54 | * 给定的字符串必须由小写字母组成
55 | */
56 | public boolean equals(String str) {
57 | return equals(str.toCharArray(), str.length());
58 | }
59 |
60 | public boolean valueEquals(char[] buf) {
61 | return valueEquals(buf, buf.length);
62 | }
63 |
64 | /**
65 | * 测试头部值是否等于给定字符数组
66 | */
67 | public boolean valueEquals(char[] buf, int end) {
68 | if(end != valueEnd)
69 | return false;
70 | for(int i = 0; i< end; i++) {
71 | if(buf[i] != value[i])
72 | return false;
73 | }
74 | return true;
75 | }
76 |
77 | /**
78 | * 测试头部值是否等于给定字符串
79 | */
80 | public boolean valueEquals(String str) {
81 | return valueEquals(str.toCharArray(), str.length());
82 | }
83 |
84 |
85 | /**
86 | * 测试头部值是否包含给定数组
87 | */
88 | public boolean valueIncludes(char[] buf) {
89 | return valueIncludes(buf, buf.length);
90 | }
91 |
92 |
93 | /**
94 | * 测试头部值是否等于给定字符数组
95 | */
96 | public boolean valueIncludes(char[] buf, int end) {
97 | char firstChar = buf[0];
98 | int pos = 0;
99 | while (pos < valueEnd) {
100 | pos = valueIndexOf(firstChar, pos);
101 | if (pos == -1)
102 | return false;
103 | if ((valueEnd - pos) < end)
104 | return false;
105 | for (int i = 0; i < end; i++) {
106 | if (value[i + pos] != buf[i])
107 | break;
108 | if (i == (end-1))
109 | return true;
110 | }
111 | pos++;
112 | }
113 | return false;
114 | }
115 |
116 |
117 | /**
118 | * 测试头部是否包含给定字符串
119 | */
120 | public boolean valueIncludes(String str) {
121 | return valueIncludes(str.toCharArray(), str.length());
122 | }
123 |
124 |
125 | /**
126 | * 返回值中字符的索引
127 | */
128 | public int valueIndexOf(char c, int start) {
129 | for (int i=start; i= 0) {
139 | request.setQueryString(new String(requestLine.uri, question + 1,
140 | requestLine.uriEnd - question - 1));
141 | uri = new String(requestLine.uri, 0, question);
142 | } else {
143 | request.setQueryString(null);
144 | uri = new String(requestLine.uri, 0, requestLine.uriEnd);
145 | }
146 |
147 | //(使用HTTP协议)检查独立 URI
148 | if(!uri.startsWith("/")) {
149 | int pos = uri.indexOf("://");
150 | //解析出协议和主机名
151 | if(pos != -1) {
152 | pos = uri.indexOf('/', pos + 3);
153 | if(pos == -1) {
154 | pos = uri.indexOf('/', pos + 3);
155 | uri = "";
156 | } else {
157 | uri = uri.substring(pos);
158 | }
159 | }
160 | }
161 |
162 | //从请求 URI 中解析任何请求的会话 ID
163 | String match = ";jsessionid=";
164 | int semicolon = uri.indexOf(';');
165 | if(semicolon >= 0) {
166 | String rest = uri.substring(semicolon + match.length());
167 | int semicolon2 = rest.indexOf(';');
168 | if(semicolon2 >= 0) {
169 | request.setRequestedSessionId(rest.substring(0, semicolon2));
170 | rest = rest.substring(semicolon2);
171 | } else {
172 | request.setRequestedSessionId(rest);
173 | rest = "";
174 | }
175 | request.setRequestedSessionURL(true);
176 | uri = uri.substring(0, semicolon) + rest;
177 | } else {
178 | request.setRequestedSessionId(null);
179 | request.setRequestedSessionURL(false);
180 | }
181 |
182 | //规范化URI(目前使用字符串操作)
183 | String normalizedUri = normalize(uri);
184 |
185 | ((HttpRequest) request).setMethod(method);
186 | request.setProtocol(protocol);
187 | if (normalizedUri != null) {
188 | ((HttpRequest) request).setRequestURI(normalizedUri);
189 | } else {
190 | ((HttpRequest) request).setRequestURI(uri);
191 | }
192 |
193 | if (normalizedUri == null) {
194 | throw new ServletException("Invalid URI: " + uri + "'");
195 | }
196 | }
197 |
198 | /**
199 | * 返回一个上下文相关的路径,以“/”开头,代表 “..”和“.”之后指定路径的规范版本。
200 | * 如果指定的路径试图超出当前上下文的边界(即太多的“..”路径元素存在)
201 | * 返回 null code>
202 | * @param path
203 | * @return
204 | */
205 | private String normalize(String path) {
206 | if(path == null)
207 | return null;
208 | //创建一个规范化的地址
209 | String normalized = path;
210 |
211 | //在开始时将“/%7E”和“/%7e”标准化为“/〜”
212 | if(normalized.startsWith("/%7E") || normalized.startsWith("/%7e"))
213 | normalized = "/~" + normalized.substring(4);
214 |
215 | //避免编码'%','/','。' 和“\”,这是特别保留字符
216 | if ((normalized.indexOf("%25") >= 0)
217 | || (normalized.indexOf("%2F") >= 0)
218 | || (normalized.indexOf("%2E") >= 0)
219 | || (normalized.indexOf("%5C") >= 0)
220 | || (normalized.indexOf("%2f") >= 0)
221 | || (normalized.indexOf("%2e") >= 0)
222 | || (normalized.indexOf("%5c") >= 0)) {
223 | return null;
224 | }
225 |
226 | if (normalized.equals("/."))
227 | return "/";
228 |
229 | //标准化斜杠并在必要时添加主斜杠
230 | if(normalized.indexOf("\\") >= 0)
231 | normalized = normalized.replace("\\", "/");
232 | if(!normalized.startsWith("/"))
233 | normalized = "/" + normalized;
234 |
235 | // 在标准化路径中解决“//”的出现
236 | while (true) {
237 | int index = normalized.indexOf("//");
238 | if (index < 0)
239 | break;
240 | normalized = normalized.substring(0, index) +
241 | normalized.substring(index + 1);
242 | }
243 |
244 | //解决标准化路径中“/./”的出现
245 | while (true) {
246 | int index = normalized.indexOf("/./");
247 | if (index < 0)
248 | break;
249 | normalized = normalized.substring(0, index) +
250 | normalized.substring(index + 2);
251 | }
252 |
253 | // 解决标准化路径中“/../”的出现
254 | while (true) {
255 | int index = normalized.indexOf("/../");
256 | if (index < 0)
257 | break;
258 | if (index == 0)
259 | return (null); // 尝试超出我们的范围
260 | int index2 = normalized.lastIndexOf('/', index - 1);
261 | normalized = normalized.substring(0, index2) +
262 | normalized.substring(index + 3);
263 | }
264 |
265 | //声明“/ ...”(三个或更多个点)的出现是无效的
266 | if (normalized.indexOf("/...") >= 0)
267 | return (null);
268 |
269 | // 返回规范化后的路径
270 | return (normalized);
271 | }
272 |
273 | }
274 |
--------------------------------------------------------------------------------
/src/ex03/connector/http/HttpRequest.java:
--------------------------------------------------------------------------------
1 | package ex03.connector.http;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 | import java.io.UnsupportedEncodingException;
8 | import java.net.InetAddress;
9 | import java.net.Socket;
10 | import java.security.Principal;
11 | import java.text.ParseException;
12 | import java.text.SimpleDateFormat;
13 | import java.util.ArrayList;
14 | import java.util.Date;
15 | import java.util.Enumeration;
16 | import java.util.HashMap;
17 | import java.util.Locale;
18 | import java.util.Map;
19 |
20 | import javax.servlet.RequestDispatcher;
21 | import javax.servlet.ServletContext;
22 | import javax.servlet.ServletInputStream;
23 | import javax.servlet.ServletRequest;
24 | import javax.servlet.ServletResponse;
25 | import javax.servlet.http.Cookie;
26 | import javax.servlet.http.HttpSession;
27 | import org.apache.catalina.util.Enumerator;
28 | import org.apache.catalina.util.ParameterMap;
29 | import org.apache.catalina.util.RequestUtil;
30 |
31 | import ex03.connector.RequestStream;
32 |
33 |
34 | /**
35 | * Http 请求
36 | * 使用负责与客户端通信的 socket 传递的 InputStream 对象来构造 HttpRequest 类的实例
37 | * @author lc
38 | */
39 | /**
40 | * @author lc
41 | *
42 | */
43 | public class HttpRequest implements ServletRequest {
44 |
45 | private String contentType;
46 | private int contentLength;
47 | private InetAddress inetAddress;
48 | private InputStream stream;
49 | private String method;
50 | private String protocol;
51 | private String queryString;
52 | private String requestURI;
53 | private String serverName;
54 | private int serverPort;
55 | private Socket socket;
56 | private boolean requestedSessionCookie;
57 | private String requestedSessionId;
58 | private boolean requestedSessionURL;
59 |
60 | public HttpRequest(InputStream input) {
61 | this.stream = input;
62 | }
63 |
64 | /**
65 | * 请求的请求属性
66 | */
67 | protected HashMap, ?> attributes = new HashMap