Coder 爱翻译 How Tomcat Works 第四章 第二部分

Request Objects

在默认的连接器中org.apache.catalina.Request接口代表了一个HTTP请求对象。HttpRequest的父类—RequestBase类直接实现了这个接口。最终的实现是HttpRequestImpl,它继承HttpRequest。在第三章中,有facade类:RequstFacade和HttpRequestFacade。

Coder 爱翻译 How Tomcat Works 第四章 第二部分_第1张图片

Response Objects

Coder 爱翻译 How Tomcat Works 第四章 第二部分_第2张图片

Processing Requests

现在,你已经了解了request、response对象和HttpConnector对象怎么创建它们。这部分我们关注HttpProcessor类的process方法,这个方法是HttpProcessor类的被指配了一个socket后的run方法里调用。这个process方法处理一下工作:

 解析连接
 解析请求
 解析头部信息

process方法使用boolean变量ok来表示在处理过程中是否出错和使用boolean变量finishResponse来表示Response接口的finishResponse方法应该被调用。
boolean ok = true; 
boolean finishResponse = true;

此外,http11.keepAlive表明了这个连接时持久的,stopped表明HttpProcessor实例连接器
process方法也应该停止,http11表明一个web客户端到来HTTP请求从支持HTTP1.1。

象第三章,一个SocketInputStream实例被用来包装socket的输入流。注意:SocketInputStream的构造函数也传递从连接器得到的buffer size,不是从HttpProcessor类的一个局部变量得到的。这是因为HttpProcessor不能被默认连接器的用户访问。通过把buffer size放进Connector接口,这就允许任何一个人使用连接器来设置buffer size。
OutputStream output = null; 
// Construct and initialize the objects we will need 
try { 
    input = new SocketInputStream(socket.getInputstream(), 
      connector.getBufferSize()); 
} 
catch (Exception e) { 
ok = false;
}

然后,有一个while循环保持读取输入流,知道HttpProcessor停止、一个异常抛出或者是连接被关闭。
  keepAlive = true; 
   while (!stopped && ok && keepAlive) { 
     ... 
   }

在这个while循环里,process方法在finishResponse被设为true、获取输出流、处理一些request和resposne对象的初始化后开始执行。
finishResponse = true; 
     try { 
       request.setStream(input); 
       request.setResponse(response); 
       output = socket.getOutputStream(); 
       response.setStream(output); 
       response.setRequest(request); 
       ((HttpServletResponse) response.getResponse()).setHeader 
         ("Server", SERVER_INFO); 
     }  catch (Exception e) { 
       log("process.create", e);  //logging is discussed in Chapter 7 
       ok = false; 
     }

后来,process方法通过调用parseConnection,parseRequest和parseHeader方法来解析到来的HTTP请求。
 try { 
       if (ok) { 
         parseConnection(socket); 
         parseRequest(input, output); 
         if (!request.getRequest().getProtocol() 
           .startsWith("HTTP/0")) 
           parseHeaders(input);

parseConnection方法获取协议的值。可以使:HTTP0.9,HTTP1.0或者是HTTP1.1。如果协议是HTTP1.0,keepAlive的boolean值设置成false,因为HTTP1.0不支持持久连接。parseHeader方法设置sendAck的boolean值为true(但在100-continue头信息被HTTP请求发现除外)

如果协议时HTTP1.1,如果web客户端发送了100—continue头部信息,它还要检查块编码是否允许。
if (http11) { 
       // Sending a request acknowledge back to the client if 
       // requested. 
       ackRequest(output); 
       // If the protocol is HTTP/1.1, chunking is allowed. 
       if (connector.isChunkingAllowed()) 
         response.setAllowChunking(true); 
     }

AckRequest方法检查sendAck的值和在sendAckweitrue时发送下面的字符串:

HTTP/1.1 100 Continue\r\n\r\n

在解析HTTP请求期间,可能会抛出异常。任何异常将设置ok或finishResponse为false。在解析后,process方法把request和response对象传递给容器的invoke方法。
  try { 
       ((HttpServletResponse) response).setHeader 
         ("Date", FastHttpDateFormat.getCurrentDate()); 
       if (ok) { 
         connector.getContainer().invoke(request, response); 
       } 
     }

最后,如果finishResponse任然为true,调用response对象的finishResponse方法和request对象的finishRequest方法,把输出flush。
 if (finishResponse) { 
       ... 
       response.finishResponse(); 
       ... 
       request.finishRequest(); 
       ... 
       output.flush();

while循环的最后一部分是检查response的Connection头部信息在servlet或HTTP1.0协议中是否被设置成colse。如果是这种情况,keepAlive被设置成false。同样的,request和response对象被回收。
  if ( "close".equals(response.getHeader("Connection")) ) { 
       keepAlive = false; 
     } 
     // End of request processing 
     status = Constants.PROCESSOR_IDLE; 
     // Recycling the request and the response objects 
     request.recycle(); 
     response.recycle();
}

While循环如果keepAlive是true,在之前的解析和容器的invoke方法没有错误,或者HttpProcessor实例没有停止。shutdownInput方法被调用,关闭socket。
try { 
shutdownInput(input); 
socket.close(); 
} 
...

shutdownInput方法检查是否有没有读取的字节。如果有,跳过这些字节。

Parsing the Connection

parseConnection方法包含来自socket的Internet地址,并把它指配给HttpRequestImpl对象。它也检查代理是否被使用,并把它指配给request对象。
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); 
}


Parsing the Request

parseRequest方法跟第三章的方法相似。

Parsing Headers

在默认连接器的parseHeaders方法使用org.apache.catalina.connector.http包中的HttpHeader和DefaultHeaders类。HttpHeader类代表一个HTTP请求头部信息。第三章使用字符串。HttpHeader类使用字符数组来避免字符串操作的开销。DefaultHeaders类是一个final类,它在字符数组中包含标准的HTTP请求头部信息。
static final char[] AUTHORIZATION_NAME = 
    "authorization".toCharArray(); 
static final char[] ACCEPT_LANGUAGE_NAME = 
    "accept-language".toCharArray(); 
static final char[] COOKIE_NAME = "cookie".toCharArray(); 
...

parseHeaders方法包含一个while循环,保持读取HTTP请求,直到没有更多的头部信息可读取。while循环调用request对象allocateHeader方法来获取一个空的HttpHeader实例后开始。这个实例被传递给SocketInputStream的readHeader方法。
  HttpHeader header = request.allocateHeader(); 
 
     // Read the next header 
     input.readHeader(header);

如果所有头部信息被读取,readHeader方法
   if (header.nameEnd == 0) { 
     if (header.valueEnd == 0) { 
       return; 
     } 
     else { 
       throw new ServletException 
         (sm.getString("httpProcessor.parseHeaders.colon")); 
     } 
   }

如果有一个header name,这里就会有一个header value与之对应。

String value = new String(header.value, 0, header.valueEnd);

接下来,就象第三章,parseHeaders方法让header name和在DefaultHeaders的标准name相比较。
注意:是在两个字符数组比较,而不是字符串。
  if (header.equals(DefaultHeaders.AUTHORIZATION_NAME)) { 
       request.setAuthorization(value); 
     } 
     else if (header.equals(DefaultHeaders.ACCEPT_LANGUAGE_NAME)) { 
       parseAcceptLanguage(value); 
     } 
     else if (header.equals(DefaultHeaders.COOKIE_NAME)) { 
       // parse cookie 
     } 
     else if (header.equals(DefaultHeaders.CONTENT_LENGTH_NAME)) { 
       // get content length 
     } 
     else if (header.equals(DefaultHeaders.CONTENT_TYPE_NAME)) { 
         request.setContentType(value); 
     } 
     else if (header.equals(DefaultHeaders.HOST_NAME)) { 
       // get host name 
     } 
     else if (header.equals(DefaultHeaders.CONNECTION_NAME)) {
  if (header.valueEquals(DefaultHeaders.CONNECTION_CLOSE_VALUE)) { 
         keepAlive = false; 
         response.setHeader("Connection", "close"); 
       }
  } 
     else if (header.equals(DefaultHeaders.EXPECT_NAME)) { 
       if (header.valueEquals(DefaultHeaders.EXPECT_100_VALUE)) 
         sendAck = true; 
       else 
         throw new ServletException(sm.getstring 
           ("httpProcessor.parseHeaders.unknownExpectation")); 
     } 
      else if (header.equals(DefaultHeaders.TRANSFER_ENCODING_NAME)) { 
       //request.setTransferEncoding(header); 
     } 
 
     request.nextHeader();


The Simple Container Application

ex04.pyrmont.core.SimpleContainer和ex04 pyrmont.startup.Bootstrap。

Listing 4.3: The SimpleContainer class   
 
package ex04.pyrmont.core; 

import java.beans.PropertyChangeListener; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.net.URLStreamHandler; 
import java.io.File; 
import java.io.IOException; 
import javax.naming.directory.DirContext; 
import javax.servlet.Servlet; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import org.apache.catalina.Cluster;
import org.apache.catalina.Container; 
import org.apache.catalina.ContainerListener; 
import org.apache.catalina.Loader; 
import org.apache.catalina.Logger; 
import org.apache.catalina.Manager; 
import org.apache.catalina.Mapper; 
import org.apache.catalina.Realm; 
import org.apache.catalina.Request; 
import org.apache.catalina.Response; 
 
public class SimpleContainer implements Container { 
 
   public static final String WEB_ROOT = 
     System.getProperty("user.dir") + File.separator + "webroot"; 
 
   public SimpleContainer() {  } 
   public String getInfo() {
  return null; 
   } 
   public Loader getLoader() { 
     return null; 
   } 
   public void setLoader(Loader loader) {  } 
   public Logger getLogger() { 
     return null; 
   } 
   public void setLogger(Logger logger) {  } 
   public Manager getManager() { 
     return null; 
   }
  public void setCluster(Cluster cluster) {  } 
 
   public String getName() { 
     return null; 
   } 
   public void setName(String name) {  } 
   public Container getParent() { 
     return null; 
   } 
   public void setParent(Container container) {  }
  public ClassLoader getParentClassLoader() { 
     return null; 
   } 
   public void setParentClassLoader(ClassLoader parent) {  } 
   public Realm getRealm() { 
     return null; 
   } 
   public void setRealm(Realm realm) {  } 
   public DirContext getResources() { 
     return null; 
   } 
   public void setResources(DirContext resources) {  } 
   public void addChild(Container child) {  } 
   public void addContainerListener(ContainerListener listener) {  } 
   public void addMapper(Mapper mapper) {  } 
   public void addPropertyChangeListener( 
   		PropertyChangeListener listener) {  } 
   public Container findchild(String name) { 
     return null; 
   } 
   public Container[] findChildren() { 
     return null; 
   }
  public ContainerListener[] findContainerListeners() { 
     return null; 
   } 
   public Mapper findMapper(String protocol) { 
     return null; 
   } 
   public Mapper[] findMappers() { 
     return null; 
   } 
   public void invoke(Request request, Response response) 
     throws IoException, ServletException { 
 
     string servletName = ( (Httpservletrequest) 
request).getRequestURI(); 
     servletName = servletName.substring(servletName.lastIndexof("/") + 
1); 
     URLClassLoader loader = null;
  try { 
       URL[] urls = new URL[1]; 
       URLStreamHandler streamHandler = null; 
       File classpath = new File(WEB_ROOT);        string repository = (new URL("file",null, 
classpath.getCanonicalpath() + File.separator)).toString(); 
       urls[0] = new URL(null, repository, streamHandler);
  loader = new URLClassLoader(urls); 
 
     } 
     catch (IOException e) { 
       System.out.println(e.toString() ); 
     } 
     Class myClass = null; 
     try { 
       myClass = loader.loadclass(servletName); 
     } 
     catch (classNotFoundException e) { 
       System.out.println(e.toString()); 
     } 
 
     servlet servlet = null; 
 
     try { 
       servlet = (Servlet) myClass.newInstance(); 
       servlet.service((HttpServletRequest) request,(HttpServletResponse) response); 
     } 
     catch (Exception e) { 
       System.out.println(e.toString()); 
     } 
     catch (Throwable e) { 
       System.out.println(e.toString()); 
     } 
   } 
 
   public Container map(Request request, boolean update) { 
     return null; 
   } 
   public void removeChild(Container child) {  } 
   public void removeContainerListener(ContainerListener listener) {  } 
   public void removeMapper(Mapper mapper) {  }
   public void removeMapper(Mapper mapper) {  } 
   public void removoPropertyChangeListener( 
     PropertyChangeListener listener) { 
   } 
}

这里只提供了SimpleContainer类的invoke方法的实现。因为默认的连接器将调用这个方法。invoke方法创建一个类加载器,加载servlet类,调用它的servlet的service方法。

Listing 4.4: The ex04.pyrmont.startup.Bootstrap class   
 
package ex04.pyrmont.startup; 
import ex04.pyrmont.core.simplecontainer; 
import org.apache.catalina.connector.http.HttpConnector; 
 
public final class Bootstrap { 
   public static void main(string[] args) { 
 	
 HttpConnector connector = new HttpConnector(); 
     SimpleContainer container = new SimpleContainer(); 
     connector.setContainer(container); 
     try { 
       connector.initialize(); 
       connector.start(); 
 
       // make the application wait until we press any key. 
       System in.read(); 
     } 
     catch (Exception e) { 
       e.printStackTrace(); 
     } 
   } 
}

Bootstrap类的main方法构建了一个org.apache.catalina.connector.http.HttpConnector的实例和一个SimpleContainer接口。然后调用连接器的setContainer方法把连接器和容器联系起来。接下来,它调用连接器的initialize和start方法。它将让连接器处理在8080端口上的任何HTTP请求。
                                                                第四章 完

你可能感兴趣的:(java,apache,tomcat,socket,servlet)