Nginx之http模块是如何解析http请求的,6个步骤一目了然-02
解析http请求:
在解析http请求之前,我们先来复习下http请求包含的各个部分及每个部分的协议格式。
一个http请求一般由 请求行、请求头、请求体、响应行、响应头、响应体 这6个部分组成。
我们就以 local.com/index为例:
1)请求行基本格式:
GET /index HTTP/1.1
2)请求头基本格式:
Host: local.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
3)响应体基本格式:
// 这里比较多 先省略
4)响应行基本格式:
HTTP/1.1 200 OK
5)响应头基本格式:
Content-length: 34
Content-type: application/json
Date: Tue, 09 Nov 2021 08:29:33 GMT
6)响应体基本格式:
// 这里比较多 先省略
了解了HTTP请求的基本格式,我们就可以来看请求是如何解析的。
上面说过是从ngx_http_init_connection方法开始处理的。
// 文件名 ngx_http_request.c
void
ngx_http_init_connection(ngx_connection_t *c)
{
ngx_event_t *rev;
rev = c->read;
rev->handler = ngx_http_wait_request_handler;
ngx_add_timer(rev, cscf->client_header_timeout);
}
可以看到这里将读事件的回调方法设置成了 ngx_http_wait_request_handler。也就是说建立连接后并没有立刻初始化请求,而是在链接对应的套接字有数据的时候才开始分配。用人话将就是只有当用户发来请求内容时才真正分配内存资源,以达到减少内存资源的浪费。
当我们追到ngx_http_wait_request_handler里的时候发现,这里将读事件回调函数设置为ngx_http_process_request_line方法
// 文件名 ngx_http_request.c
static void
ngx_http_wait_request_handler(ngx_event_t *rev)
{
rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
}
那么这里为什么多加一行设置成回调?
这是因为http请求的url长度不确定性,内核套接字缓存区未必能接收下整个请求行,因此将读事件的回调方法置为ngx_http_process_request_line。后续epoll事件将调用该方法,直到完整解析请求行。
请求行解析完了就轮到请求头了。那请求头也一样,长度不确定。同理也需要设置回调方法ngx_http_process_request_header(可以看到,读取请求头依然用的是ngx_http_read_request_header方法),
然后调用ngx_http_parse_header_line方法对请求头进行解析,每解析一个请求头就将其放入ngx_http_request_t结构体的headers_in中,并以链表的形式保存。
到此,Nginx已经获取请求行与请求头的全部信息,然后调用ngx_http_process_request_header方法对请求头做简单验证后,就可正式进入请求的处理阶段。

那么Nginx到底是如何处理请求,如何响应的呢?我们明天继续。