首页 > 代码库 > How Tomcat Works - Connector
How Tomcat Works - Connector
在前两章实现的WebServer还有很多问题,比如:
1)最后一个out.print("xxx")没有生效。
2)没有解析请求头,请求方法,协议,uri等,而这些内容在servlet里面是可能要用到的。
在这一章中,增加了一个新的servlet:ModerServlet:
import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ModernServlet extends HttpServlet { public void init(ServletConfig config) { System.out.println("ModernServlet -- init"); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Modern Servlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h2>Headers</h2"); Enumeration headers = request.getHeaderNames(); while (headers.hasMoreElements()) { String header = (String) headers.nextElement(); out.println("<br>" + header + " : " + request.getHeader(header)); } out.println("<br><h2>Method</h2"); out.println("<br>" + request.getMethod()); out.println("<br><h2>Parameters</h2"); Enumeration parameters = request.getParameterNames(); while (parameters.hasMoreElements()) { String parameter = (String) parameters.nextElement(); out.println("<br>" + parameter + " : " + request.getParameter(parameter)); } out.println("<br><h2>Query String</h2"); out.println("<br>" + request.getQueryString()); out.println("<br><h2>Request URI</h2"); out.println("<br>" + request.getRequestURI()); out.println("</body>"); out.println("</html>"); } }
这个Servlet和之前两章的PrimitiveServlet不同的是,这个Servlet是继承了HttpServlet而不是实现了Servlet接口。HttpServlet又继承了GenericServlet,实现了Servlet, ServletConfig这两个接口。所以ModerServlet也实现了Servlet接口,而且除此之外还复用了基类的功能。
WebServer的启动和业务逻辑分成了三个类:Bootstrap,HttpProcessor和HttpConnector。HttpConnector实现了Runnable接口,这样就可以把自身的实例通过线程去去执行:
import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; public class HttpConnector implements Runnable { boolean stopped; private String scheme = "http"; public String getScheme() { return scheme; } public void start() { Thread thread = new Thread(this); thread.start(); } @Override 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) { Socket socket = null; try { socket = serverSocket.accept(); } catch (IOException e) { continue; } HttpProcessor processor = new HttpProcessor(this); processor.process(socket); } } }
这部分逻辑和前两章类似,出了新增了一个HttpProcessor,这个类是处理http请求的关键部分。首先来看process函数:
public void process(Socket socket) { SocketInputStream input = null; OutputStream output = null; try { input = new SocketInputStream(socket.getInputStream(), 2048); output = socket.getOutputStream(); // create HttpRequest object and parse request = new HttpRequest(input); // create HttpResponse object response = new HttpResponse(output); response.setRequest(request); response.setHeader("Server", "Pyrmont Servlet Container"); parseRequest(input, output); parseHeaders(input); //check if this is a request for a servlet or a static resource //a request for a servlet begins with "/servlet/" if (request.getRequestURI().startsWith("/servlet/")) { ServletProcessor processor = new ServletProcessor(); processor.process(request, response); } else { StaticResourceProcessor processor = new StaticResourceProcessor(); processor.process(request, response); } // Close the socket socket.close(); // no shutdown for this application } catch (Exception e) { e.printStackTrace(); } }
和HttpServer不一样的是:
1)input类型又InputStream变成了SocketInputStream,这个类tomcat4里面对InputStream的封装类,在现在的tomcat源代码里面这个类已经被移除了。这里直接把这个类的源代码和它依赖的HttpRequestLine和HttpHeader拷贝进来了。同时SocketInputStream这个类还使用了org.apache.catalina.util.RequestUtil 和org.apache.catalina.util.StringManager这两个不包含在源代码里的类,可以去这里下载http://archive.apache.org/dist/tomcat/tomcat-4/v4.1.40/bin/ tomcat的zip版本,在server/lib下找到catalina.jar并加到classpath里面。
2)增加了解析request和header的逻辑:
response.setHeader("Server", "Pyrmont Servlet Container"); parseRequest(input, output); parseHeaders(input)
parseRequest就是解析Http请求的第一部分,比如:
GET /myApp/ModernServlet?userName=tarzan&password=pwd HTTP/1.1
然后把解析到的结果保存到HttpRequest对象中:
private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException { // Parse the incoming request line input.readRequestLine(requestLine);
// http请求方法,这里是GET String method = new String(requestLine.method, 0, requestLine.methodEnd); String uri = null;
// http请求协议,这里是http/1.1 String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd); // Validate the incoming request line if (method.length() < 1) { throw new ServletException("Missing HTTP request method"); } else if (requestLine.uriEnd < 1) { throw new ServletException("Missing HTTP request URI"); }
// 解析问号后面的部分,比如/myApp/ModernServlet?userName=tarzan&password=pwd 解析之后uri成了/myApp/ModernServlet
// ,userName=tarzan&password=pwd保存在HttpRequest的queryString中 // Parse any query parameters out of the request URI int question = requestLine.indexOf("?"); if (question >= 0) { request.setQueryString(new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1)); uri = new String(requestLine.uri, 0, question); } else { request.setQueryString(null); uri = new String(requestLine.uri, 0, requestLine.uriEnd); } // 如果uri是一个绝对路径,比如http://baidu.com/index.html?abc=xyz则把http://baidu.com这一部分去掉 // 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); } } } // 从uri里解析jsessionid,如果有的话 // 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); } // 规范化uri的内容 // 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 + "‘"); } }
How Tomcat Works - Connector