InputStream的封装类
package ex03.pyrmont.connector.http; import java.io.IOException; import java.io.InputStream; import java.io.EOFException; import org.apache.catalina.util.StringManager; /** * 继承于InputStream,可以在处理HTTP请求头的时候更有效率 */ public class SocketInputStream extends InputStream { // ---------------------------------------------------------解析字符串时的常量 /** * CR. 回车 */ private static final byte CR = (byte) '\r'; /** * LF.换行 */ private static final byte LF = (byte) '\n'; /** * SP.空格 */ private static final byte SP = (byte) ' '; /** * HT. Tab */ private static final byte HT = (byte) '\t'; /** * COLON. 冒号 */ private static final byte COLON = (byte) ':'; /** * Lower case offset. 小写字符转大写的偏移量 */ private static final int LC_OFFSET = 'A' - 'a'; /** * 内部缓存. */ protected byte buf[]; /** * 最大字节数. */ protected int count; /** * Position in the buffer. */ protected int pos; /** * Underlying input stream.里层的输入流 */ protected InputStream is; // ----------------------------------------------------------- Constructors /** * Construct a servlet input stream associated with the specified socket * input. * * @param is socket input stream * @param bufferSize size of the internal buffer */ public SocketInputStream(InputStream is, int bufferSize) { this.is = is; buf = new byte[bufferSize]; } // -------------------------------------------------------------- Variables /** * The string manager for this package. */ protected static StringManager sm = StringManager.getManager(Constants.Package); // ----------------------------------------------------- Instance Variables // --------------------------------------------------------- Public Methods /** * Read the request line, and copies it to the given buffer. This * function is meant to be used during the HTTP request header parsing. * Do NOT attempt to read the request body using it. * * @param requestLine Request line object * @throws IOException If an exception occurs during the underlying socket * read operations, or if the given buffer is not big enough to accomodate * the whole line. */ public void readRequestLine(HttpRequestLine requestLine) throws IOException { // Recycling check if (requestLine.methodEnd != 0) requestLine.recycle(); // Checking for a blank line int chr = 0; do { // Skipping CR or LF try { chr = read(); } catch (IOException e) { chr = -1; } } while ((chr == CR) || (chr == LF)); if (chr == -1) throw new EOFException (sm.getString("requestStream.readline.error")); pos--; // Reading the method name int maxRead = requestLine.method.length; int readStart = pos; int readCount = 0; boolean space = false; while (!space) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpRequestLine.MAX_METHOD_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(requestLine.method, 0, newBuffer, 0, maxRead); requestLine.method = newBuffer; maxRead = requestLine.method.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { int val = read(); if (val == -1) { throw new IOException (sm.getString("requestStream.readline.error")); } pos = 0; readStart = 0; } if (buf[pos] == SP) { space = true; } requestLine.method[readCount] = (char) buf[pos]; readCount++; pos++; } requestLine.methodEnd = readCount - 1; // Reading URI maxRead = requestLine.uri.length; readStart = pos; readCount = 0; space = false; boolean eol = false; while (!space) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpRequestLine.MAX_URI_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(requestLine.uri, 0, newBuffer, 0, maxRead); requestLine.uri = newBuffer; maxRead = requestLine.uri.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == SP) { space = true; } else if ((buf[pos] == CR) || (buf[pos] == LF)) { // HTTP/0.9 style request eol = true; space = true; } requestLine.uri[readCount] = (char) buf[pos]; readCount++; pos++; } requestLine.uriEnd = readCount - 1; // Reading protocol maxRead = requestLine.protocol.length; readStart = pos; readCount = 0; while (!eol) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpRequestLine.MAX_PROTOCOL_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(requestLine.protocol, 0, newBuffer, 0, maxRead); requestLine.protocol = newBuffer; maxRead = requestLine.protocol.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == CR) { // Skip CR. } else if (buf[pos] == LF) { eol = true; } else { requestLine.protocol[readCount] = (char) buf[pos]; readCount++; } pos++; } requestLine.protocolEnd = readCount; } /** * Read a header, and copies it to the given buffer. This * function is meant to be used during the HTTP request header parsing. * Do NOT attempt to read the request body using it. * * @param requestLine Request line object * @throws IOException If an exception occurs during the underlying socket * read operations, or if the given buffer is not big enough to accomodate * the whole line. */ public void readHeader(HttpHeader header) throws IOException { // Recycling check if (header.nameEnd != 0) header.recycle(); // Checking for a blank line int chr = read(); if ((chr == CR) || (chr == LF)) { // Skipping CR if (chr == CR) read(); // Skipping LF header.nameEnd = 0; header.valueEnd = 0; return; } else { pos--; } // Reading the header name int maxRead = header.name.length; int readStart = pos; int readCount = 0; boolean colon = false; while (!colon) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpHeader.MAX_NAME_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(header.name, 0, newBuffer, 0, maxRead); header.name = newBuffer; maxRead = header.name.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { int val = read(); if (val == -1) { throw new IOException (sm.getString("requestStream.readline.error")); } pos = 0; readStart = 0; } if (buf[pos] == COLON) { colon = true; } char val = (char) buf[pos]; if ((val >= 'A') && (val <= 'Z')) { val = (char) (val - LC_OFFSET); } header.name[readCount] = val; readCount++; pos++; } header.nameEnd = readCount - 1; // Reading the header value (which can be spanned over multiple lines) maxRead = header.value.length; readStart = pos; readCount = 0; int crPos = -2; boolean eol = false; boolean validLine = true; while (validLine) { boolean space = true; // Skipping spaces // Note : Only leading white spaces are removed. Trailing white // spaces are not. while (space) { // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if ((buf[pos] == SP) || (buf[pos] == HT)) { pos++; } else { space = false; } } while (!eol) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpHeader.MAX_VALUE_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(header.value, 0, newBuffer, 0, maxRead); header.value = newBuffer; maxRead = header.value.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == CR) { } else if (buf[pos] == LF) { eol = true; } else { // FIXME : Check if binary conversion is working fine int ch = buf[pos] & 0xff; header.value[readCount] = (char) ch; readCount++; } pos++; } int nextChr = read(); if ((nextChr != SP) && (nextChr != HT)) { pos--; validLine = false; } else { eol = false; // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpHeader.MAX_VALUE_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(header.value, 0, newBuffer, 0, maxRead); header.value = newBuffer; maxRead = header.value.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } header.value[readCount] = ' '; readCount++; } } header.valueEnd = readCount; } /** * Read byte. */ public int read() throws IOException { if (pos >= count) { fill(); if (pos >= count) return -1; } return buf[pos++] & 0xff; } /** * */ /* public int read(byte b[], int off, int len) throws IOException { } */ /** * */ /* public long skip(long n) throws IOException { } */ /** * Returns the number of bytes that can be read from this input * stream without blocking. */ public int available() throws IOException { return (count - pos) + is.available(); } /** * Close the input stream. */ public void close() throws IOException { if (is == null) return; is.close(); is = null; buf = null; } // ------------------------------------------------------ Protected Methods /** * Fill the internal buffer using data from the undelying input stream. */ protected void fill() throws IOException { pos = 0; count = 0; int nRead = is.read(buf, 0, buf.length); if (nRead > 0) { count = nRead; } } }
下面是连接器HttpConnector的run方法
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); } }
下面是HttpProcessor类的process方法 与第二章的架构不同的是,创建Request和Response以及解析工作都放到了HttpProcessor当中,而不是在HttpServer当中,在这个模式当中仅仅向HttpProcessor当中传入Socket对象,输入流的处理防盗了HttpProcessor当中
public void process(Socket socket) {
SocketInputStream input = null; OutputStream output = null; try {
//创建SocketInputStream对象 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(); } }
Constants.java
package ex03.pyrmont.connector.http; import java.io.File; public final class Constants { public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; public static final String Package = "ex03.pyrmont.connector.http"; public static final int DEFAULT_CONNECTION_TIMEOUT = 60000; public static final int PROCESSOR_IDLE = 0; public static final int PROCESSOR_ACTIVE = 1; }
package org.apache.catalina.util; import java.net.URLClassLoader; import java.text.MessageFormat; import java.util.Hashtable; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class StringManager { private ResourceBundle bundle; private static Log log = LogFactory.getLog(StringManager.class); private static Hashtable managers = new Hashtable(); private StringManager(String packageName) { String bundleName = packageName + ".LocalStrings"; try { this.bundle = ResourceBundle.getBundle(bundleName); return; } catch (MissingResourceException ex) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl != null) try { this.bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault(), cl); return; } catch (MissingResourceException ex2) { } if (cl == null) { cl = getClass().getClassLoader(); } if (log.isDebugEnabled()) { log.debug("Can't find resource " + bundleName + " " + cl); } if (((cl instanceof URLClassLoader)) && (log.isDebugEnabled())) log.debug(((URLClassLoader)cl).getURLs()); } } public String getString(String key) { return MessageFormat.format(getStringInternal(key), (Object[])null); } protected String getStringInternal(String key) { if (key == null) { String msg = "key is null"; throw new NullPointerException(msg); } String str = null; if (this.bundle == null) return key; try { str = this.bundle.getString(key); } catch (MissingResourceException mre) { str = "Cannot find message associated with key '" + key + "'"; } return str; } public String getString(String key, Object[] args) { String iString = null; String value = getStringInternal(key); try { Object[] nonNullArgs = args; for (int i = 0; i < args.length; i++) { if (args[i] == null) { if (nonNullArgs == args) nonNullArgs = (Object[])args.clone(); nonNullArgs[i] = "null"; } } iString = MessageFormat.format(value, nonNullArgs); } catch (IllegalArgumentException iae) { StringBuffer buf = new StringBuffer(); buf.append(value); for (int i = 0; i < args.length; i++) { buf.append(" arg[" + i + "]=" + args[i]); } iString = buf.toString(); } return iString; } public String getString(String key, Object arg) { Object[] args = { arg }; return getString(key, args); } public String getString(String key, Object arg1, Object arg2) { Object[] args = { arg1, arg2 }; return getString(key, args); } public String getString(String key, Object arg1, Object arg2, Object arg3) { Object[] args = { arg1, arg2, arg3 }; return getString(key, args); } public String getString(String key, Object arg1, Object arg2, Object arg3, Object arg4) { Object[] args = { arg1, arg2, arg3, arg4 }; return getString(key, args); } public static synchronized StringManager getManager(String packageName) { StringManager mgr = (StringManager)managers.get(packageName); if (mgr == null) { mgr = new StringManager(packageName); managers.put(packageName, mgr); } return mgr; } }
里面用到了Tomcat5中提供的StringManager类
(貌似在Tomcat7当中没有这个类了,或者更改了包的位置,目前不得而知)
作用:像Tomcat这样的大型程序必须小心的仔细处理错误消息。在Tomcat当中,错误消息对于系统管理员和Servlet程序员来说都是非常有用的。
例如:系统管理员可以很容易的根据Tomcat的错误日志消息定位到异常发生的位置。而对于servlet程序员来说,在抛出的每个javax.servlet.ServletException异常中,Tomcat都会发送一条特姝的错误消息。
这样,程序员就可以知道servlet程序到底哪里出错了
Tomcat处理错误消息的方法是将错误消息存储在一个properties文件中,便于读取和编辑,但是Tomcat中有几百个类。
若是将所有类使用的错误消息都存储在一个大的properties属性文件当中,
使用到的工具类HttpRequestLine类
package ex03.pyrmont.connector.http; /** * HTTP request line enum type. * * @author Remy Maucherat * @version $Revision: 1.6 $ $Date: 2002/03/18 07:15:40 $ * @deprecated */ final class HttpRequestLine { // -------------------------------------------------------------- Constants public static final int INITIAL_METHOD_SIZE = 8; public static final int INITIAL_URI_SIZE = 64; public static final int INITIAL_PROTOCOL_SIZE = 8; public static final int MAX_METHOD_SIZE = 1024; public static final int MAX_URI_SIZE = 32768; public static final int MAX_PROTOCOL_SIZE = 1024; // ----------------------------------------------------------- Constructors public HttpRequestLine() { this(new char[INITIAL_METHOD_SIZE], 0, new char[INITIAL_URI_SIZE], 0, new char[INITIAL_PROTOCOL_SIZE], 0); } public HttpRequestLine(char[] method, int methodEnd, char[] uri, int uriEnd, char[] protocol, int protocolEnd) { this.method = method; this.methodEnd = methodEnd; this.uri = uri; this.uriEnd = uriEnd; this.protocol = protocol; this.protocolEnd = protocolEnd; } // ----------------------------------------------------- Instance Variables public char[] method; public int methodEnd; public char[] uri; public int uriEnd; public char[] protocol; public int protocolEnd; // ------------------------------------------------------------- Properties // --------------------------------------------------------- Public Methods /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { methodEnd = 0; uriEnd = 0; protocolEnd = 0; } /** * Test if the uri includes the given char array. */ public int indexOf(char[] buf) { return indexOf(buf, buf.length); } /** * Test if the value of the header includes the given char array. */ public int indexOf(char[] buf, int end) { char firstChar = buf[0]; int pos = 0; while (pos < uriEnd) { pos = indexOf(firstChar, pos); if (pos == -1) return -1; if ((uriEnd - pos) < end) return -1; for (int i = 0; i < end; i++) { if (uri[i + pos] != buf[i]) break; if (i == (end - 1)) return pos; } pos++; } return -1; } /** * Test if the value of the header includes the given string. */ public int indexOf(String str) { return indexOf(str.toCharArray(), str.length()); } /** * Returns the index of a character in the value. */ public int indexOf(char c, int start) { for (int i = start; i < uriEnd; i++) { if (uri[i] == c) return i; } return -1; } // --------------------------------------------------------- Object Methods public int hashCode() { // FIXME return 0; } public boolean equals(Object obj) { return false; } }
//HttpProcessor.java 对SocketInputStream类的使用 这里还是没有使用多线程,估计要到第4章才会介绍使用多线程吧,此时每次只能处理一个请求,其余请求只能排队。
package ex03.pyrmont.connector.http; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import org.apache.catalina.util.RequestUtil; import org.apache.catalina.util.StringManager; import ex03.pyrmont.ServletProcessor; import ex03.pyrmont.StaticResourceProcessor; /* this class used to be called HttpServer */ public class HttpProcessor { //将HttpConnector传递进来进行回调或者说绑定 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); //为响应报文设置响应头Server 表明Server的名字 比如百度的就是bws baidu web server 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 调用SocketInputStream当中的方法初始化HttpHeader() 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 "//" in the normalized path while (true) { int index = normalized.indexOf("//"); 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); } }
//在HttpProcessor当中parseRequest的时候初始化HttpHeader对象
package ex03.pyrmont.connector.http; /** * HTTP header enum type. * * @author Remy Maucherat * @version $Revision: 1.4 $ $Date: 2002/03/18 07:15:40 $ * @deprecated */ final class HttpHeader { // -------------------------------------------------------------- Constants public static final int INITIAL_NAME_SIZE = 32; public static final int INITIAL_VALUE_SIZE = 64; public static final int MAX_NAME_SIZE = 128; public static final int MAX_VALUE_SIZE = 4096; // ----------------------------------------------------------- Constructors public HttpHeader() { this(new char[INITIAL_NAME_SIZE], 0, new char[INITIAL_VALUE_SIZE], 0); } public HttpHeader(char[] name, int nameEnd, char[] value, int valueEnd) { this.name = name; this.nameEnd = nameEnd; this.value = value; this.valueEnd = valueEnd; } public HttpHeader(String name, String value) { this.name = name.toLowerCase().toCharArray(); this.nameEnd = name.length(); this.value = value.toCharArray(); this.valueEnd = value.length(); } // ----------------------------------------------------- Instance Variables public char[] name; public int nameEnd; public char[] value; public int valueEnd; protected int hashCode = 0; // ------------------------------------------------------------- Properties // --------------------------------------------------------- Public Methods /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { nameEnd = 0; valueEnd = 0; hashCode = 0; } /** * Test if the name of the header is equal to the given char array. All the * characters must already be lower case. */ public boolean equals(char[] buf) { return equals(buf, buf.length); } /** * Test if the name of the header is equal to the given char array. All the * characters must already be lower case. */ public boolean equals(char[] buf, int end) { if (end != nameEnd) return false; for (int i = 0; i < end; i++) { if (buf[i] != name[i]) return false; } return true; } /** * Test if the name of the header is equal to the given string. The String * given must be made of lower case characters. */ public boolean equals(String str) { return equals(str.toCharArray(), str.length()); } /** * Test if the value of the header is equal to the given char array. */ public boolean valueEquals(char[] buf) { return valueEquals(buf, buf.length); } /** * Test if the value of the header is equal to the given char array. */ public boolean valueEquals(char[] buf, int end) { if (end != valueEnd) return false; for (int i = 0; i < end; i++) { if (buf[i] != value[i]) return false; } return true; } /** * Test if the value of the header is equal to the given string. */ public boolean valueEquals(String str) { return valueEquals(str.toCharArray(), str.length()); } /** * Test if the value of the header includes the given char array. */ public boolean valueIncludes(char[] buf) { return valueIncludes(buf, buf.length); } /** * Test if the value of the header includes the given char array. */ public boolean valueIncludes(char[] buf, int end) { char firstChar = buf[0]; int pos = 0; while (pos < valueEnd) { pos = valueIndexOf(firstChar, pos); if (pos == -1) return false; if ((valueEnd - pos) < end) return false; for (int i = 0; i < end; i++) { if (value[i + pos] != buf[i]) break; if (i == (end - 1)) return true; } pos++; } return false; } /** * Test if the value of the header includes the given string. */ public boolean valueIncludes(String str) { return valueIncludes(str.toCharArray(), str.length()); } /** * Returns the index of a character in the value. */ public int valueIndexOf(char c, int start) { for (int i = start; i < valueEnd; i++) { if (value[i] == c) return i; } return -1; } /** * Test if the name of the header is equal to the given header. All the * characters in the name must already be lower case. */ public boolean equals(HttpHeader header) { return (equals(header.name, header.nameEnd)); } /** * Test if the name and value of the header is equal to the given header. * All the characters in the name must already be lower case. */ public boolean headerEquals(HttpHeader header) { return (equals(header.name, header.nameEnd)) && (valueEquals(header.value, header.valueEnd)); } // --------------------------------------------------------- Object Methods /** * Return hash code. The hash code of the HttpHeader object is the same as * returned by new String(name, 0, nameEnd).hashCode(). */ public int hashCode() { int h = hashCode; if (h == 0) { int off = 0; char val[] = name; int len = nameEnd; for (int i = 0; i < len; i++) h = 31 * h + val[off++]; hashCode = h; } return h; } public boolean equals(Object obj) { if (obj instanceof String) { return equals(((String) obj).toLowerCase()); } else if (obj instanceof HttpHeader) { return equals((HttpHeader) obj); } return false; } }