您的位置:首页 > 运维架构 > Tomcat

自己动手编写tomcat服务器(三)

2013-09-03 01:45 417 查看
Catalina有两个主要的模块:connector和container,connector接收http请求,发送给container进行处理。container必须创建HttpServletRequest和HttpServletResponse的实例,然后传递给被调用的servlet的service方法。在这篇文章的应用中,connector解析HTTP请求头,并允许servlet获取headers, cookies, parameter names/values。本篇的应用由三个模块组成:connector, startup, 和core.startup模块仅包含一个类:BootStrap,它是应用的入口connector模块分为5个类别:connector 和它的支持(supporting )类(HttpConnector 和HttpProcessor )代表HTTP 请求的类(HttpRequest )及 其支持类代表HTTP 响应的类(HttpResponse )及其支持类门面(Facade )类(HttpRequestFacade 和HttpResponseFacade )Constant 类core模块包含两个类: ServletProcessor and StaticResourceProcessor在这章的应用中,监听HTTP请求的任务交给了HttpConnector类,创建http请求和响应的任务交给了HttpProcessor类。HttpRequest类代表一个请求,HttpRespons代表一个响应。HttpRequest必须实现javax.servlet.http.HttpServletRequest接口。一个HttpRequest对象将会被转换成(cast)HttpServletRequest的实例然后传递给被请求的servlet的service方法。因此,每个HttpRequest的实例必须拥有合适的成员,被分配给HttpRequest的值有:URI,query string, parameters, cookies and 其他的 headersSocketInputStream类包含两个重要的方法:readRequestLine和readHeader。readRequestLine返回请求字符串的第一行,readHeader用来获取名值对。本篇的应用包含如下的结构: Starting the Application The Connector Creating an HttpRequest Object Creating an HttpResponse Object Static resource processor and servlet processor Running the ApplicationStarting the Applicationex03.pyrmont.startup.Bootstrap类为起点类,源代码如下:
package ex03.pyrmont.startup;

import ex03.pyrmont.connector.http.HttpConnector;

public final class Bootstrap {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
connector.start();
}
}
HttpConnector类的main方法实例化一个HttpConnector,然后调用它的start方法,启动一个线程
HttpConnector类的代码如下:
package ex03.pyrmont.connector.http;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 socketSocket socket = null;try {socket = serverSocket.accept();}catch (Exception e) {continue;}// Hand this socket off to an HttpProcessorHttpProcessor processor = new HttpProcessor(this);processor.process(socket);}}public void start() {Thread thread = new Thread(this);thread.start();}}
我们来看看HttpProcessor的processor方法:
 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 parserequest = new HttpRequest(input);// create HttpResponse objectresponse = 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 socketsocket.close();// no shutdown for this application}catch (Exception e) {e.printStackTrace();}}
创建HttpRequest对象
httpRequest类实现了javax.servlet.http.HttpServletRequest接口,类图如下:
HttpRequest的代码如下:package ex03.pyrmont.connector.http;/** this class copies methods from org.apache.catalina.connector.HttpRequestBase*  and org.apache.catalina.connector.http.HttpRequestImpl.*  The HttpRequestImpl class employs a pool of HttpHeader objects for performance*  These two classes will be explained in Chapter 4.*/import ex03.pyrmont.connector.RequestStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import javax.servlet.http.Cookie;import javax.servlet.RequestDispatcher;import javax.servlet.ServletInputStream;import java.security.Principal;import java.io.InputStream;import java.io.InputStreamReader;import java.io.IOException;import java.io.BufferedReader;import java.io.UnsupportedEncodingException;import java.net.InetAddress;import java.net.Socket;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.Enumeration;import java.util.HashMap;import java.util.Locale;import java.util.Map;import org.apache.catalina.util.Enumerator;import org.apache.catalina.util.ParameterMap;import org.apache.catalina.util.RequestUtil;public class HttpRequest implements HttpServletRequest {private String contentType;private int contentLength;private InetAddress inetAddress;private InputStream input;private String method;private String protocol;private String queryString;private String requestURI;private String serverName;private int serverPort;private Socket socket;private boolean requestedSessionCookie;private String requestedSessionId;private boolean requestedSessionURL;/*** The request attributes for this request.*/protected HashMap attributes = new HashMap();/*** The authorization credentials sent with this Request.*/protected String authorization = null;/*** The context path for this request.*/protected String contextPath = "";/*** The set of cookies associated with this Request.*/protected ArrayList cookies = new ArrayList();/*** An empty collection to use for returning empty Enumerations.  Do not* add any elements to this collection!*/protected static ArrayList empty = new ArrayList();/*** The set of SimpleDateFormat formats to use in getDateHeader().*/protected SimpleDateFormat formats[] = {new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)};/*** The HTTP headers associated with this Request, keyed by name.  The* values are ArrayLists of the corresponding header values.*/protected HashMap headers = new HashMap();/*** The parsed parameters for this request.  This is populated only if* parameter information is requested via one of the* <code>getParameter()</code> family of method calls.  The key is the* parameter name, while the value is a String array of values for this* parameter.* <p>* <strong>IMPLEMENTATION NOTE</strong> - Once the parameters for a* particular request are parsed and stored here, they are not modified.* Therefore, application level access to the parameters need not be* synchronized.*/protected ParameterMap parameters = null;/*** Have the parameters for this request been parsed yet?*/protected boolean parsed = false;protected String pathInfo = null;/*** The reader that has been returned by <code>getReader</code>, if any.*/protected BufferedReader reader = null;/*** The ServletInputStream that has been returned by* <code>getInputStream()</code>, if any.*/protected ServletInputStream stream = null;public HttpRequest(InputStream input) {this.input = input;}public void addHeader(String name, String value) {name = name.toLowerCase();synchronized (headers) {ArrayList values = (ArrayList) headers.get(name);if (values == null) {values = new ArrayList();headers.put(name, values);}values.add(value);}}/*** Parse the parameters of this request, if it has not already occurred.* If parameters are present in both the query string and the request* content, they are merged.*/protected void parseParameters() {if (parsed)return;ParameterMap results = parameters;if (results == null)results = new ParameterMap();results.setLocked(false);String encoding = getCharacterEncoding();if (encoding == null)encoding = "ISO-8859-1";// Parse any parameters specified in the query stringString queryString = getQueryString();try {RequestUtil.parseParameters(results, queryString, encoding);}catch (UnsupportedEncodingException e) {;}// Parse any parameters specified in the input streamString contentType = getContentType();if (contentType == null)contentType = "";int semicolon = contentType.indexOf(';');if (semicolon >= 0) {contentType = contentType.substring(0, semicolon).trim();}else {contentType = contentType.trim();}if ("POST".equals(getMethod()) && (getContentLength() > 0)&& "application/x-www-form-urlencoded".equals(contentType)) {try {int max = getContentLength();int len = 0;byte buf[] = new byte[getContentLength()];ServletInputStream is = getInputStream();while (len < max) {int next = is.read(buf, len, max - len);if (next < 0 ) {break;}len += next;}is.close();if (len < max) {throw new RuntimeException("Content length mismatch");}RequestUtil.parseParameters(results, buf, encoding);}catch (UnsupportedEncodingException ue) {;}catch (IOException e) {throw new RuntimeException("Content read fail");}}// Store the final resultsresults.setLocked(true);parsed = true;parameters = results;}public void addCookie(Cookie cookie) {synchronized (cookies) {cookies.add(cookie);}}/*** Create and return a ServletInputStream to read the content* associated with this Request.  The default implementation creates an* instance of RequestStream associated with this request, but this can* be overridden if necessary.** @exception IOException if an input/output error occurs*/public ServletInputStream createInputStream() throws IOException {return (new RequestStream(this));}public InputStream getStream() {return input;}public void setContentLength(int length) {this.contentLength = length;}public void setContentType(String type) {this.contentType = type;}public void setInet(InetAddress inetAddress) {this.inetAddress = inetAddress;}public void setContextPath(String path) {if (path == null)this.contextPath = "";elsethis.contextPath = path;}public void setMethod(String method) {this.method = method;}public void setPathInfo(String path) {this.pathInfo = path;}public void setProtocol(String protocol) {this.protocol = protocol;}public void setQueryString(String queryString) {this.queryString = queryString;}public void setRequestURI(String requestURI) {this.requestURI = requestURI;}/*** Set the name of the server (virtual host) to process this request.** @param name The server name*/public void setServerName(String name) {this.serverName = name;}/*** Set the port number of the server to process this request.** @param port The server port*/public void setServerPort(int port) {this.serverPort = port;}public void setSocket(Socket socket) {this.socket = socket;}/*** Set a flag indicating whether or not the requested session ID for this* request came in through a cookie.  This is normally called by the* HTTP Connector, when it parses the request headers.** @param flag The new flag*/public void setRequestedSessionCookie(boolean flag) {this.requestedSessionCookie = flag;}public void setRequestedSessionId(String requestedSessionId) {this.requestedSessionId = requestedSessionId;}public void setRequestedSessionURL(boolean flag) {requestedSessionURL = flag;}/* implementation of the HttpServletRequest*/public Object getAttribute(String name) {synchronized (attributes) {return (attributes.get(name));}}public Enumeration getAttributeNames() {synchronized (attributes) {return (new Enumerator(attributes.keySet()));}}public String getAuthType() {return null;}public String getCharacterEncoding() {return null;}public int getContentLength() {return contentLength ;}public String getContentType() {return contentType;}public String getContextPath() {return contextPath;}public Cookie[] getCookies() {synchronized (cookies) {if (cookies.size() < 1)return (null);Cookie results[] = new Cookie[cookies.size()];return ((Cookie[]) cookies.toArray(results));}}public long getDateHeader(String name) {String value = getHeader(name);if (value == null)return (-1L);// Work around a bug in SimpleDateFormat in pre-JDK1.2b4// (Bug Parade bug #4106807)value += " ";// Attempt to convert the date header in a variety of formatsfor (int i = 0; i < formats.length; i++) {try {Date date = formats[i].parse(value);return (date.getTime());}catch (ParseException e) {;}}throw new IllegalArgumentException(value);}public String getHeader(String name) {name = name.toLowerCase();synchronized (headers) {ArrayList values = (ArrayList) headers.get(name);if (values != null)return ((String) values.get(0));elsereturn null;}}public Enumeration getHeaderNames() {synchronized (headers) {return (new Enumerator(headers.keySet()));}}public Enumeration getHeaders(String name) {name = name.toLowerCase();synchronized (headers) {ArrayList values = (ArrayList) headers.get(name);if (values != null)return (new Enumerator(values));elsereturn (new Enumerator(empty));}}public ServletInputStream getInputStream() throws IOException {if (reader != null)throw new IllegalStateException("getInputStream has been called");if (stream == null)stream = createInputStream();return (stream);}public int getIntHeader(String name) {String value = getHeader(name);if (value == null)return (-1);elsereturn (Integer.parseInt(value));}public Locale getLocale() {return null;}public Enumeration getLocales() {return null;}public String getMethod() {return method;}public String getParameter(String name) {parseParameters();String values[] = (String[]) parameters.get(name);if (values != null)return (values[0]);elsereturn (null);}public Map getParameterMap() {parseParameters();return (this.parameters);}public Enumeration getParameterNames() {parseParameters();return (new Enumerator(parameters.keySet()));}public String[] getParameterValues(String name) {parseParameters();String values[] = (String[]) parameters.get(name);if (values != null)return (values);elsereturn null;}public String getPathInfo() {return pathInfo;}public String getPathTranslated() {return null;}public String getProtocol() {return protocol;}public String getQueryString() {return queryString;}public BufferedReader getReader() throws IOException {if (stream != null)throw new IllegalStateException("getInputStream has been called.");if (reader == null) {String encoding = getCharacterEncoding();if (encoding == null)encoding = "ISO-8859-1";InputStreamReader isr =new InputStreamReader(createInputStream(), encoding);reader = new BufferedReader(isr);}return (reader);}public String getRealPath(String path) {return null;}public String getRemoteAddr() {return null;}public String getRemoteHost() {return null;}public String getRemoteUser() {return null;}public RequestDispatcher getRequestDispatcher(String path) {return null;}public String getScheme() {return null;}public String getServerName() {return null;}public int getServerPort() {return 0;}public String getRequestedSessionId() {return null;}public String getRequestURI() {return requestURI;}public StringBuffer getRequestURL() {return null;}public HttpSession getSession() {return null;}public HttpSession getSession(boolean create) {return null;}public String getServletPath() {return null;}public Principal getUserPrincipal() {return null;}public boolean isRequestedSessionIdFromCookie() {return false;}public boolean isRequestedSessionIdFromUrl() {return isRequestedSessionIdFromURL();}public boolean isRequestedSessionIdFromURL() {return false;}public boolean isRequestedSessionIdValid() {return false;}public boolean isSecure() {return false;}public boolean isUserInRole(String role) {return false;}public void removeAttribute(String attribute) {}public void setAttribute(String key, Object value) {}/*** Set the authorization credentials sent with this request.** @param authorization The new authorization credentials*/public void setAuthorization(String authorization) {this.authorization = authorization;}public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException {}public String getLocalAddr() {// TODO Auto-generated method stubreturn null;}public String getLocalName() {// TODO Auto-generated method stubreturn null;}public int getLocalPort() {// TODO Auto-generated method stubreturn 0;}public int getRemotePort() {// TODO Auto-generated method stubreturn 0;}}
HttpRequest类包含如下子内容:
读取套接字的输入流解析请求行解析headers解析cookies获取请求参数

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: