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

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

tomcat之連接器

2019-11-10 17:08:24
字體:
來源:轉載
供稿:網友

深入學習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
亚洲国产欧美一区二区三区同亚洲| 日韩av在线电影网| 亚洲欧美中文在线视频| 国产精品久久久久久久7电影| 热久久视久久精品18亚洲精品| 欧美日韩色婷婷| 精品国产视频在线| 国产精品一区专区欧美日韩| 亚洲三级 欧美三级| 日韩在线观看免费高清完整版| 亚洲精品国产精品自产a区红杏吧| 伊人久久久久久久久久久| 精品久久久久久久久久久| 久久久精品欧美| 欧美精品videossex性护士| 中文字幕日韩欧美在线视频| 精品视频在线播放免| 国产日本欧美在线观看| 538国产精品一区二区免费视频| 成人在线视频网站| 亚洲欧美在线第一页| 97久久超碰福利国产精品…| 国产成人精品一区二区三区| 日韩一区二区三区国产| 欧美亚洲国产视频| 欧美麻豆久久久久久中文| 国产精品专区一| 日韩在线视频二区| 欧美日韩国产在线看| 国产精品一区二区在线| 国产一区二区三区高清在线观看| 日韩av电影免费观看高清| 日韩欧美一区二区三区久久| 亚洲石原莉奈一区二区在线观看| 欧美日韩成人免费| 91超碰caoporn97人人| 亚洲美女视频网站| 精品免费在线视频| 亚洲缚视频在线观看| 日韩在线免费视频观看| 色yeye香蕉凹凸一区二区av| 亚洲国产成人爱av在线播放| 亚洲美女视频网站| 欧美视频一区二区三区…| 久久99精品久久久久久青青91| 日韩精品在线看| 亚洲精品日韩久久久| 欧洲亚洲免费在线| 91欧美精品午夜性色福利在线| 亚洲第一天堂无码专区| 日韩美女主播视频| 高清欧美性猛交| 亚洲激情视频网| 在线观看日韩专区| 一区二区三区无码高清视频| 欧美精品久久久久久久久| 国产情人节一区| 精品国产乱码久久久久久虫虫漫画| 亚洲国产另类久久精品| 九九热r在线视频精品| 国产97在线亚洲| 国产成人综合精品| 欧美中文在线视频| 欧美另类99xxxxx| 亚洲高清在线观看| 欧美日韩国产成人高清视频| 精品偷拍各种wc美女嘘嘘| 欧美激情视频播放| 成人免费淫片aa视频免费| 欧美性xxxx在线播放| 日韩视频中文字幕| 欧美在线欧美在线| 欧美黑人xxxⅹ高潮交| 欧美日韩aaaa| 6080yy精品一区二区三区| 色综合久久天天综线观看| 蜜臀久久99精品久久久久久宅男| 自拍偷拍亚洲在线| 精品中文字幕在线| 国产精品亚洲网站| 日韩av在线一区二区| 91在线观看免费高清| 91手机视频在线观看| www.色综合| 成人午夜两性视频| 国内精品久久久久影院 日本资源| 欧美大全免费观看电视剧大泉洋| 成人免费网站在线| 亚洲成人网在线| 精品亚洲永久免费精品| 亚洲激情视频在线播放| 亚洲黄色www网站| 日韩专区中文字幕| 欧美在线视频免费观看| 色99之美女主播在线视频| 欧美在线观看日本一区| 亚洲iv一区二区三区| 中文字幕在线精品| 欧美高清一级大片| 中文字幕日韩有码| 亚洲欧洲日本专区| 久久精品91久久久久久再现| 人体精品一二三区| 久久久精品国产网站| 国产成人拍精品视频午夜网站| 欧美整片在线观看| 91亚洲一区精品| 国产精品免费一区| 亚洲综合第一页| 欧美激情成人在线视频| 日本人成精品视频在线| 91精品久久久久久久久久久| xvideos亚洲人网站| 91啪国产在线| 欧美亚洲国产另类| 欧美理论片在线观看| 国产精品美女视频网站| 欧美亚洲在线视频| 欧美高跟鞋交xxxxhd| 亚洲精品久久久久国产| 日韩高清电影免费观看完整版| 欧美日韩亚洲精品一区二区三区| 91久久精品视频| 欧美一级片在线播放| 91在线观看免费| 成人黄色在线免费| 色综合久久中文字幕综合网小说| 91av在线网站| 日韩欧美在线免费观看| 国产91精品青草社区| 中文国产成人精品久久一| 欧美日韩第一视频| 亚洲国产黄色片| 欧美精品一区在线播放| 亚洲国产精品yw在线观看| 欧美日韩中文在线观看| 亚洲第一中文字幕在线观看| 国产精品久久久久久久久久| 欧美一区二区三区免费视| 亚洲高清免费观看高清完整版| 日韩av色在线| 欧美自拍大量在线观看| 国产亚洲精品va在线观看| 精品久久久一区二区| 日韩激情av在线免费观看| 黑人巨大精品欧美一区二区三区| 亚洲国产精品电影| 国产国语videosex另类| 久久免费观看视频| 96精品久久久久中文字幕| 亚洲天堂成人在线| 欧美日韩国产精品| 91精品久久久久久久久久入口| 亚洲影视中文字幕| 成人在线免费观看视视频| 欧美中文字幕视频| 午夜精品一区二区三区在线视频| 亚洲一区二区日本| 久久精品国产综合| 亚洲qvod图片区电影| 亚洲一区二区免费| 亚洲精品720p| 92福利视频午夜1000合集在线观看| 亚洲男人天堂2024|