这里先插入一张我找到的很好的一张图,描述了request初始化的整个过程:
从这篇文章开始正式进入http部分了,首先应该着手的是nginx是如何初始化http request的,我们在前面的文章,我们知道当接收到一个connection后,nginx首先会调用ngx_listening_t的handler来处理这个connection,这个handler函数是ngx_http_init_connection,好我们就从这个函数入手(Ngx_http_request.c):
//初始化日志相关的东西
ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
if (ctx == NULL) {
ngx_http_close_connection(c);
return;
}
ctx->connection = c;
ctx->request = NULL;
ctx->current_request = NULL;
c->log->connection = c->number;
c->log->handler = ngx_http_log_error;
c->log->data = ctx;
c->log->action = "reading client request line";
c->log_error = NGX_ERROR_INFO;
上述代码首先是初始化日志相关的东西,
rev = c->read;
rev->handler = ngx_http_init_request; //当前connection的读事件的处理函数,也就是说连接进来的connection的第一次读事件用这个函数进行处理
c->write->handler = ngx_http_empty_handler; //当前connection的写事件的处理函数
紧接着的代码是设置当前connection的读写事件的handler,我们主要看写事件,它的处理函数是ngx_http_init_request。
if (rev->ready) { //表示当前的已经可读了,那么可以直接用ngx_http_init_request来处理,这里如果使用的是epoll的话,一般是不会执行的
/* the deferred accept(), rtsig, aio, iocp */
if (ngx_use_accept_mutex) {
ngx_post_event(rev, &ngx_posted_events); //这里因为还在accept中,所以可能会持有锁,因此要延后处理
return;
}
ngx_http_init_request(rev);
return;
}
接下来就是如果使用的是aio等模型,那么现在就可以用
ngx_http_init_request来处理写事件了,但是我们知道一般情况下用的是epoll,所以这里一般是不会执行的。
ngx_add_timer(rev, c->listening->post_accept_timeout); //为读事件设置定时器,超时的时候会自动调用该事件的处理函数ngx_http_init_request,如果是超时调用的话会直接关闭当前的connection
if (ngx_handle_read_event(rev, 0) != NGX_OK) { //将事件挂起来,把它放到epoll中
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
#endif
ngx_http_close_connection(c);
return;
}
上述的代码首先是为读事件设置了timer,当超时的时候会自动调用
ngx_http_init_request函数,如果是超时的话,这个时候nginx会关闭这个连接。ngx_handle_read_event函数的作用是将当前的读事件加入到epoll当中去。
好了,接下来我们可以来看读事件的处理函数了ngx_http_init_request:
c = rev->data; //获取相应的connection
if (rev->timedout) { //表示读取已经超时了,直接关闭connection
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
ngx_http_close_connection(c); //关闭connection
return;
}
首先是获取事件对应的connection,并且判断当前是否是已经超时了,如果超时的话,那么直接关闭掉这个connection。
c->requests++; //request计数加1
hc = c->data; //创建http connection
if (hc == NULL) {
hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
if (hc == NULL) {
ngx_http_close_connection(c);
return;
}
}
r = hc->request; //获取http请求结构
if (r) {
ngx_memzero(r, sizeof(ngx_http_request_t));
r->pipeline = hc->pipeline;
if (hc->nbusy) {
r->header_in = hc->busy[0];
}
} else {
r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)); //为请求结构分配内存空间
if (r == NULL) {
ngx_http_close_connection(c);
return;
}
hc->request = r;
}
c->data = r; //将connection的data置为当前的request请求
r->http_connection = hc; //请求所在的http connection
c->sent = 0;
r->signature = NGX_HTTP_MODULE;
上述代码用来创建http request结构,以及http connection,并且进行一些初始化。
/* find the server configuration for the address:port */
/**
* ngx_listening_t的servers存放监听同一端口的server,但它们的监听的地址可以不同。
*
* port
* / | \
* addr1 addr2 addr3
* | | |
* conf1 conf2 conf3
*/
port = c->listening->servers;
r->connection = c; //当前请求的connection为c
if (port->naddrs > 1) {
/*
* there are several addresses on this port and one of them
* is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
* is required to determine a server address
*/
/* 获取连接c的socket绑定的本地地址 */
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
/* 根据连接c的socket地址匹配port->addrs,找到对应的address:port */
switch (c->local_sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
addr6 = port->addrs;
/* the last address is "*" */
for (i = 0; i < port->naddrs - 1; i++) {
if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
break;
}
}
addr_conf = &addr6[i].conf;
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) c->local_sockaddr;
addr = port->addrs; //监听当前port的所有地址结构
/* the last address is "*" */
for (i = 0; i < port->naddrs - 1; i++) {
if (addr[i].addr == sin->sin_addr.s_addr) {
break;
}
}
addr_conf = &addr[i].conf;
break;
}
} else {
switch (c->local_sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
addr6 = port->addrs;
addr_conf = &addr6[0].conf;
break;
#endif
default: /* AF_INET */
addr = port->addrs;
addr_conf = &addr[0].conf;
break;
}
}
r->virtual_names = addr_conf->virtual_names;
/* the default server configuration for the address:port */
cscf = addr_conf->default_server; //获取当前地址结构所在的ngx_http_core_srv_conf_t结构
r->main_conf = cscf->ctx->main_conf; //获取main_conf
r->srv_conf = cscf->ctx->srv_conf; //获取srv_conf
r->loc_conf = cscf->ctx->loc_conf; //获取loc_conf
上述代码,其实英文的注释就已经说的很清楚了,根据add:port的信息,来找到对应的server配置,因为我们知道对于同一个端口,可以有好几个地址在监听。
rev->handler = ngx_http_process_request_line; //将读事件的处理函数设置为ngx_http_process_request_line,用于处理请求行
r->read_event_handler = ngx_http_block_reading;
接下来就是将当前event的handler设置为ngx_http_process_request_line函数,以及设置request的读事件,接下来的代码还会涉及到初始化nginx变量,为heade_in分配buffer,用来存储头部信息等等操作,最后就是会有如下代码:
rev->handler(rev); //调用ngx_http_process_request_line函数来处理
嗯,说白了就是调用
ngx_http_process_request_line函数。那好,接下来我们来看ngx_http_process_request_line函数,该函数式用来处理请求的行的,例如GET /index.php HTTP/1.1
c = rev->data; //获取相应的connection
r = c->data; //获取request结构 (这个时候的connection的data域已经被设置为request了)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http process request line");
//如果已经超时了,那么关闭就ok了
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
c->timedout = 1;
ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
代码首先是获取对应的connection,以及request结构,并要判断是否是超时了,如果超时了,那么可以直接关闭connection。
//死循环,不断调用ngx_http_read_request_header函数来从socket中读取足够的信息,然后解析出HTTP Method、URI以及HTTP版本等信息。
for ( ;; ) {
if (rc == NGX_AGAIN) {
/**
* 读取请求行,保存到r->header_in缓冲区
*/
n = ngx_http_read_request_header(r);
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
/**
* 解析请求行,解析后的信息的是以r->uri_start,r->uri_end,r->arg_start等
* 一些指针存储的,这些指针指向r->header_in
*/
rc = ngx_http_parse_request_line(r, r->header_in);
接下来程序进入了死循环,不断的从socket中读取信息保存到header_in缓冲当中,来初始化http的method,url以及参数,版本信息,调用ngx_http_parse_request_line函数来读取信息,调用ngx_http_parse_request_line函数来解析这些信息。接下来的代码还会涉及到对uri_start等指针在buffer中的位置进行设置,这里就不贴代码了,最后
rev->handler = ngx_http_process_request_headers; //调用ngx_http_process_request_headers来处理header
ngx_http_process_request_headers(rev);
设置读事件的handler为函数,并调用该函数。
好吧,接下来我们来看ngx_http_process_request_headers函数,前面的ngx_http_process_request_line函数已经搞定了最初的请求数据,那么ngx_http_process_request_headers函数是用来处理请求的header信息的,
c = rev->data; //获取connection
r = c->data; //获取request
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http process request header line");
//如果超时的话,那么直接关闭
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
c->timedout = 1;
ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
函数首先还是获取相应的connection以及request结构,然后再判断是否已经超时了,
//获取配置
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
接下来获取一些配置结构。
/每一次读取解析一个header
for ( ;; ) {
if (rc == NGX_AGAIN) {
n = ngx_http_read_request_header(r); //读取header,然后保存在header_in 当中
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
rc = ngx_http_parse_header_line(r, r->header_in,
cscf->underscores_in_headers); //解析header的一行
接下来还是进入for循环来读取header信息,这里还涉及到当buffer不够用扩大的代码,没有贴出来。调用ngx_http_read_request_header函数读取header信息,并保存在header_in缓冲当中,接着调用ngx_http_parse_header_line函数来解析刚刚读取的一条header信息。接下来还会涉及到对解析出来的header信息进行处理的代码,将其变成key-value的结构,并将其保存到headers_in的header链表中存储起来。
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
r->request_length += r->header_in->pos - r->header_name_start;
r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
rc = ngx_http_process_request_header(r); ///* 根据host匹配虚拟主机,并对一些header初始化 */ ,说白了就是对头部的一些效验
if (rc != NGX_OK) {
return;
}
ngx_http_process_request(r); // 真正的处理request
return;
}
最后,当header相关的处理完成了以后,先调用ngx_http_process_request_header函数对header进行一些效验,然后调用ngx_http_process_request函数真正的开始处理request。
好了,接下来我们来看ngx_http_process_request函数吧,该函数很简单,首先是将以前设置的定时器删除,然后
c->read->handler = ngx_http_request_handler; //大概是处理子请求的吧
c->write->handler = ngx_http_request_handler;
r->read_event_handler = ngx_http_block_reading;
ngx_http_handler(r); //为跑一遍所有phase hander做准备。
//处理subrequest
ngx_http_run_posted_requests(c);
设置读事件的handler,这些貌似是用于处理子请求的,然后调用ngx_http_handler函数来处理这个request,准备跑一边所有的phase。
好,接下来来看ngx_http_handler函数:
//初始化phase handler
if (!r->internal) {
switch (r->headers_in.connection_type) {
case 0:
r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
break;
case NGX_HTTP_CONNECTION_CLOSE:
r->keepalive = 0;
break;
case NGX_HTTP_CONNECTION_KEEP_ALIVE:
r->keepalive = 1;
break;
}
r->lingering_close = (r->headers_in.content_length_n > 0);
r->phase_handler = 0;
} else {
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
r->phase_handler = cmcf->phase_engine.server_rewrite_index; //设置当前所在的phase handler
}
首先是初始化request的phase handler,接下来就是
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r); //调用ngx_http_core_run_phases跑一遍phase
调用ngx_http_core_run_phases函数来跑一边phase。接下来来看
ngx_http_core_run_phases函数:
//该函数用于对request跑一遍phase
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); //获取ngx_http_core_main_conf_t
ph = cmcf->phase_engine.handlers; //获取所有的phase handler
/* 遍历phase上注册的所有handler,这里是以r->phase_handler为索引组成的链表 */
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}
该函数很简单,因为nginx把整个http的分成了11个phase,每一个phase都有自己的handler,那么我们按照这个顺序调用完以后,相当于这个http连接也就完成了。
下一篇文章来讲nginx的http phase相关的东西吧。