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;
}
}