亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

tomcat之連接器

2019-11-10 19:09:54
字體:
來源:轉載
供稿:網友

深入學習java Web服務器系列三

一個簡單的連接器

下面我們來學習tomcat中的連接器。 首先我們先了解一下Catalina的結構圖。

1. Catalina架構圖

catalina 就是Tomcat服務器使用Servlet容器的名字。 Tomcat的核心可以分為3個部分:

Web容器—處理靜態頁面;catalina —處理servlet;jsp容器 — jsp頁面翻譯成一般的servlet

我們可以把catalina看成是兩個主要模塊組成的,連接器(connector)和容器(container)。

這里寫圖片描述

連接器是用來“連接”容器里邊的請求的。它的工作是為接收到每一個HTTP請求構造一個request和response對象。然后它把流程傳遞給容器。容器從連接器接收到requset和response對象之后調用servlet的service方法用于響應。

在本系列的前一篇博文中,一個簡單的servlet容器,我們把創建request和response對象的功能直接交給了我們的容器使用,而本篇博文,我們將寫一個可以創建更好的請求和響應對象的連接器,用來改善之前的程序。

2. StringManager類(Tomcat5)

在Tomcat中,錯誤信息對于系統管理員和servlet程序員都是有用的。例 如,Tomcat記錄錯誤信息,讓系統管理員可以定位發生的任何異常。對servlet程序員來說,Tomcat會在拋出的任何一個 javax.servlet.ServletException中發送一個錯誤信息,這樣程序員可以知道他/她的servlet究竟發送什么錯誤了。

Tomcat所采用的方法是在一個屬性文件里邊存儲錯誤信息,這樣,可以容易的修改這些信息。不過,Tomcat中有數以百計的類。把所有類使用的錯誤信 息存儲到一個大的屬性文件里邊將會容易產生維護的噩夢。為了避免這一情況,Tomcat為每個包都分配一個屬性文件。例如,在包 org.apache.catalina.connector里邊的屬性文件包含了該包所有的類拋出的所有錯誤信息。每個屬性文件都會被一個 org.apache.catalina.util.StringManager類的實例所處理。當Tomcat運行時,將會有許多 StringManager實例,每個實例會讀取包對應的一個屬性文件。此外,由于Tomcat的受歡迎程度,提供多種語言的錯誤信息也是有意義的。

當包里邊的一個類需要查找放在該包屬性文件的一個錯誤信息時,它首先會獲得一個StringManager實例。不過,相同包里邊的許多類可能也需要 StringManager,為每個對象創建一個StringManager實例是一種資源浪費。因此,StringManager類被設計成一個StringManager實例可以被包里邊的所有類共享,這里,StringManager被設計成了單例模式的。我們通過傳遞一個包名來調用它的公共靜態方法 getManager來獲得一個實例。每個實例存儲在一個以包名為鍵(key)的Hashtable中。

PRivate static Hashtable managers = new Hashtable();public synchronized static StringManager getManager(String packageName){ StringManager mgr = (StringManager)managers.get(packageName); if (mgr == null) { mgr = new StringManager(packageName); managers.put(packageName, mgr); } return mgr;}

我們將在這篇博文中的程序中使用這種思想。

3. 模塊劃分

下面我們自己仿照tomcat來實現一個自己的連接器,我們將把本篇博文中的程序分成三個模塊,connector, startup和core。

startup模塊只有一個類,Bootstrap,用來啟動應用的。

connector模塊的類可以分為五組: - 連接器和它的支撐類(HttpConnector和HttpProcessor) - 指代HTTP請求的類(HttpRequest)和它的輔助類 - 指代HTTP響應的類(HttpResponse)和它的輔助類。 - Facade類(HttpRequestFacade和HttpResponseFacade) - Constant類

core模塊由兩個類組成:ServletProcessor和StaticResourceProcessor。

程序的uml圖如下圖所示:

這里寫圖片描述

3.1 startup模塊

startup模塊中只有一個啟動類。

Bootstrap類 Bootstrap類中的main方法實例化HttpConnector類并調用它的start方法

import ex03.pyrmont.connector.http.HttpConnector;public final class Bootstrap { public static void main(String[] args) { HttpConnector connector = new HttpConnector(); connector.start(); }}

HttpConnector類的定義見下面模塊。

3.2 connector模塊

HttpConnector類

HttpConnector類指代一個連接器,職責是創建一個服務器套接字用來等待前來的HTTP請求。 HttpConnector類實現了java.lang.Runnable,所以它能被它自己的線程專用。當你啟動應用程序,一個HttpConnector的實例被創建,并且它的run方法被執行。 一個HttpConnector主要完成下面的事情:

等待HTTP請求為每個請求創建個HttpProcessor實例調用HttpProcessor的process方法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 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) { // Accept the next incoming connection from the server socket Socket socket = null; try { socket = serverSocket.accept(); } catch (Exception e) { continue; } // Hand this socket off to an HttpProcessor HttpProcessor processor = new HttpProcessor(this); processor.process(socket); } } public void start() { Thread thread = new Thread(this); thread.start(); }}

HttpProcessor類 HttpProcessor類的process方法接受前來的HTTP請求的套接字,會做下面的事情

創建一個HttpRequest對象創建一個HttpResponse對象解析HTTP請求的第一行和頭部,并放到HttpRequest對象解析HttpRequest和HttpResponse對象到一個ServletProcessor或者 StaticResourceProcessorimport ex03.pyrmont.ServletProcessor;import ex03.pyrmont.StaticResourceProcessor;import java.net.Socket;import java.io.OutputStream;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.Cookie;import org.apache.catalina.util.RequestUtil;import org.apache.catalina.util.StringManager;/* this class used to be called HttpServer */public class HttpProcessor { public HttpProcessor(HttpConnector connector) { this.connector = connector; } /** * The HttpConnector with which this processor is associated. */ private HttpConnector connector = null; private HttpRequest request; private HttpRequestLine requestLine = new HttpRequestLine(); private HttpResponse response; protected String method = null; protected String queryString = null; /** * The string manager for this package. */ protected StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http"); 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(); } } /** * This method is the simplified version of the similar method in * org.apache.catalina.connector.http.HttpProcessor. * However, this method only parses some "easy" headers, such as * "cookie", "content-length", and "content-type", and ignore other headers. * @param input The input stream connected to our socket * * @exception IOException if an input/output error occurs * @exception ServletException if a parsing error occurs */ private void parseHeaders(SocketInputStream input) throws IOException, ServletException { while (true) { HttpHeader header = new HttpHeader();; // Read the next header input.readHeader(header); if (header.nameEnd == 0) { if (header.valueEnd == 0) { return; } else { throw new ServletException (sm.getString("httpProcessor.parseHeaders.colon")); } } String name = new String(header.name, 0, header.nameEnd); String value = new String(header.value, 0, header.valueEnd); request.addHeader(name, value); // do something for some headers, ignore others. if (name.equals("cookie")) { Cookie cookies[] = RequestUtil.parseCookieHeader(value); for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals("jsessionid")) { // Override anything requested in the URL if (!request.isRequestedSessionIdFromCookie()) { // Accept only the first session id cookie request.setRequestedSessionId(cookies[i].getValue()); request.setRequestedSessionCookie(true); request.setRequestedSessionURL(false); } } request.addCookie(cookies[i]); } } else if (name.equals("content-length")) { int n = -1; try { n = Integer.parseInt(value); } catch (Exception e) { throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength")); } request.setContentLength(n); } else if (name.equals("content-type")) { request.setContentType(value); } } //end while } private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException { // Parse the incoming request line input.readRequestLine(requestLine); String method = new String(requestLine.method, 0, requestLine.methodEnd); String uri = null; 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"); } // 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); } // 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 + "'"); } } /** * Return a context-relative path, beginning with a "/", that represents * the canonical version of the specified path after ".." and "." elements * are resolved out. If the specified path attempts to go outside the * boundaries of the current context (i.e. too many ".." path elements * are present), return <code>null</code> instead. * * @param path Path to be normalized */ protected String normalize(String path) { if (path == null) return null; // Create a place for the normalized path String normalized = path; // Normalize "/%7E" and "/%7e" at the beginning to "/~" if (normalized.startsWith("/%7E") || normalized.startsWith("/%7e")) normalized = "/~" + normalized.substring(4); // Prevent encoding '%', '/', '.' and '/', which are special reserved // characters if ((normalized.indexOf("%25") >= 0) || (normalized.indexOf("%2F") >= 0) || (normalized.indexOf("%2E") >= 0) || (normalized.indexOf("%5C") >= 0) || (normalized.indexOf("%2f") >= 0) || (normalized.indexOf("%2e") >= 0) || (normalized.indexOf("%5c") >= 0)) { return null; } if (normalized.equals("/.")) return "/"; // Normalize the slashes and add leading slash if necessary if (normalized.indexOf('//') >= 0) normalized = normalized.replace('//', '/'); if (!normalized.startsWith("/")) normalized = "/" + normalized; // Resolve occurrences of "http://" in the normalized path while (true) { int index = normalized.indexOf("http://"); if (index < 0) break; normalized = normalized.substring(0, index) + normalized.substring(index + 1); } // Resolve occurrences of "/./" in the normalized path while (true) { int index = normalized.indexOf("/./"); if (index < 0) break; normalized = normalized.substring(0, index) + normalized.substring(index + 2); } // Resolve occurrences of "/../" in the normalized path while (true) { int index = normalized.indexOf("/../"); if (index < 0) break; if (index == 0) return (null); // Trying to go outside our context int index2 = normalized.lastIndexOf('/', index - 1); normalized = normalized.substring(0, index2) + normalized.substring(index + 3); } // Declare occurrences of "/..." (three or more dots) to be invalid // (on some Windows platforms this walks the directory tree!!!) if (normalized.indexOf("/...") >= 0) return (null); // Return the normalized path that we have completed return (normalized); }}

SocketInputStream 是org.apache.catalina.connector.http.SocketInputStream。該類提供了獲取請求行(request line)和請求頭(request header)的方法。通過傳入一個 InputStream 對象和一個代表緩沖區大小的整數值來創建 SocketInputStream 對象。

HttpProcessor 的 process 調用其私有方法 parseRequest 來解析請求行(request line,即 http 請求的第一行)。下面是一個請求行(request line)的例子:

GET /myApp/ModernServlet?userName=tarzan&passWord=pwd HTTP/1.1

注意:“GET”后面和“HTTP”前面各有一個空格。 請求行的第 2 部分是 uri 加上查詢字符串。在上面的例子中,uri 是: /myApp/ModernServlet 問號后面的都是查詢字符串,這里是: userName=tarzan&password=pwd 在 servlet/jsp 編程中,參數 jsessionid 通常是嵌入到 cookie 中的,也可以將其嵌入到查詢字符串中 。

請求頭(request header)由 HttpHeader 對象表示??梢酝ㄟ^ HttpHeader 的無參構造方法建立對象,并將其作為參數傳給 SocketInputStream 的 readHeader 方法,該方法會自動填充 HttpHeader 對象。parseHeader 方法內有一個循環體,不斷的從 SocketInputStream 中讀取 header 信息,直到讀完。獲取 header 的 name 和value 值可使用下米娜的語句: String name = new String(header.name, 0, header.nameEnd); String value = new String(header.value, 0, header.valueEnd); 獲取到 header 的 name 和 value 后,要將其填充到 HttpRequest 的 header 屬性(hashMap 類型)中: request.addHeader(name, value); 其中某些 header 要設置到 request 對象的屬性中,如 contentLength 等。

cookie 是由瀏覽器作為請求頭的一部分發送的,這樣的請求頭的名字是 cookie,它的值是一個 keyvalue 對。舉例如下: Cookie: userName=budi; password=pwd; 對 cookie 的解析是通過 org.apache.catalina.util.RequestUtil 類的 parseCookieHeader 方法完成的。該方法接受一個 cookie 頭字符串,返回一個 javax.servlet.http.Cookie 類型的數組。

我們通過解析http請求的信息并存在httprequest和httpresponse中,并通過process方法傳遞到core模塊。

關于httprequest和httpresponse如何編寫,可以參考之前的博文。

httpconnector調用httpprocessor的process方法,通過傳遞socket對象,連接器解析HTTP請求頭部并讓servlet可以獲得頭部, cookies, 參數名/值等等。這就是連接器的重要作用。

3.3 core模塊

StaticResourceProcessor import ex03.pyrmont.connector.http.HttpRequest; import ex03.pyrmont.connector.http.HttpResponse; import java.io.IOException;

public class StaticResourceProcessor {

public void process(HttpRequest request, HttpResponse response) { try { response.sendStaticResource(); } catch (IOException e) { e.printStackTrace(); } }

}

ServletProcessor

import ex03.pyrmont.connector.http.Constants;import ex03.pyrmont.connector.http.HttpRequest;import ex03.pyrmont.connector.http.HttpResponse;import ex03.pyrmont.connector.http.HttpRequestFacade;import ex03.pyrmont.connector.http.HttpResponseFacade;import java.io.File;import java.io.IOException;import java.net.URL;import java.net.URLClassLoader;import java.net.URLStreamHandler;import javax.servlet.Servlet;public class ServletProcessor { public void process(HttpRequest request, HttpResponse response) { String uri = request.getRequestURI(); String servletName = uri.substring(uri.lastIndexOf("/") + 1); URLClassLoader loader = null; try { // create a URLClassLoader URL[] urls = new URL[1]; URLStreamHandler streamHandler = null; File classPath = new File(Constants.WEB_ROOT); String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ; urls[0] = new URL(null, repository, streamHandler); loader = new URLClassLoader(urls); } catch (IOException e) { System.out.println(e.toString() ); } Class myClass = null; try { myClass = loader.loadClass(servletName); } catch (ClassNotFoundException e) { System.out.println(e.toString()); } Servlet servlet = null; try { servlet = (Servlet) myClass.newInstance(); HttpRequestFacade requestFacade = new HttpRequestFacade(request); HttpResponseFacade responseFacade = new HttpResponseFacade(response); servlet.service(requestFacade, responseFacade); ((HttpResponse) response).finishResponse(); } catch (Exception e) { System.out.println(e.toString()); } catch (Throwable e) { System.out.println(e.toString()); } }}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
8x拔播拔播x8国产精品| 国产精品揄拍500视频| 国产精品无码专区在线观看| 精品国产一区二区三区久久久狼| 国产精品高潮在线| 亚洲美女激情视频| 成人深夜直播免费观看| 福利二区91精品bt7086| 亚洲男人的天堂在线| 中文字幕国产亚洲2019| 国产精品男人的天堂| 操人视频在线观看欧美| 成人啪啪免费看| 日本国产欧美一区二区三区| 国产精品久久久久影院日本| 国产精品夜色7777狼人| 亚洲性日韩精品一区二区| 久久久影视精品| 国产成人一区二| 亚洲成人激情视频| 亚洲欧洲在线看| 午夜精品www| 日韩高清免费观看| 中文字幕视频一区二区在线有码| 久久91亚洲精品中文字幕奶水| 国产精品久久久av| 在线日韩欧美视频| 亚洲欧美日韩中文视频| 国产精品麻豆va在线播放| 高清亚洲成在人网站天堂| 亚洲精品视频免费| 亚洲午夜av久久乱码| 精品国产老师黑色丝袜高跟鞋| 91超碰caoporn97人人| 国内免费精品永久在线视频| 一区二区欧美久久| 一级做a爰片久久毛片美女图片| 国产精品av免费在线观看| 日韩福利视频在线观看| 亚洲精品久久7777777| 欧美激情视频给我| 91po在线观看91精品国产性色| 国产噜噜噜噜久久久久久久久| 日韩高清人体午夜| 91大神在线播放精品| xvideos成人免费中文版| 国产精品专区h在线观看| 日韩高清电影免费观看完整版| 国产丝袜精品视频| 日日狠狠久久偷偷四色综合免费| 国产精品视频自拍| 亚洲国产精彩中文乱码av在线播放| 亚洲风情亚aⅴ在线发布| 国产精品一二三在线| 欧美午夜片欧美片在线观看| 成人欧美一区二区三区黑人孕妇| 国产xxx69麻豆国语对白| 精品久久久国产| 亚洲性生活视频在线观看| 国产v综合v亚洲欧美久久| 亚洲性xxxx| 欧美在线播放视频| 国产成人精品视频| 亚洲午夜国产成人av电影男同| 久久精品福利视频| 国产亚洲在线播放| 91福利视频网| 国产亚洲精品久久久久动| 中文字幕视频一区二区在线有码| 78色国产精品| 久青草国产97香蕉在线视频| 欧美日韩美女视频| 91欧美精品午夜性色福利在线| 国产精品老女人视频| 国产91精品视频在线观看| 欧美在线观看日本一区| 成人在线精品视频| 国产成人精品电影久久久| 国产丝袜一区二区三区| 亚洲午夜未满十八勿入免费观看全集| 成人午夜激情免费视频| 久久久999精品免费| 91久久夜色精品国产网站| 亚洲成年人在线| 久久久亚洲成人| 欧美日韩福利电影| 亚洲精品福利视频| 精品国产91久久久| 日本久久中文字幕| 日本精品久久中文字幕佐佐木| 亚洲精品福利免费在线观看| 红桃av永久久久| 久久精品亚洲热| 欧美精品videosex性欧美| 国产精品大陆在线观看| 疯狂做受xxxx高潮欧美日本| 国产成人高清激情视频在线观看| 亚洲色图av在线| 亚洲精品国产综合区久久久久久久| 久久久91精品国产| 久久久噜噜噜久久| 国产精品中文久久久久久久| 国产亚洲欧洲黄色| 91精品视频观看| 欧美尺度大的性做爰视频| 欧美在线亚洲在线| 久久艳片www.17c.com| 色中色综合影院手机版在线观看| 国模精品系列视频| 欧美超级免费视 在线| 91九色在线视频| 国产香蕉精品视频一区二区三区| 久久人人爽亚洲精品天堂| 中文字幕亚洲情99在线| 欧美性猛交xxxx富婆弯腰| 亚洲欧美日本精品| 欧美激情一区二区三区成人| 欧美精品www在线观看| 亚洲国产成人久久综合一区| 97视频在线看| www.精品av.com| 欧美国产高跟鞋裸体秀xxxhd| 国产精品视频播放| 成人乱人伦精品视频在线观看| 精品国内自产拍在线观看| 亚洲免费高清视频| 日韩a**中文字幕| 夜夜嗨av色一区二区不卡| 欧美自拍视频在线| 国产精品亚洲视频在线观看| 一本大道久久加勒比香蕉| 欧美日韩精品在线观看| 国产精品第七影院| 国产精品小说在线| 国产一区二区三区在线观看网站| 91在线观看免费网站| 亚洲天堂av女优| 国产精品一区二区三区毛片淫片| 中日韩美女免费视频网址在线观看| 亚洲剧情一区二区| 国产精品成人aaaaa网站| 92看片淫黄大片看国产片| 国产a∨精品一区二区三区不卡| 国产日韩欧美黄色| 亚洲男人的天堂在线播放| 国产精品日日做人人爱| 国产视频亚洲视频| 免费99精品国产自在在线| 亚洲国产精品久久| 一本一本久久a久久精品牛牛影视| 日韩有码在线播放| 欧洲永久精品大片ww免费漫画| 久久久成人精品视频| 国产精品视频99| 一色桃子一区二区| 国产精品福利小视频| 欧美日韩一二三四五区| 亚洲国产高清高潮精品美女| 最好看的2019年中文视频| 亚洲的天堂在线中文字幕| 在线观看日韩欧美| 国产在线拍揄自揄视频不卡99| 日韩亚洲在线观看| 欧美怡红院视频一区二区三区|