首先:我们需要知道几个Ascii码及http请求报文:

           10 : 换行键、13:回车键、32:空格。(这几个是http协议重要的分割符)、当然还有其他的解析分割符、如&、:、= 等,这些具体的就先不引入了。

梳理 tomcat 是怎样解析一个请求的

   同时至于tomcat是怎样启动、接受报文的在这里也不进行具体什么了:可以看以前的文章(Tomcat源码系列)可能整理的并不是很清晰,当应该能理解一个大概了。

 

   这里我们直接从:Processor的初始化说明吧。

      1、我们首先要清晰,在源码中有两个Request:org.apache.coyote.Request、

tomcat接收的http报文都是解析保存在org.apache.coyote.Request。

       2、我们来看下coyote.Request对象的初始化。由于Processor是关键request、以及response的,所以其初始化是在Http11Processor(Http11Processor继承AbstractProcessor)

梳理 tomcat 是怎样解析一个请求的

初始是直接new:  

public AbstractProcessor(AbstractEndpoint<?> endpoint) {
    this(endpoint, new Request(), new Response());
}
public Http11Processor(............ AbstractEndpoint<?> endpoint, .............) {
    super(endpoint);
    this.httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
    this.inputBuffer = new Http11InputBuffer(this.request, maxHttpHeaderSize, rejectIllegalHeaderName, this.httpParser);
    this.request.setInputBuffer(this.inputBuffer);
    this.outputBuffer = new Http11OutputBuffer(this.response, maxHttpHeaderSize, sendReasonPhrase);
        ..........................
}

这里有关键的一步要注意(标黑)。这个inputBuffer就是tomcat会将接收到的报文保存在这里,对于报文解析的读取就是操作它。

   3、我们看下processor是怎样处理socketWrapper的(其实可以简单将其看为是一个Socket)

梳理 tomcat 是怎样解析一个请求的

processor再调用service:

梳理 tomcat 是怎样解析一个请求的

关键就是这个service了:

   在service方法中,解析请求行:

梳理 tomcat 是怎样解析一个请求的

我们现在看下inputBuffer中的内容:梳理 tomcat 是怎样解析一个请求的

byteBuffer中是没有内容的(lim=0)。

在parseRequestLine中,this.fill(false)方法完成将socket中字节读到buffer中的:

    我们看到在read前:

       梳理 tomcat 是怎样解析一个请求的

梳理 tomcat 是怎样解析一个请求的

这里wrapper,就是socketWrapper。所以就将socket的字节读到了inputBuffer。

  4、  同时我们知道,在http协议的请求行中,有请求方法、url、协议版本,中间以空格(32)分开。

所以解析分了几步:

boolean parseRequestLine(boolean keptAlive) throws IOException {
        if (this.parsingRequestLinePhase < 2) {
                  .........
                    if (!this.fill(false)) {
              .............
        }
        if (this.parsingRequestLinePhase == 2) {
               ............
                    if (chr != 32 && chr != 9) {
                     .............
                        this.request.method().setBytes(this.byteBuffer.array(), this.parsingRequestLineStart, pos - this.parsingRequestLineStart);
                    }
                }

                this.parsingRequestLinePhase = 3;
                break;
            }
        }
           ..................
        int pos;
        if (this.parsingRequestLinePhase == 4) {
                     ...........
                            if (chr != 32 && chr != 9) {
                                if (chr != 13 && chr != 10) {
                                    if (chr == 63 && this.parsingRequestLineQPos == -1) {
                                        this.parsingRequestLineQPos = pos;
                                      ................
                          //63  就是问号
                        if (this.parsingRequestLineQPos >= 0) {
                            this.request.queryString().setBytes( ...........
                            this.request.requestURI().setBytes(this.byteBuffer.array() .........
                        } else {
                            this.request.requestURI().setBytes(this.byteBuffer.array() ...........;
                        }
        ..........
        if (this.parsingRequestLinePhase == 6) {
              .............
                this.request.protocol().setBytes(this.byteBuffer.array(),  ...........
            } else {
                this.request.protocol().setString("");
            }
              ..........
        }
    }
}

,我们来看解析之后的:coyote.Request

  梳理 tomcat 是怎样解析一个请求的

由于还没有解析头部所以headers为空。

     5、 之后就是解析头部了:

梳理 tomcat 是怎样解析一个请求的

头部的解析就不具体展开了。不过我们来看下inputBuffer中的byteBuffer。

在未解析前,只解析了请求行:pos为44:

梳理 tomcat 是怎样解析一个请求的

梳理 tomcat 是怎样解析一个请求的

而全面的13/10就代表换了一行了,所以到了头部的位置。

解析头部之后就读到373了:

梳理 tomcat 是怎样解析一个请求的

然后就还是请求体在inputBuffer中了。

但我们可以看到,直到调用适配器方法的时候,并没有读出请求体的内容,(如果请求体中的内容是文件的话、需要在使用getPart()才会其解析):

梳理 tomcat 是怎样解析一个请求的

    6、然后在适配器中的service就会使用一个新的request了:

    梳理 tomcat 是怎样解析一个请求的

需要注意的是,这个reques只是将coyot.Reqeust包装了一下。connector.Request内部的的方法获取数据还是建立在coyot.Reqeust。(后面会进行说明)

   之后则是pipeLine的调用了:

梳理 tomcat 是怎样解析一个请求的

同时我们知道,tomcat还会用到一个门面模式。

梳理 tomcat 是怎样解析一个请求的

梳理 tomcat 是怎样解析一个请求的

将connector.Request包装了一下。

  现在我们来回答前面那个问题:

      通过自己写的,看看回去Parameter的过程:

   梳理 tomcat 是怎样解析一个请求的

这里会跳到:RequestFacade

   在RequestFacade中会通过其中的reqeust(connector.Request)再去获取

梳理 tomcat 是怎样解析一个请求的

而connector.Request会去获取coyot.Reqesut

梳理 tomcat 是怎样解析一个请求的

这里,会将coyoteRequest中的解析放到connecotr.Request,但数据最终还是从coyoteRequest中获取。

 

==================================================

 

顺便提下body、与parameter的解析:

   

protected void parseParameters() {
         ...............

        if ("multipart/form-data".equals(contentType)) {
            this.parseParts(false);
            success = true;
            return;
        }
          ...................
}

梳理 tomcat 是怎样解析一个请求的

 

相关文章:

  • 2021-04-25
  • 2021-10-05
  • 2021-12-17
  • 2021-07-17
  • 2021-12-24
  • 2022-12-23
  • 2021-11-18
猜你喜欢
  • 2021-05-21
  • 2021-04-10
  • 2022-12-23
  • 2022-01-18
  • 2022-01-19
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案