代码结构:
该目录未能显示项目根下的webroot文件夹,此文件夹存放servlet程序员编译的servlet.class文件和静态资源。
- BootStrap
package sub.startup; import sub.connector.http.HttpConnector; public final class Bootstrap { public static void main(String[] args) { //之前是一个server,现在是有启动和连接器代理 HttpConnector connector = new HttpConnector(); connector.start(); } }
View Code
- HttpConnector
1 package sub.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 public class HttpConnector implements Runnable { //开启了线程机制 9 10 boolean stopped;//默认false 11 private String scheme = "http"; 12 13 public String getScheme() { 14 return scheme; 15 } 16 17 public void run() { 18 ServerSocket serverSocket = null; 19 int port = 8080; 20 try { 21 serverSocket = new ServerSocket(port, 1, InetAddress.getByName("192.168.1.100"));//绑定自己的ip 22 } 23 catch (IOException e) { 24 e.printStackTrace(); 25 System.exit(1); 26 } 27 while (!stopped) { 28 // Accept the next incoming connection from the server socket 29 Socket socket = null; 30 try { 31 socket = serverSocket.accept(); 32 System.out.println("服务器接受到请求"); 33 } 34 catch (Exception e) { 35 continue; 36 } 37 // Hand this socket off to an HttpProcessor 38 HttpProcessor processor = new HttpProcessor(this); 39 processor.process(socket); 40 System.out.println("进入Http解析程序"); 41 } 42 } 43 44 public void start() { 45 Thread thread = new Thread(this); 46 thread.start(); 47 } 48 }
View Code
- HttpProcessor
1 package sub.connector.http; 2 3 import sub.ServletProcessor; 4 import sub.StaticResourceProcessor; 5 import java.net.Socket; 6 import java.io.OutputStream; 7 import java.io.IOException; 8 import javax.servlet.ServletException; 9 import javax.servlet.http.Cookie; 10 import org.apache.catalina.util.RequestUtil; 11 import org.apache.catalina.util.StringManager; 12 13 /* this class used to be called HttpServer 取代了原来的HttpServer类*/ 14 /** 15 * 原来的HttpServer除去等待请求的任务以外,就是创建res/req并“初始化”,req对象的创建,头部,行部的初始化和处理,req形同一个entity,但是请求体的解析在哪里? 16 * 并调用加载servlet的processor类(负责servlet的加载处理),使其动态加载servlet并传递res/req 17 * 如何获得请求体? 18 * */ 19 public class HttpProcessor { 20 21 public HttpProcessor(HttpConnector connector) { 22 this.connector = connector; 23 } 24 /** 25 * The HttpConnector with which this processor is associated. 26 */ 27 private HttpConnector connector = null; 28 private HttpRequest request; 29 private HttpRequestLine requestLine = new HttpRequestLine(); 30 private HttpResponse response; 31 32 protected String method = null; 33 protected String queryString = null; 34 35 /** 36 * The string manager for this package. 37 */ 38 protected StringManager sm = 39 StringManager.getManager("sub.connector.http");//读取标签文件中的信息,用来打印日志,指定包,供本包使用。 40 41 public void process(Socket socket) { //负责创建res/req 42 SocketInputStream input = null; 43 OutputStream output = null; 44 try { 45 input = new SocketInputStream(socket.getInputStream(), 2048); 46 output = socket.getOutputStream(); 47 48 // create HttpRequest object and parse 49 request = new HttpRequest(input); 50 51 // create HttpResponse object 52 response = new HttpResponse(output); 53 response.setRequest(request); //响应对象的诸多属性,为什么不需要初始化?除了响应体之外。 54 55 response.setHeader("Server", "Pyrmont Servlet Container"); 56 parseRequest(input, output); //解析请求行 57 parseHeaders(input); //解析请求头 58 59 //check if this is a request for a servlet or a static resource 60 //a request for a servlet begins with "/servlet/" 61 if (request.getRequestURI().startsWith("/servlet/")) { 62 ServletProcessor processor = new ServletProcessor(); 63 processor.process(request, response); 64 } 65 else { 66 StaticResourceProcessor processor = new StaticResourceProcessor(); 67 processor.process(request, response); 68 } 69 70 // Close the socket 71 socket.close(); 72 // no shutdown for this application 73 } 74 catch (Exception e) { 75 e.printStackTrace(); 76 } 77 } 78 79 /** 80 * 该方法类似于初始化或构造方法的延伸,根据目的是使属性得到值。 81 * This method is the simplified version of the similar method in 82 * org.apache.catalina.connector.http.HttpProcessor. 83 * However, this method only parses some "easy" headers, such as 84 * "cookie", "content-length", and "content-type", and ignore other headers. 85 * @param input The input stream connected to our socket 86 * 87 * @exception IOException if an input/output error occurs 88 * @exception ServletException if a parsing error occurs 89 */ 90 private void parseHeaders(SocketInputStream input) 91 throws IOException, ServletException { //旧版本中该输入流有现成的方法获取头部信息。从关系来看,request更类似一个entity类 92 while (true) { 93 HttpHeader header = new HttpHeader(); 94 95 // Read the next header 96 input.readHeader(header); 97 if (header.nameEnd == 0) { 98 if (header.valueEnd == 0) { 99 return; 100 } 101 else { 102 throw new ServletException 103 (sm.getString("httpProcessor.parseHeaders.colon")); 104 } 105 } 106 107 String name = new String(header.name, 0, header.nameEnd); 108 String value = new String(header.value, 0, header.valueEnd); 109 request.addHeader(name, value); 110 // do something for some headers, ignore others. 111 if (name.equals("cookie")) { 112 Cookie cookies[] = RequestUtil.parseCookieHeader(value);//从header中获得cookie 113 for (int i = 0; i < cookies.length; i++) { 114 if (cookies[i].getName().equals("jsessionid")) { 115 // Override anything requested in the URL 116 if (!request.isRequestedSessionIdFromCookie()) { 117 // Accept only the first session id cookie 118 request.setRequestedSessionId(cookies[i].getValue()); 119 request.setRequestedSessionCookie(true); 120 request.setRequestedSessionURL(false); 121 } 122 } 123 request.addCookie(cookies[i]); //request添加cookie对象,以上是cookie对象赋值。 124 } 125 } 126 else if (name.equals("content-length")) { 127 int n = -1; 128 try { 129 n = Integer.parseInt(value); 130 } 131 catch (Exception e) { 132 throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength")); 133 } 134 request.setContentLength(n); //设置content-length 135 } 136 else if (name.equals("content-type")) { 137 request.setContentType(value); 138 } 139 } //end while 140 } 141 142 143 private void parseRequest(SocketInputStream input, OutputStream output) 144 throws IOException, ServletException { //解析请求行,input对象可以直接获取这些属性值。 145 146 // Parse the incoming request line 147 input.readRequestLine(requestLine); 148 String method = 149 new String(requestLine.method, 0, requestLine.methodEnd); 150 String uri = null; 151 String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd); 152 153 // Validate the incoming request line 154 if (method.length() < 1) { 155 throw new ServletException("Missing HTTP request method"); 156 } 157 else if (requestLine.uriEnd < 1) { 158 throw new ServletException("Missing HTTP request URI"); 159 } 160 // Parse any query parameters out of the request URI 161 int question = requestLine.indexOf("?"); 162 if (question >= 0) { 163 request.setQueryString(new String(requestLine.uri, question + 1, 164 requestLine.uriEnd - question - 1)); 165 uri = new String(requestLine.uri, 0, question); 166 } 167 else { 168 request.setQueryString(null); 169 uri = new String(requestLine.uri, 0, requestLine.uriEnd); 170 } 171 172 173 // Checking for an absolute URI (with the HTTP protocol) 174 if (!uri.startsWith("/")) { 175 int pos = uri.indexOf("://"); 176 // Parsing out protocol and host name 177 if (pos != -1) { 178 pos = uri.indexOf('/', pos + 3); 179 if (pos == -1) { 180 uri = ""; 181 } 182 else { 183 uri = uri.substring(pos); 184 } 185 } 186 187 } 188 189 // Parse any requested session ID out of the request URI 从请求URI中解析所请求的会话ID 190 String match = ";jsessionid="; 191 int semicolon = uri.indexOf(match); 192 if (semicolon >= 0) { 193 String rest = uri.substring(semicolon + match.length()); 194 int semicolon2 = rest.indexOf(';'); 195 if (semicolon2 >= 0) { 196 request.setRequestedSessionId(rest.substring(0, semicolon2)); 197 rest = rest.substring(semicolon2); 198 } 199 else { 200 request.setRequestedSessionId(rest); 201 rest = ""; 202 } 203 request.setRequestedSessionURL(true); 204 uri = uri.substring(0, semicolon) + rest; 205 } 206 else { 207 request.setRequestedSessionId(null); 208 request.setRequestedSessionURL(false); 209 } 210 211 // Normalize URI (using String operations at the moment) 规范化请求uri 212 String normalizedUri = normalize(uri); 213 214 // Set the corresponding request properties 设置相应的请求属性 215 ((HttpRequest) request).setMethod(method); 216 request.setProtocol(protocol); 217 if (normalizedUri != null) { 218 ((HttpRequest) request).setRequestURI(normalizedUri); 219 } 220 else { 221 ((HttpRequest) request).setRequestURI(uri); 222 } 223 224 if (normalizedUri == null) { 225 throw new ServletException("Invalid URI: " + uri + "'"); 226 } 227 } 228 229 /** 230 * Return a context-relative path, beginning with a "/", that represents 231 * the canonical version of the specified path after ".." and "." elements 232 * are resolved out. If the specified path attempts to go outside the 233 * boundaries of the current context (i.e. too many ".." path elements 234 * are present), return <code>null</code> instead. 235 * 236 * @param path Path to be normalized 237 */ 238 protected String normalize(String path) { 239 if (path == null) 240 return null; 241 // Create a place for the normalized path 242 String normalized = path; 243 244 // Normalize "/%7E" and "/%7e" at the beginning to "/~" 245 if (normalized.startsWith("/%7E") || normalized.startsWith("/%7e")) 246 normalized = "/~" + normalized.substring(4); 247 248 // Prevent encoding '%', '/', '.' and '\', which are special reserved 249 // characters 250 if ((normalized.indexOf("%25") >= 0) 251 || (normalized.indexOf("%2F") >= 0) 252 || (normalized.indexOf("%2E") >= 0) 253 || (normalized.indexOf("%5C") >= 0) 254 || (normalized.indexOf("%2f") >= 0) 255 || (normalized.indexOf("%2e") >= 0) 256 || (normalized.indexOf("%5c") >= 0)) { 257 return null; 258 } 259 260 if (normalized.equals("/.")) 261 return "/"; 262 263 // Normalize the slashes and add leading slash if necessary 264 if (normalized.indexOf('\\') >= 0) 265 normalized = normalized.replace('\\', '/'); 266 if (!normalized.startsWith("/")) 267 normalized = "/" + normalized; 268 269 // Resolve occurrences of "//" in the normalized path 270 while (true) { 271 int index = normalized.indexOf("//"); 272 if (index < 0) 273 break; 274 normalized = normalized.substring(0, index) + 275 normalized.substring(index + 1); 276 } 277 278 // Resolve occurrences of "/./" in the normalized path 279 while (true) { 280 int index = normalized.indexOf("/./"); 281 if (index < 0) 282 break; 283 normalized = normalized.substring(0, index) + 284 normalized.substring(index + 2); 285 } 286 287 // Resolve occurrences of "/../" in the normalized path 288 while (true) { 289 int index = normalized.indexOf("/../"); 290 if (index < 0) 291 break; 292 if (index == 0) 293 return (null); // Trying to go outside our context 294 int index2 = normalized.lastIndexOf('/', index - 1); 295 normalized = normalized.substring(0, index2) + 296 normalized.substring(index + 3); 297 } 298 299 // Declare occurrences of "/..." (three or more dots) to be invalid 300 // (on some Windows platforms this walks the directory tree!!!) 301 if (normalized.indexOf("/...") >= 0) 302 return (null); 303 304 // Return the normalized path that we have completed 305 return (normalized); 306 307 } 308 309 }
View Code
- HttpRequest
1 package sub.connector.http; 2 3 /** this class copies methods from org.apache.catalina.connector.HttpRequestBase 4 * and org.apache.catalina.connector.http.HttpRequestImpl. 5 * The HttpRequestImpl class employs a pool of HttpHeader objects for performance 6 * These two classes will be explained in Chapter 4. 7 */ 8 import sub.connector.RequestStream; 9 10 11 12 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletResponse; 14 import javax.servlet.http.HttpSession; 15 import javax.servlet.http.HttpUpgradeHandler; 16 import javax.servlet.http.Part; 17 import javax.servlet.http.Cookie; 18 import javax.servlet.AsyncContext; 19 import javax.servlet.DispatcherType; 20 import javax.servlet.RequestDispatcher; 21 import javax.servlet.ServletContext; 22 import javax.servlet.ServletException; 23 import javax.servlet.ServletInputStream; 24 import javax.servlet.ServletRequest; 25 import javax.servlet.ServletResponse; 26 27 import java.security.Principal; 28 import java.io.InputStream; 29 import java.io.InputStreamReader; 30 import java.io.IOException; 31 import java.io.BufferedReader; 32 import java.io.UnsupportedEncodingException; 33 import java.net.InetAddress; 34 import java.net.Socket; 35 import java.text.ParseException; 36 import java.text.SimpleDateFormat; 37 import java.util.ArrayList; 38 import java.util.Collection; 39 import java.util.Date; 40 import java.util.Enumeration; 41 import java.util.HashMap; 42 import java.util.Locale; 43 import java.util.Map; 44 import org.apache.catalina.util.Enumerator; 45 import org.apache.catalina.util.ParameterMap; 46 import org.apache.catalina.util.RequestUtil; 47 /**request在servet程序员使用之前解析还是使用时解析更好? 48 * 如果是前者,就应该在servletProcessor中类似于初始化那样,全部解析,然后程序员可以直接get; 49 * 若后者,则需要在get中包含解析过程。 50 * */ 51 public class HttpRequest implements HttpServletRequest { 52 53 private String contentType; 54 private int contentLength; 55 private InetAddress inetAddress; 56 private InputStream input; 57 private String method; 58 private String protocol; 59 private String queryString; 60 private String requestURI; 61 private String serverName; 62 private int serverPort; 63 private Socket socket; 64 private boolean requestedSessionCookie; 65 private String requestedSessionId; 66 private boolean requestedSessionURL; 67 68 /** 69 * The request attributes for this request. 该当前请求的请求属性 70 */ 71 protected HashMap attributes = new HashMap(); 72 /** 73 * The authorization credentials sent with this Request. 当前请求的授权证书 74 */ 75 protected String authorization = null; 76 /** 77 * The context path for this request. 当前请求的上下文路径 78 */ 79 protected String contextPath = ""; 80 /** 81 * The set of cookies associated with this Request. 与请求关联的一组cookie(浏览器会自行发送cookie) 82 */ 83 protected ArrayList cookies = new ArrayList(); 84 /** 85 * An empty collection to use for returning empty Enumerations. Do not 86 * add any elements to this collection! 87 */ 88 protected static ArrayList empty = new ArrayList(); 89 /** 90 * The set of SimpleDateFormat formats to use in getDateHeader(). 91 */ 92 protected SimpleDateFormat formats[] = { 93 new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), 94 new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), 95 new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) 96 }; 97 98 /** 99 * The HTTP headers associated with this Request, keyed by name. The 100 * values are ArrayLists of the corresponding header values. 用map保存header信息。 101 */ 102 protected HashMap headers = new HashMap(); 103 /** 104 * The parsed parameters for this request. This is populated only if 105 * parameter information is requested via one of the 106 * <code>getParameter()</code> family of method calls. The key is the 107 * parameter name, while the value is a String array of values for this 108 * parameter. 109 * <p> 110 * <strong>IMPLEMENTATION NOTE</strong> - Once the parameters for a 111 * particular request are parsed and stored here, they are not modified. 112 * Therefore, application level access to the parameters need not be 113 * synchronized. 114 */ 115 protected ParameterMap parameters = null; //hashmap的子类 116 117 /** 118 * Have the parameters for this request been parsed yet? 119 */ 120 protected boolean parsed = false; 121 protected String pathInfo = null; 122 123 /** 124 * The reader that has been returned by <code>getReader</code>, if any. 125 */ 126 protected BufferedReader reader = null