一个较丰满的servlet web server,由简化的conector、HttpProcessor、bootstrap 和简单servelt处理器构成(1代码清单)…

  • Post author:
  • Post category:其他


代码结构:

该目录未能显示项目根下的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