南风知我不易

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

How tomcat works

chapter 1 简单的web服务器

这一张的主要内容就是实现一个简单的静态资源服务器,socket编程,利用java提供的socket和serverSocket编程

整体过程如下:

HttpServer通过serverSocket监听端口,通过阻塞的accept方法接收请求,然后创建resquest和response对象,

//服务器启动主方法
public static void main(String[] args) {
    HttpServer server = new HttpServer();
    server.await();
  }

//await方法,几乎所有的任务都在这里完成
public void await() {
    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);
    }

    // Loop waiting for a request
    //shutdown为一个成员变量,这样的服务器的较为优雅的停止方式为接收一个uri为"/SHUTDOWN_COMMAND"的请求
    while (!shutdown) {
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      try {
        socket = serverSocket.accept();
        input = socket.getInputStream();
        output = socket.getOutputStream();

        //创建request对象,把inputstream给他
        // create Request object and parse
        Request request = new Request(input);
        request.parse();

        // create Response object
        Response response = new Response(output);
        response.setRequest(request);
        response.sendStaticResource();

        // Close the socket
        socket.close();

        //check if the previous URI is a shutdown command
        shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
      }
      catch (Exception e) {
        e.printStackTrace();
        continue;
      }
    }
  }

request对象主要就做一件事情,从inputstream中解析uri

parse()

从inputstream中读取数据转成字符串,然后调用parseUri方法

public void parse() {
    // Read a set of characters from the socket
    StringBuffer request = new StringBuffer(2048);
    int i;
    byte[] buffer = new byte[2048];
    try {
      i = input.read(buffer);
    }
    catch (IOException e) {
      e.printStackTrace();
      i = -1;
    }
    for (int j=0; j<i; j++) {
      request.append((char) buffer[j]);
    }
    System.out.print(request.toString());
    uri = parseUri(request.toString());
  }

parseUri(String requestString)

private String parseUri(String requestString) {
    int index1, index2;
    index1 = requestString.indexOf(' ');
    if (index1 != -1) {
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
          //切割出uri
        return requestString.substring(index1 + 1, index2);
    }
    return null;
  }

response对象,拿到request和outputstream,按照request中的uri找到对应的资源,然后塞入outputstream中就完事儿

sendStaticResource()

public void sendStaticResource() throws IOException {
  byte[] bytes = new byte[BUFFER_SIZE];
  FileInputStream fis = null;
  try {
    File file = new File(HttpServer.WEB_ROOT, request.getUri());
    if (file.exists()) {
      fis = new FileInputStream(file);
      int ch = fis.read(bytes, 0, BUFFER_SIZE);
      while (ch!=-1) {
        output.write(bytes, 0, ch);
        ch = fis.read(bytes, 0, BUFFER_SIZE);
      }
    }
    else {
      // file not found
      String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
              "Content-Type: text/html\r\n" +
              "Content-Length: 23\r\n" +
              "\r\n" +
              "<h1>File Not Found</h1>";
      output.write(errorMessage.getBytes());
    }
  }
  catch (Exception e) {
    // thrown if cannot instantiate a File object
    System.out.println(e.toString() );
  }
  finally {
    if (fis!=null)
      fis.close();
  }
}

tips:由于这个响应没有任何响应头,所以chrome现在的版本不会接受它,但是firefox可以,但也只是以纯文本形式

chapter 2 一个简单的servlet容器

第一章利用了java的socket编程构建了一个只能响应只能响应静态资源的http服务器,这一章实现一个简单的servlet容器,它只能处理简单的servlet和响应静态资源

熟悉servlet接口

servlet接口中声明了5个方法

//初始化方法
public void init(ServletConfig config)throws ServletException;

//主要的方法,接受一个request对象和response对象,然后进行处理
public void service(ServletRequest request,ServletResponse response)throws ServletException,Java.io.IOException;

//在将servlet实例从服务中移除时,servlet容器会调用servlet实例的destory()方法,清理一些资源
public void destory();

public ServletConfig getServletConfig();

public java.lang.String getServletInfo();

一个功能齐全的servlet容器处理流程

  • 第一次调用某servlet时,将它载入,然后执行init()方法
  • 针对每个request请求,创建ServletRequest实例和ServletResponse实例,传递给service()方法
  • 关闭servlet时,调用destroy()方法,并且卸载该类

本章是一个简单的实现,就不调用init方法和destroy方法,创建好ServletRequest和ServletResponse然后作为参数传递给service方法即可

tips:在这样的servlet容器中,每次对于该servlet的请求,都会导致载入相应的servlet类,可想而知,效率是非常低的

主要类以及流程分析

本章主要包含了以下几个类:

  • HttpServer1
  • Request
  • Response
  • StaticResourceProcessor
  • ServletProcessor1
  • Constants

该servlet容器的UML如下

image.png

处理流程

入口类,HttpServer1,监听端口8080,构建request,response,根据是请求静态资源还是servlet分别处理

//服务器主方法
public static void main(String[] args) {
    HttpServer1 server = new HttpServer1();
    server.await();
  }

//await
public void await() {
    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);
    }

    // Loop waiting for a request
    while (!shutdown) {
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      try {
        socket = serverSocket.accept();
        input = socket.getInputStream();
        output = socket.getOutputStream();

        // create Request object and parse
        Request request = new Request(input);
        request.parse();

        // create Response object
        Response response = new Response(output);
        response.setRequest(request);

        // check if this is a request for a servlet or a static resource
        // a request for a servlet begins with "/servlet/"
        if (request.getUri().startsWith("/servlet/")) {
          ServletProcessor1 processor = new ServletProcessor1();
          processor.process(request, response);
        }
        else {
          StaticResourceProcessor processor = new StaticResourceProcessor();
          processor.process(request, response);
        }

        // Close the socket
        socket.close();
        //check if the previous URI is a shutdown command
        shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
      }
      catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
      }
    }
  }

request.parse()

和第一章一样解析uri

public void parse() {
    // Read a set of characters from the socket
    StringBuffer request = new StringBuffer(2048);
    int i;
    byte[] buffer = new byte[2048];
    try {
      i = input.read(buffer);
    }
    catch (IOException e) {
      e.printStackTrace();
      i = -1;
    }
    for (int j=0; j<i; j++) {
      request.append((char) buffer[j]);
    }
    System.out.print(request.toString());
    uri = parseUri(request.toString());
  }

//uri = parseUri(request.toString());
private String parseUri(String requestString) {
    int index1, index2;
    index1 = requestString.indexOf(' ');
    if (index1 != -1) {
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
        return requestString.substring(index1 + 1, index2);
    }
    return null;
  }

processor.process(request, response);

请求servlet分支,接受request,response作为参数,然后调用ServletProcessor1的process方法,载入对应的servlet然后,实例化该servlet并且真正地处理请求

public void process(Request request, Response response) {

    String uri = request.getUri();
    String servletName = uri.substring(uri.lastIndexOf("/") + 1);
    URLClassLoader loader = null;

    try {
      // create a URLClassLoader
      URL[] urls = new URL[1];
      URLStreamHandler streamHandler = null;
      File classPath = new File(Constants.WEB_ROOT);
      // the forming of repository is taken from the createClassLoader method in
      // org.apache.catalina.startup.ClassLoaderFactory
      String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
      // the code for forming the URL is taken from the addRepository method in
      // org.apache.catalina.loader.StandardClassLoader class.
      urls[0] = new URL(null, repository, streamHandler);
      loader = new URLClassLoader(urls);
    }
    catch (IOException e) {
      System.out.println(e.toString() );
    }
    Class myClass = null;
    try {
      //载入servlet
      myClass = loader.loadClass(servletName);
    }
    catch (ClassNotFoundException e) {
      System.out.println(e.toString());
    }

    Servlet servlet = null;

    try {
      //实例化servlet并处理请求
      servlet = (Servlet) myClass.newInstance();
      servlet.service((ServletRequest) request, (ServletResponse) response);
    }
    catch (Exception e) {
      System.out.println(e.toString());
    }
    catch (Throwable e) {
      System.out.println(e.toString());
    }

  }

processor.process(request, response);

发送静态资源分支,这里只是调用了以下response的sendStaticResource方法

public void process(Request request, Response response) {
    try {
      response.sendStaticResource();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }

chapter 3 连接器(Connector)

建立一个连接器,用来增强第二章的功能,主要就是用一种更好的方式创建request和response对象

本章应用包含三个模块:连接器模块、启动模块和核心模块

启动模块只包含了一个类:BootStrap

连接器模块可分为以下5个类型:

  • 连接器及其支持类(HttpConnector和HttpProcessor)
  • 表示Http请求的类(HttpRequest)及其支持类
  • 表示Http响应的类(HttpResponse)及其支持类
  • 外观类(HttpRequestFacade和HttpResponseFacade)
  • 常量类(Constants)

核心模块包含两个类,servletProcessor和StaticResourceProcessor,具体UML如下

image.png

主要类以及流程分析

启动类BootStrap,new一个connector然后调用connector.start(),不难猜到connector的start另启了一个线程

public static void main(String[] args) {
    HttpConnector connector = new HttpConnector();
    connector.start();
  }

HttpConnector

连接器模块主要类,负责监听端口,接收请求,然后将自己传递给HttpProcessor,并且将socket作为参数,传递给HttpProcessor的process方法

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

public void start() {
    Thread thread = new Thread(this);
    thread.start();
  }

HttpProcessor

负责生成resquest以及response,然后交给ServletProcessor或者StaticResourceProcessor处理

public void process(Socket socket)

public void process(Socket socket) {
    //SocketInputStream是InputStream的包装类,主要提供了readRequestLine方法和readHeader方法
    SocketInputStream input = null;
    OutputStream output = null;
    try {
      //2048为指定的缓冲数组大小
      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);
	
      //然后就可以根据请求的uri决定交给什么processor了
      //这两者就和第二章的一样了
      //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();
    }
  }
解析请求行parseRequest

private void parseRequest(SocketInputStream input, OutputStream output)

private void parseRequest(SocketInputStream input, OutputStream output)
    throws IOException, ServletException {

    // Parse the incoming request line
    //HttpRequestLine为请求行对象,利用readRequestLine方法填充HttpProcessor的HttpRequestLine成员变量
    input.readRequestLine(requestLine);
    //经过上面的处理,这里就能拿到方法名了
    String method =
      new String(requestLine.method, 0, requestLine.methodEnd);
    //uri暂时还拿不到,因为可能带参,此时的uri字符数组保存的是带参的uri
    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
    //处理带参uri
    int question = requestLine.indexOf("?");
    if (question >= 0) {
        //包请求参数塞进queryString,回头需要的时候解析
      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);
        }
      }
    }
	
    //处理jessionId
    // 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 + "'");
    }
  }

HttpRequestLine

HttpRequestLine为请求行对象,用字符数组来存储请求行的数据,代码一瞥

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;

}

    public char[] method;
    public int methodEnd;
    public char[] uri;
    public int uriEnd;
    public char[] protocol;
    public int protocolEnd;

SocketInputStream的readRequestLine方法

public void readRequestLine(HttpRequestLine requestLine)

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 {
                //调用read方法,
                chr = read();
            } catch (IOException e) {
                chr = -1;
            }
        } while ((chr == CR) || (chr == LF));
        if (chr == -1)
            throw new EOFException
                (sm.getString("requestStream.readline.error"));
    	//由于read方法中将pos指向了下一个字节了,需要回退一步
        pos--;

        // Reading the method name

        int maxRead = requestLine.method.length;
        int readStart = pos;
        int readCount = 0;

        boolean space = false;

        while (!space) {
            //超过method字符数组的最大长度了
            // 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
            //如果已经读取到inputstream读取出来的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;
            }
            //这里method字符数组是以空格节未的,并没有在buf[pos]=SP的时候break
            requestLine.method[readCount] = (char) buf[pos];
            readCount++;
            pos++;
        }
		//以空格结尾,readCount去掉空格
        requestLine.methodEnd = readCount - 1;
		//下面就是解析uri和protocol了

    }

SocketInputStream的read、fill方法

public int read()
        throws IOException {
   		//起始值pos为0,count也为0
        if (pos >= count) {
            //修改count为本次读入的字节长度
            fill();
            if (pos >= count)
                return -1;
        }
    	//返回buf[pos]的那个字节
        return buf[pos++] & 0xff;
    }

protected void fill()
        throws IOException {
        pos = 0;
        count = 0;
    	//
        int nRead = is.read(buf, 0, buf.length);
        if (nRead > 0) {
            count = nRead;
        }
    }
解析首部行parseHeaders

private void parseHeaders(SocketInputStream input)

private void parseHeaders(SocketInputStream input)
    throws IOException, ServletException {
    //循环解析每个请求头
    while (true) {
      HttpHeader header = new HttpHeader();;

      // Read the next header
      //socketInputStream提供的readHeader方法,上面已经详细解读过readRequestLine方法了,这里不详细解读,填充header
      //HttpHeader和HttpRequestLine结构也差不多
      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);
      //对于cookie和content-length两个header需要特殊处理一下
      // 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
  }
懒加载的parameter

请求参数在被调用之前是不会解析的,在request的getParameter、getParameterMap、getParameterNames等方法被调用时,首先会调用parseParameter方法

需要注意的一点是,ParameterMap是不支持修改的,它被锁住了,很简单,用一个lock变量,修改的直接return

protected void parseParameters()

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请求头中的参数
    // Parse any parameters specified in the query string
    String queryString = getQueryString();
    try {
      RequestUtil.parseParameters(results, queryString, encoding);
    }
    catch (UnsupportedEncodingException e) {
      ;
    }

    //parse请求体中的参数
    // 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
    //再把parameterMap锁上,并且将parsed置为true避免重复parse
    results.setLocked(true);
    parsed = true;
    parameters = results;
  }

chapter 4 Tomcat默认的连接器

总体介绍

第三章实现了简单的连接器,第四章来看看Tomcat默认的连接器,虽然该连接器已经被弃用,换成了另一个运行速率更快的连接器——Coyote——替代,但是还是值得一看的,相较于第三章简单的连接器,Tomcat4的默认连接器有以下优化:

  • 更为全面地支持Http协议,包括从0.9到1.1,也支持1.1的新特性
  • 每一个connector不再只绑定一个HttpProcessor,将会利用多线程支持多Http连接
  • 大量利用字符数组,减少了代价较高的字符串操作
  • 利用了池的技术,缓存了HttpProcessor对象,避免每次来都需要创建,节省了对象创建的开销

但仍然还存在着上来就直接parse所有的Header不管有没有使用,效率比较低的问题

它的主要工作原理和第三章的连接器基本类似,细微之处不同稍后源代码讨论

默认的连接器UML图如下:

image.png

首先需要注意的一点是,于第三章不同,HttpConnector来负责Request、Response的创建了,而HttpProcessor专注于parse信息,也就是填充request和response

Http1.1的新特性

持久连接

减少了频繁创建TCP连接带来的系统消耗,也减少了三次握手的次数,提高了速度,在Http1.1中,默认采用持久连接,也可以显式使用,在request首部行加上

Connection: Keep-alive
块编码

有了持久连接之后,一个TCP连接可能被共享,那么多个对象的数据可能混在一起,那么如何把它们区分开来呢?利用transfer-encoding的特殊请求头,它用来指明字节流会分块发送,对每一个块,块的长度后面会有一个CRLF,然后才是数据,一个事务以一个长度为0的块标记,“0\r\n”表示事务已经结束

//e.g利用两个块发送 I'am as helpess as a kitten up a tree.

//发送内容如下
1D\r\n
I'am as helpess as a kitten u
9\r\n
p a tree.
0\r\n
状态码100

当浏览器想发送一个比较大的请求体的时候,不知道服务器能不能接收时,可以先发送一个带有如下请求头的请求

Except: 100-continue

期待服务的回应为100-continue,收到这个回应说明可以接收,这个时候浏览器才会真正地发出这个比较大的请求,避免浪费带宽、系统资源

如果服务器收到带有上述请求头的请求,并且支持的话,就发送如下的回复

HTTP/1.1 100 continue

这样浏览器就会发送真正的请求了

主要类以及流程分析

创建ServerSocket

首先调用initialize方法

void initialize()

public void initialize()
    throws LifecycleException {
        if (initialized)
            throw new LifecycleException (
                sm.getString("httpConnector.alreadyInitialized"));
        this.initialized=true;
        Exception eRethrow = null;
        // Establish a server socket on the specified port
        try {
            //调用open方法
            serverSocket = open();
        } catch (IOException ioe) {
         	...   
        }
        if ( eRethrow != null )
            throw new LifecycleException(threadName + ".open", eRethrow);
}

ServerSocket open()

private ServerSocket open()
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException
    {

        // Acquire the server socket factory for this Connector
        ServerSocketFactory factory = getFactory();
		//根据是否有限定的IP地址选择不同的工厂方法
        // If no address is specified, open a connection on all addresses
        if (address == null) {
            log(sm.getString("httpConnector.allAddresses"));
            try {
                //不限定端口地址,也就是说监听任何IP的请求
                return (factory.createSocket(port, acceptCount));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }

        // Open a server socket on the specified address
        try {
            InetAddress is = InetAddress.getByName(address);
            log(sm.getString("httpConnector.anAddress", address));
            try {
                return (factory.createSocket(port, acceptCount, is));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + address +
                                        ":" + port);
            }
        } catch (Exception e) {
            log(sm.getString("httpConnector.noAddress", address));
            try {
                return (factory.createSocket(port, acceptCount));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }

    }

factory.createSocket(port, acceptCount)

//ServerSocketFactory中定义,在DefalutServerSocketFactory中实现
public ServerSocket createSocket (int port, int backlog)
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException {

        return (new ServerSocket(port, backlog));

    }

至此创建了ServerSocket并与HttpConnector的serverSocket成员变量绑定

监听请求

start()

public void start() throws LifecycleException {

        // Validate and update our current state
        if (started)
            throw new LifecycleException
                (sm.getString("httpConnector.alreadyStarted"));
        threadName = "HttpConnector[" + port + "]";
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        // Start our background thread
    	//new了一个thread 并且调用了thread的start方法
        threadStart();

        // Create the specified minimum number of processors
        while (curProcessors < minProcessors) {
            if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
                break;
            HttpProcessor processor = newProcessor();
            recycle(processor);
        }

    }

void run()

start之后进入run方法

public void run() {

        // Loop until we receive a shutdown command
    	//stopped成员变量
        while (!stopped) {

            // Accept the next incoming connection from the server socket
            Socket socket = null;
            try {
                //接收请求
                socket = serverSocket.accept();
                //设置超时时长
                if (connectionTimeout > 0)
                    socket.setSoTimeout(connectionTimeout);
            } catch (AccessControlException ace) {
                log("socket accept security exception: " + ace.getMessage());
                continue;
            } catch (IOException e) {
                if (started && !stopped)
                    log("accept: ", e);
                break;
            }

            // Hand this socket off to an appropriate processor
            //从成员变量processors,这个processor池中拿一个HttpProcessor出来
            //如果池中没有,则创建一个
            HttpProcessor processor = createProcessor();
            if (processor == null) {
                try {
                    log(sm.getString("httpConnector.noProcessor"));
                    //如果不允许创建了,则直接把socket close掉,也就是直接抛弃这个请求
                    socket.close();
                } catch (IOException e) {
                    ;
                }
                continue;
            }
            //把socket交给processor
            processor.assign(socket);

            // The processor will recycle itself when it finishes

        }

        // Notify the threadStop() method that we have shut ourselves down
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }

HttpProcessor createProcessor()

private HttpProcessor createProcessor() {

        synchronized (processors) {
            if (processors.size() > 0)
                return ((HttpProcessor) processors.pop());
            if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
                //stack中没有空闲的的processor了,并且当前Processor数量还没超过允许的最大Processor数量,则创建一个新的并返回
                return (newProcessor());
            } else {
                //如果maxProcessors<0 说明processor数量不受限制,可以无限制创建
                if (maxProcessors < 0) {
                    return (newProcessor());
                } else {
                    //说明curProcessors > maxProcessors 没有空闲的processor了,也不允许创建
                    return (null);
                }
            }
        }

    }

HttpProcessor newProcessor()

private HttpProcessor newProcessor() {
		//创建processor并且绑定connector
        HttpProcessor processor = new HttpProcessor(this, curProcessors++);
        if (processor instanceof Lifecycle) {
            try {
                //注意!!! 这里启动了HttpProcessor线程
                ((Lifecycle) processor).start();
            } catch (LifecycleException e) {
                log("newProcessor", e);
                return (null);
            }
        }
        created.addElement(processor);
        return (processor);

    }

processor.assign(socket)

connector把socket交付给HttpProcessor就结束了,它并不像之前那样还需要等待HttpProcessor的执行结果,它可以去继续监听其他请求了,它这里就只负责这里就负责唤醒了一下在run方法中由于没有socket阻塞的processor

synchronized void assign(Socket socket) {

        // Wait for the Processor to get the previous Socket
    	//available默认值为false,代表是否有新的socket过来
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        // Store the newly available Socket and notify our thread
        this.socket = socket;
        available = true;
    	//notifyAll唤醒了谁呢?唤醒了HttpProcessor的主线程
    	//它在newProcessor方法中启动,然后在await方法中等待
        notifyAll();

        if ((debug >= 1) && (socket != null))
            log(" An incoming request is being assigned");

    }
处理请求

HttpProcessor

run()

public void run() {

        // Process requests until we receive a shutdown signal
    	//stopped成员变量
        while (!stopped) {

            // Wait for the next socket to be assigned
            Socket socket = await();
            if (socket == null)
                continue;

            // Process the request from this socket
            try {
                process(socket);
            } catch (Throwable t) {
                log("process.invoke", t);
            }

            // Finish up this request
            //把processor重新又压回connector的栈内重复利用
            connector.recycle(this);

        }

        // Tell threadStop() we have shut ourselves down successfully
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }

await()

private synchronized Socket await() {

        // Wait for the Connector to provide a new Socket
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
		//被唤醒之后availabe被修改成了true了,将socket赋值给局部变量,并且将availabe修改为false
        // Notify the Connector that we have received this Socket
        Socket socket = this.socket;
        available = false;
        notifyAll();

        if ((debug >= 1) && (socket != null))
            log("  The incoming request has been awaited");

        return (socket);

    }

image.png

这里引用一下原文的解释

为什么这里需要返回一个局部变量?

这样做的话,就可以在当前socket被处理完成之前,接收下一个socket???黑人问号,这个process方法不是单线程的嘛?它不是必须完成解析才能被回收嘛?怎么同时接收两个socket的

为什么还需要notifyAll呢?

防止connector在执行到processor.assign()方法阻塞,此时available为true,connector一直阻塞,知道processor唤醒它

recycle(HttpProcessor processor)

void recycle(HttpProcessor processor) {

        //        if (debug >= 2)
        //            log("recycle: Recycling processor " + processor);
        processors.push(processor);

    }

处理请求的主方法

**private void process(Socket socket) **

private void process(Socket socket) {
        boolean ok = true;
        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;

        // Construct and initialize the objects we will need
        try {
            input = new SocketInputStream(socket.getInputStream(),
                                          connector.getBufferSize());
        } catch (Exception e) {
            log("process.create", e);
            ok = false;
        }

        keepAlive = true;
		//原来宣扬了半天的持久连接新特性就是个keepAlive变量
        while (!stopped && ok && keepAlive) {

            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);
                ok = false;
            }


            // Parse the incoming request
            try {
                if (ok) {
					//如同第三章那样,这里负责处理填充Request,如果想追究如何处理Http1.1的新特性,可以查看,这里不细说
                    parseConnection(socket);
                    parseRequest(input, output);
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        parseHeaders(input);
                    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);
                    }

                }
            } catch (EOFException e) {
                ...
            } 

            // Ask our Container to process this request
            try {
                ((HttpServletResponse) response).setHeader
                    ("Date", FastHttpDateFormat.getCurrentDate());
                if (ok) {
                    //从processor绑定的connector中获取contianer然后把parse好的request和response传递给他,处理请求
                    connector.getContainer().invoke(request, response);
                }
            } catch (ServletException e) {
                ...
            } 

            // Finish up the handling of the request
            if (finishResponse) {
                try {
                    //
                    response.finishResponse();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    request.finishRequest();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    if (output != null)
                        output.flush();
                } catch (IOException e) {
                    ok = false;
                }
            }

            // We have to check if the connection closure has been requested
            // by the application or the response stream (in case of HTTP/1.0
            // and keep-alive).
            if ( "close".equals(response.getHeader("Connection")) ) {
                keepAlive = false;
            }

            // End of request processing
            status = Constants.PROCESSOR_IDLE;

            // Recycling the request and the response objects
            //request和response对象也是需要回收的,在newProcessor的时候调用HttpConnector的createxxx方法创建作为processor的成员变量
            request.recycle();
            response.recycle();

        }

        try {
            shutdownInput(input);
            socket.close();
        } catch (IOException e) {
            ;
        } catch (Throwable e) {
            log("process.invoke", e);
        }
        socket = null;



    }
处理请求(Container)

核心方法invoke,也就是之前的servletProcessor做的事情,通过类加载器找到对应的servlet然后让他处理

invoke方法

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



  }
posted on 2020-11-12 17:09  南风知我不易  阅读(65)  评论(0编辑  收藏  举报