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 类
在这章的应用中,监听HTTP请求的任务交给了HttpConnector类,创建http请求和响应的任务交给了HttpProcessor类。HttpRequest类代表一个请求,HttpRespons代表一个响应。HttpRequest必须实现javax.servlet.http.HttpServletRequest接口。一个HttpRequest对象将会被转换成(cast)HttpServletRequest的实例然后传递给被请求的servlet的service方法。因此,每个HttpRequest的实例必须拥有合适的成员,被分配给HttpRequest的值有:URI, query string, parameters, cookies and 其他的 headers
SocketInputStream类包含两个重要的方法: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 Application
Starting the Application
ex03.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方法,启动一个线程
- <pre name="code" class="java">HttpConnector类的代码如下:</pre><pre name="code" class="java"><pre name="code" class="java">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 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();
- }
- }</pre><br>
- 我们来看看HttpProcessor的processor方法:<p></p>
- <pre></pre>
- <pre name="code" class="java"><pre name="code" class="java"> 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();
- }
- }
- </pre><br>
- <span style="font-size:24px"><strong><br>
- 创建HttpRequest对象 </strong></span>
- <pre></pre>
- <pre name="code" class="java"><span style="font-size:14px;">httpRequest类实现了javax.servlet.http.HttpServletRequest接口,类图如下:</span></pre><pre name="code" class="java"><img src="https://img-blog.csdn.net/20130903030707843?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VuamluZ3l1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
- </pre><pre name="code" class="java">HttpRequest的代码如下:<pre name="code" class="java">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 string
- String queryString = getQueryString();
- try {
- RequestUtil.parseParameters(results, queryString, encoding);
- }
- catch (UnsupportedEncodingException e) {
- ;
- }
- // Parse any parameters specified in the input stream
- String 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 results
- results.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 = "";
- else
- this.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 formats
- for (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));
- else
- return 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));
- else
- return (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);
- else
- return (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]);
- else
- return (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);
- else
- return 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 stub
- return null;
- }
- public String getLocalName() {
- // TODO Auto-generated method stub
- return null;
- }
- public int getLocalPort() {
- // TODO Auto-generated method stub
- return 0;
- }
- public int getRemotePort() {
- // TODO Auto-generated method stub
- return 0;
- }
- }
- </pre><br>
- <pre></pre>
- <pre name="code" class="java">HttpRequest类包含如下子内容:</pre><pre name="code" class="java"><ul style="margin: 0px 0px 1.5em; padding: 0px;"><li style="margin: 0px 0px 0.25em 30px; padding: 0px;">读取套接字的输入流</li><li style="margin: 0px 0px 0.25em 30px; padding: 0px;">解析请求行</li><li style="margin: 0px 0px 0.25em 30px; padding: 0px;">解析headers</li><li style="margin: 0px 0px 0.25em 30px; padding: 0px;">解析cookies</li><li style="margin: 0px 0px 0.25em 30px; padding: 0px;">获取请求参数</li></ul></pre>
- <pre></pre>
- <pre name="code" class="java"></pre><br>
- <p></p>
- <p><br>
- </p>
- </pre></pre></pre>