leevis.com
leevis.com copied to clipboard
nginx 请求解析相关代码分析
概述
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。