leevis.com icon indicating copy to clipboard operation
leevis.com copied to clipboard

nginx 请求解析相关代码分析

Open vislee opened this issue 7 years ago • 0 comments

概述

http协议的请求(request)分为3部分, 第一部分为请求行(request line),第二部分为请求头(request header),第三部分为请求体(request body)。 请求行格式:

请求方法 空格 URI 空格 协议版本 回车符 换行符

URI的格式也很复杂: scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]

不过在nginx中,只关心这样的格式:scheme://host:port/path?query#fragment

请求头格式:

请求头key : 请求头value 回车符 换行符

......

请求头key : 请求头value 回车符 换行符

请求头可以有多个。请求头和请求body 之间用回车符和换行符分割。

回车符 换行符

请求body

body

把上述所有表格组合在一起就是一次http请求的格式。

nginx主要的功能是http的代理。 因此需要解析http请求并校验正确性。

源码分析

解析http请求行和请求头的函数基本都封装在ngx_http_parse.c 文件中。

  • 请求行解析函数ngx_http_parse_request_line

请求行格式:method [scheme://host:port]/path[?query][#fragment] HTTP/major.minor nginx 以该格式为准,定义了一系列的状态机来解析该格式 。状态机:


    enum {
        sw_start = 0,
        sw_method,
        sw_spaces_before_uri,
        sw_schema,
        sw_schema_slash,
        sw_schema_slash_slash,
        sw_host_start,
        sw_host,
        sw_host_end,
        sw_host_ip_literal,
        sw_port,
        sw_host_http_09,
        sw_after_slash_in_uri,
        sw_check_uri,
        sw_check_uri_http_09,
        sw_uri,
        sw_http_09,
        sw_http_H,
        sw_http_HT,
        sw_http_HTT,
        sw_http_HTTP,
        sw_first_major_digit,
        sw_major_digit,
        sw_first_minor_digit,
        sw_minor_digit,
        sw_spaces_after_digit,
        sw_almost_done
    } state;

该函数用到了一个数组,该数组标识了哪些字符是常规字符。 该表是如何组织的,ascii码对应的二进制,高3位是数组的下标,低5位是偏移量。

static uint32_t  usual[] = {
    0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */

                /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
    0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */

                /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
#if (NGX_WIN32)
    0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */
#else
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
#endif

                /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
};


// 使用该数组的方法,我认为应该定义一个宏,这样好理解一些
// ch >> 5 在数组中的下标 0-7
// 1U << (ch & 0x1f) 该字符在32bit中所在的位置。0x1f 是31,实际上就是0-31 共32bit
// 下面的这个语句实际上就是从上述表中定位一个bit位
usual[ch >> 5] & (1U << (ch & 0x1f))

  • ngx_http_process_request_uri 解析uri中的uri部分,args部分和exten部分。

  • ngx_http_parse_complex_uri 解析编码的uri。

vislee avatar Feb 01 '18 07:02 vislee