How Tomcat works 3: 简单的connector
- Connetor的业务流程图:
- StringManager:
(1)用于给tomcat管理各种error message。不同语言包的error Message存储在如:LocalStrings.properties, 日文的文件如:LocalStrings_jp.properties
(2)StringManager是包内共享,全局使用Singleton pattern将所有key-value(包名-stringmanager)存储在HashTable中。
(3)实现代码:
package com.cisco.tomcat.connector;
import java.util.Hashtable;
public class StringManager {
private static Hashtable<String, StringManager> managers = new Hashtable<>();
private StringManager(String packageName) {}
public synchronized static StringManager getManager(String packageName) {
StringManager stringManager = managers.get(packageName);
if(stringManager == null) {
stringManager = new StringManager(packageName);
managers.put(packageName, stringManager);
}
return stringManager;
}
}
- Connecter应用:
(1)包含三个模块:startup、core、connector
(2)startup主要一个类:Bootstrap,用于启动程序
(3)core主要两个类:ServletProcessor、staticResourceProcessor
(4)connector五个部分:HttpRequest和support类、HttpResponse和support类、HttpRequestFacade类、HttpResponseFacade类、HttpConnector和HttpProcessor、constants类
UML图如下:
- Connection应用程序细节:
(1)程序细节组成:Starting Application->The Connector->Creating an HttpRequest Object->Creating an HttpResponse Object->Static Resouce Process and Servlet Resource Process->Running Application
(2)Starting Application:
Bootstrap类代码:
package com.cisco.tomcat.connector;
public class Bootstrap {
public void main(String[] args) {
HttpConnector connector = new HttpConnector();
connector.start();
}
}
(3)Connector类代码:
生成serverSocket->调用socket = serverSocket.accept() -> Processor(HttpConnector).process(socket)
package com.cisco.tomcat.connector;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpConnector implements Runnable {
boolean stopped;
private int PORT = 8080;
private String HOST = "127.0.0.1";
private String schema = "HTTP";
public String getSchema() {
return this.schema;
}
@Override
public void run() {
ServerSocket serverSocket = null;
try {
// 生成serverSocket服务
serverSocket = new ServerSocket(PORT, 1, InetAddress.getByName(HOST));
}catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
while(!stopped) {
Socket socket = null;
try {
// 从serverSocket接受socket
socket = serverSocket.accept();
}catch (Exception e) {
e.printStackTrace();
continue;
}
// 初始化processor, 处理socket
HttpProcessor httpProcessor = new HttpProcessor(this);
httpProcessor.process(socket);
}
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
}
(4)HttpProcessor类:
生成inputStream/outputStream->创建request填充inputStream->创建response填充output,header->调用parseHeader/parseRequestLine->根据request.getUri分派到staticResource/servletProcessor->socket.close()
package com.cisco.tomcat.connector;
import java.io.OutputStream;
import java.net.Socket;
public class HttpProcessor {
private HttpConnector httpConnector;
private SocketInputStream inputStream;
private OutputStream outputStream;
private static int BYTE_SIZE = 2048;
public HttpProcessor(HttpConnector httpConnector) {
this.httpConnector = httpConnector;
}
public void process(Socket socket) {
try {
inputStream = new SocketInputStream(socket.getInputStream(), BYTE_SIZE);
outputStream = socket.getOutputStream();
// 创建httpRequest对象
HttpRequest httpRequest = new HttpRequest(inputStream);
// 创建httpResponse对象
HttpResponse httpResponse = new HttpResponse(outputStream);
httpResponse.setRequest(httpRequest);
httpResponse.setHeader("Server", "Pyromont Servlet Container");
// 解析Header和Request参数
parseRequest(input, output);
parseHeader(input);
// 判断url路径,是否调用ServletResource
if(httpRequest.getUri().contains("/servlet/")) {
ServletProcessor servletProcessor = new ServletProcessor();
servletProcessor.process(httpRequest, httpResponse);
// 或者调用staticResource
}else {
StaticResourceProcessor staticResourceProcessor = new StaticResourceProcessor();
staticResourceProcessor.process(httpRequest, httpResponse);
}
// 关闭socket
socket.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
(5)HttpProcessor类中解析requestLine和requestHeader
request类的UML:
requestLine: 读取inputStream->生成requestLine对象->解析protocol/uri/method/requestSessionId/queryString
private void parseRequestLine(SocketInputStream inputStream, OutputStream outputStream) throws ServletException {
inputStream.readRequestLine(requestLine);
String method = new String(requestLine.method, 0, requestLine.methodEnd);
String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);
String uri = new String(requestLine.uri, 0, requestLine.uriEnd);
if (method.length() < 1) {
throw new ServletException("Http Request line missing method");
}
if (protocol.length() < 1) {
throw new ServletException("Http Request Line missing protocol");
}
if (uri.length() < 1) {
throw new ServletException("Http Request Line missing uri");
}
int question = requestLine.indexOf("?");
if (question >= 0) {
request.setQueryString(new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1));
uri = new String(requestLine.uri, 0, question);
} else {
request.setQueryString(null);
uri = new String(requestLine.uri, 0, requestLine.uriEnd);
}
// Checking for an absolute URI (with the HTTP protocol)
if (!uri.startsWith("/")) {
int pos = uri.indexOf("://"); // Parsing out protocol and host name
if (pos != -1) {
pos = uri.indexOf('/', pos + 3);
if (pos == -1) {
uri = "";
} else {
uri = uri.substring(pos);
}
}
}
// Parse any requested session ID out of the request URI
String match = ";jsessionid=";
int semicolon = uri.indexOf(match);
if (semicolon >= 0) {
String rest = uri.substring(semicolon + match.length());
int semicolon2 = rest.indexOf(';');
if (semicolon2 >= 0) {
request.setRequestedSessionId(rest.substring(0, semicolon2));
rest = rest.substring(semicolon2);
} else {
request.setRequestedSessionId(rest);
rest = "";
}
request.setRequestedSessionURL(true);
uri = uri.substring(0, semicolon) + rest;
} else {
request.setRequestedSessionId(null);
request.setRequestedSessionURL(false);
}
// Normalize URI (using String operations at the moment)
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 + "'");
}
}
requestHeader:读取name:value键值对,如果有cookie解析headerCookie
requestParameter: 使用catalina.parameterMap:如果是GET,解析header的queryString即可。 如果是POST,解析body。仅在servlet中调用getParameter时,才启动解析Parameter的程序