这一小节需要掌握两个函数。
1,首先先来看看parseConnection()方法:解析连接。下面来看看Tomcat的源码实现:
org.apache.catalina.connector.http.HttpProcessor
/**
* Parse and record the connection parameters related to this request.
*
* @param socket The socket on which we are connected
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a parsing error occurs
*/
private void parseConnection(Socket socket)
throws IOException, ServletException {
if (debug >= 2)
log(" parseConnection: address=" + socket.getInetAddress() +
", port=" + connector.getPort());
((HttpRequestImpl) request).setInet(socket.getInetAddress());
if (proxyPort != 0)
request.setServerPort(proxyPort);
else
request.setServerPort(serverPort);
request.setSocket(socket);
}
上面很清楚,做了三件事,首先是从套接字中获取Internet地址,将其赋值给HttpRequestImpl对象,此外,它还要检查是否使用了代理,最后将Socket对象赋值给request对象。
但是我对于上面的代码有一个小疑问,就是 ((HttpRequestImpl) request).setInet(socket.getInetAddress())这一句,request实例它就是HttpRequestImpl类型的了,为什么还要做一次转型呢???????????
2,再来看看parseRequest()函数,解析请求。
/**
* Parse the incoming HTTP request and set the corresponding HTTP request
* properties.
*
* @param input The input stream attached to our socket
* @param output The output stream of the socket
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a parsing error occurs
*/
private void parseRequest(SocketInputStream input, OutputStream output)
throws IOException, ServletException {
// Parse the incoming request line
input.readRequestLine(requestLine);//读取一行请求,这个方法在后面会详细讨论它
// When the previous method returns, we're actually processing a
// request
status = Constants.PROCESSOR_ACTIVE;
String method =
new String(requestLine.method, 0, requestLine.methodEnd);//获得提交的方法
String uri = null;//uri
String protocol = new String(requestLine.protocol, 0,
requestLine.protocolEnd);//获得提交的协议版本信息
//System.out.println(" Method:" + method + "_ Uri:" + uri
// + "_ Protocol:" + protocol);
if (protocol.length() == 0)//如果协议版本长度为0,那么protocol设置为0.9
protocol = "HTTP/0.9";
// Now check if the connection should be kept alive after parsing the
// request.
if ( protocol.equals("HTTP/1.1") ) {//如果protocol为HTTP/1.1,那么将http11标志设置为true
http11 = true;
sendAck = false;
} else {
http11 = false;
sendAck = false;
// For HTTP/1.0, connection are not persistent by default,
// unless specified with a Connection: Keep-Alive header.
keepAlive = false;//非HTTP/1.1不支持持久性连接
}
// Validate the incoming request line
if (method.length() < 1) {
throw new ServletException
(sm.getString("httpProcessor.parseRequest.method"));
} else if (requestLine.uriEnd < 1) {
throw new ServletException
(sm.getString("httpProcessor.parseRequest.uri"));
}
// Parse any query parameters out of the request URI
//HttpProcess类的process()方法会调用私有方法parseRequest()方法来解析请求行,即HTTP请求的第1行内容。例如一个HTTP请求行的实例:
//GET /myApp/Modernservlet?userName=tarzan&password=pwd HTTP/1.1
//请求行的第2部分是URL加上一个可选的查询字符串。在这个例子是,URL是:
// /myApp/Modernservlet
//问号后面的部分就是查询字符串,如下所示: userName=tarzan&password=pwd ----------这些都是从表单提交过来的
int question = requestLine.indexOf("?");//定位到?的位置
if (question >= 0) {
request.setQueryString
(new String(requestLine.uri, question + 1,
requestLine.uriEnd - question - 1));//获取查询字符串
if (debug >= 1)
log(" Query string is " +
((HttpServletRequest) request.getRequest())
.getQueryString());
uri = new String(requestLine.uri, 0, question);//设置uri
} else {
request.setQueryString(null);//没有查询字符串则置空
uri = new String(requestLine.uri, 0, requestLine.uriEnd);//设置uri
}
// Checking for an absolute URI (with the HTTP protocol)
//还有一种以 http://www.brainysoftware.com/index.html?name=Tarzan的这种请求方式
if (!uri.startsWith("/")) {
int pos = uri.indexOf("://");//获得://中":"的位置
// Parsing out protocol and host name
if (pos != -1) {
pos = uri.indexOf('/', pos + 3);//执行完这一步获得符号"/"的位置
if (pos == -1) {//如果pos==-1
uri = "";//uri设置为空
} else {
uri = uri.substring(pos);//否则uri此时变为http://www.brainsoftware.com/
}
}
}
// Parse any requested session ID out of the request URI
//然后,查询字符串可能也会报行一个会话标识符,参数名为jessionid。因此,parseRequest()方法还要检查是否包含会话标识符,若在查询字符串中包含jessionid,
//则parseRequest()方法要获取jsessionid的值,并调用HttpRequest类的setRequestedSessionId()方法来填充HttpRequest实例
//match = ";jsessionid="
int semicolon = uri.indexOf(match);//匹配该字符串
if (semicolon >= 0) {//如果匹配成功
String rest = uri.substring(semicolon + match.length());//将提取出字符串中包含jsessionid的那一段字符串
int semicolon2 = rest.indexOf(';');
if (semicolon2 >= 0) {
request.setRequestedSessionId(rest.substring(0, semicolon2));//设置session的ID
rest = rest.substring(semicolon2);//提取sessionId后面的字符串
} else {
request.setRequestedSessionId(rest);//sessionId后面没有带其他字符串那么直接将rest赋值给seesionId
rest = "";
}
request.setRequestedSessionURL(true);
uri = uri.substring(0, semicolon) + rest;//设置uri
if (debug >= 1)
log(" Requested URL session id is " +
((HttpServletRequest) request.getRequest())
.getRequestedSessionId());
} else {
request.setRequestedSessionId(null);//如果没有session,则ID设置为空
request.setRequestedSessionURL(false);//表示木有session
}
// Normalize URI (using String operations at the moment)
将字符串进行拨乱反正。例如,出现"\"的地方会被替换为"/"。若URI本身是正常的,或不正常的地方可以修正,则normalize()方法
//会返回相同的URI或修正过的URI。若URI无法修正,则会认为它是无效的。normalize()方法返回null。在这种情况下(normalize()方法返回null),parseRequest()
//方法会在方法的末尾抛出异常
String normalizedUri = normalize(uri);
if (debug >= 1)
log("Normalized: '" + uri + "' to '" + normalizedUri + "'");
// Set the corresponding request properties
((HttpRequest) request).setMethod(method);//填充获取到的方法
request.setProtocol(protocol);//填充获取的到的协议版本
if (normalizedUri != null) {
((HttpRequest) request).setRequestURI(normalizedUri);//填充URI
} else {
((HttpRequest) request).setRequestURI(uri);
}
request.setSecure(connector.getSecure());//填充连接器的安全信息
request.setScheme(connector.getScheme());//填充连接器的版本
if (normalizedUri == null) {
log(" Invalid request URI: '" + uri + "'");
throw new ServletException("Invalid URI: " + uri + "'");
}
if (debug >= 1)
log(" Request is '" + method + "' for '" + uri +
"' with protocol '" + protocol + "'");
}
接下来就是苦力活的readRequestLine()还有normalize()方法,请看下一节的分析.........
1,首先先来看看parseConnection()方法:解析连接。下面来看看Tomcat的源码实现:
org.apache.catalina.connector.http.HttpProcessor
/**
* Parse and record the connection parameters related to this request.
*
* @param socket The socket on which we are connected
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a parsing error occurs
*/
private void parseConnection(Socket socket)
throws IOException, ServletException {
if (debug >= 2)
log(" parseConnection: address=" + socket.getInetAddress() +
", port=" + connector.getPort());
((HttpRequestImpl) request).setInet(socket.getInetAddress());
if (proxyPort != 0)
request.setServerPort(proxyPort);
else
request.setServerPort(serverPort);
request.setSocket(socket);
}
上面很清楚,做了三件事,首先是从套接字中获取Internet地址,将其赋值给HttpRequestImpl对象,此外,它还要检查是否使用了代理,最后将Socket对象赋值给request对象。
但是我对于上面的代码有一个小疑问,就是 ((HttpRequestImpl) request).setInet(socket.getInetAddress())这一句,request实例它就是HttpRequestImpl类型的了,为什么还要做一次转型呢???????????
2,再来看看parseRequest()函数,解析请求。
/**
* Parse the incoming HTTP request and set the corresponding HTTP request
* properties.
*
* @param input The input stream attached to our socket
* @param output The output stream of the socket
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a parsing error occurs
*/
private void parseRequest(SocketInputStream input, OutputStream output)
throws IOException, ServletException {
// Parse the incoming request line
input.readRequestLine(requestLine);//读取一行请求,这个方法在后面会详细讨论它
// When the previous method returns, we're actually processing a
// request
status = Constants.PROCESSOR_ACTIVE;
String method =
new String(requestLine.method, 0, requestLine.methodEnd);//获得提交的方法
String uri = null;//uri
String protocol = new String(requestLine.protocol, 0,
requestLine.protocolEnd);//获得提交的协议版本信息
//System.out.println(" Method:" + method + "_ Uri:" + uri
// + "_ Protocol:" + protocol);
if (protocol.length() == 0)//如果协议版本长度为0,那么protocol设置为0.9
protocol = "HTTP/0.9";
// Now check if the connection should be kept alive after parsing the
// request.
if ( protocol.equals("HTTP/1.1") ) {//如果protocol为HTTP/1.1,那么将http11标志设置为true
http11 = true;
sendAck = false;
} else {
http11 = false;
sendAck = false;
// For HTTP/1.0, connection are not persistent by default,
// unless specified with a Connection: Keep-Alive header.
keepAlive = false;//非HTTP/1.1不支持持久性连接
}
// Validate the incoming request line
if (method.length() < 1) {
throw new ServletException
(sm.getString("httpProcessor.parseRequest.method"));
} else if (requestLine.uriEnd < 1) {
throw new ServletException
(sm.getString("httpProcessor.parseRequest.uri"));
}
// Parse any query parameters out of the request URI
//HttpProcess类的process()方法会调用私有方法parseRequest()方法来解析请求行,即HTTP请求的第1行内容。例如一个HTTP请求行的实例:
//GET /myApp/Modernservlet?userName=tarzan&password=pwd HTTP/1.1
//请求行的第2部分是URL加上一个可选的查询字符串。在这个例子是,URL是:
// /myApp/Modernservlet
//问号后面的部分就是查询字符串,如下所示: userName=tarzan&password=pwd ----------这些都是从表单提交过来的
int question = requestLine.indexOf("?");//定位到?的位置
if (question >= 0) {
request.setQueryString
(new String(requestLine.uri, question + 1,
requestLine.uriEnd - question - 1));//获取查询字符串
if (debug >= 1)
log(" Query string is " +
((HttpServletRequest) request.getRequest())
.getQueryString());
uri = new String(requestLine.uri, 0, question);//设置uri
} else {
request.setQueryString(null);//没有查询字符串则置空
uri = new String(requestLine.uri, 0, requestLine.uriEnd);//设置uri
}
// Checking for an absolute URI (with the HTTP protocol)
//还有一种以 http://www.brainysoftware.com/index.html?name=Tarzan的这种请求方式
if (!uri.startsWith("/")) {
int pos = uri.indexOf("://");//获得://中":"的位置
// Parsing out protocol and host name
if (pos != -1) {
pos = uri.indexOf('/', pos + 3);//执行完这一步获得符号"/"的位置
if (pos == -1) {//如果pos==-1
uri = "";//uri设置为空
} else {
uri = uri.substring(pos);//否则uri此时变为http://www.brainsoftware.com/
}
}
}
// Parse any requested session ID out of the request URI
//然后,查询字符串可能也会报行一个会话标识符,参数名为jessionid。因此,parseRequest()方法还要检查是否包含会话标识符,若在查询字符串中包含jessionid,
//则parseRequest()方法要获取jsessionid的值,并调用HttpRequest类的setRequestedSessionId()方法来填充HttpRequest实例
//match = ";jsessionid="
int semicolon = uri.indexOf(match);//匹配该字符串
if (semicolon >= 0) {//如果匹配成功
String rest = uri.substring(semicolon + match.length());//将提取出字符串中包含jsessionid的那一段字符串
int semicolon2 = rest.indexOf(';');
if (semicolon2 >= 0) {
request.setRequestedSessionId(rest.substring(0, semicolon2));//设置session的ID
rest = rest.substring(semicolon2);//提取sessionId后面的字符串
} else {
request.setRequestedSessionId(rest);//sessionId后面没有带其他字符串那么直接将rest赋值给seesionId
rest = "";
}
request.setRequestedSessionURL(true);
uri = uri.substring(0, semicolon) + rest;//设置uri
if (debug >= 1)
log(" Requested URL session id is " +
((HttpServletRequest) request.getRequest())
.getRequestedSessionId());
} else {
request.setRequestedSessionId(null);//如果没有session,则ID设置为空
request.setRequestedSessionURL(false);//表示木有session
}
// Normalize URI (using String operations at the moment)
将字符串进行拨乱反正。例如,出现"\"的地方会被替换为"/"。若URI本身是正常的,或不正常的地方可以修正,则normalize()方法
//会返回相同的URI或修正过的URI。若URI无法修正,则会认为它是无效的。normalize()方法返回null。在这种情况下(normalize()方法返回null),parseRequest()
//方法会在方法的末尾抛出异常
String normalizedUri = normalize(uri);
if (debug >= 1)
log("Normalized: '" + uri + "' to '" + normalizedUri + "'");
// Set the corresponding request properties
((HttpRequest) request).setMethod(method);//填充获取到的方法
request.setProtocol(protocol);//填充获取的到的协议版本
if (normalizedUri != null) {
((HttpRequest) request).setRequestURI(normalizedUri);//填充URI
} else {
((HttpRequest) request).setRequestURI(uri);
}
request.setSecure(connector.getSecure());//填充连接器的安全信息
request.setScheme(connector.getScheme());//填充连接器的版本
if (normalizedUri == null) {
log(" Invalid request URI: '" + uri + "'");
throw new ServletException("Invalid URI: " + uri + "'");
}
if (debug >= 1)
log(" Request is '" + method + "' for '" + uri +
"' with protocol '" + protocol + "'");
}
接下来就是苦力活的readRequestLine()还有normalize()方法,请看下一节的分析.........