在建立连接过程中,对于nginx监听到的每个客户端连接,都会将它的读事件的handler设置为ngx_http_init_request函数,这个函数就是请求处理的入口。在处理请求时,主要就是要解析http请求,比如:uri,请求行等,然后再根据请求生成响应。下面看一下nginx处理的具体过程。
在ngx_http_init_connection函数中,将连接的读事件的handler设置为这个函数,在客户端发送请求时会被调用。
/* ngx_event_t的data域存放事件对应的连接句柄 */ c = rev->data; /* 在ngx_init_connection中对读事件添加了timer,超时直接返回*/ if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_http_close_connection(c); return; } /* 连接处理的request的计数 */ c->requests++; /* ngx_http_connection_t */ hc = c->data; 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; if (r) { ngx_memzero(r, sizeof(ngx_http_request_t)); r->pipeline = hc->pipeline; if (hc->nbusy) { r->header_in = hc->busy[0]; } } else { /* 为request分配空间 */ 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; r->http_connection = hc; c->sent = 0; r->signature = NGX_HTTP_MODULE;
上面一段代码完成获取连接、请求并进行一部分初始化工作,比如会为新的请求分配内存。
/** * ngx_listening_t的servers存放监听同一端口的server,但它们的监听的地址可以不同。 * * port * / | \ * addr1 addr2 addr3 * | | | * conf1 conf2 conf3 */ port = c->listening->servers; r->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; /* 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; } } /* virtual hosts based on the address:port */ r->virtual_names = addr_conf->virtual_names; /* the default server configuration for the address:port */ cscf = addr_conf->default_server; /* 初始化为default server的配置,后续虚拟主机匹配成功会采用对应的配置 */ r->main_conf = cscf->ctx->main_conf; r->srv_conf = cscf->ctx->srv_conf; r->loc_conf = cscf->ctx->loc_conf;
注释中解释的很清楚,这段代码是为地址addr:port匹配server config,从而确定该请求的配置。由于nginx支持虚拟主机,所以这里确定了r->virtual_names是该addr:port对应的虚拟主机数组,后面会根据请求的HOST匹配对应的虚拟主机从而确定最终的配置。nginx的每个请求都有执行环境,这个环境就是ngx_request_t请求的main_conf、srv_conf和loc_conf。请求的执行环境就是nginx各个模块的配置信息,根据这些信息的不同请求的处理效果是不一样的。待解释完虚拟主机匹配后,再详细说明执行环境查找。
rev->handler = ngx_http_process_request_line; r->read_event_handler = ngx_http_block_reading;将连接读事件的handler设置为ngx_http_process_request_line,在本方法的最后会直接调用rev->handler(rev)。为什么这么做?
我的猜测是,在客户端第一次请求时,该连接的读事件的handler是ngx_init_request,用于处理话请求,后续请求直接复用,所以不需要执行ngx_init_request,所以需要将rev->handler设置成ngx_http_process_request_line,最后直接调用handler是为了在ngx_init_request中处理第一个请求。不知道这样对不对?
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); c->log->file = clcf->error_log->file; if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { c->log->log_level = clcf->error_log->log_level; } /* initialise the temporary buffer for the request's connection */ if (c->buffer == NULL) { c->buffer = ngx_create_temp_buf(c->pool, cscf->client_header_buffer_size); if (c->buffer == NULL) { ngx_http_close_connection(c); return; } } /* raw request header string will be stored in the connection's buffer */ if (r->header_in == NULL) { r->header_in = c->buffer; } /* initialise the memory pool of the request */ r->pool = ngx_create_pool(cscf->request_pool_size, c->log); if (r->pool == NULL) { ngx_http_close_connection(c); return; }初始化连接的日志信息,为连接分配临时buffer,将request的header_in指向该临时buffer,后面会看到header_in用于存放请求头信息。然后为该请求分配内存池。
if (ngx_list_init(&r->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_destroy_pool(r->pool); ngx_http_close_connection(c); return; } // ? r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); ngx_http_close_connection(c); return; }为http响应header分配空间,request的ctx属性目前不知道干什么用的。。。
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); /** * 为所有变量在此请求中分配空间 * Allocate memory space for the variable values of the request. * The type of variables array is ngx_http_variable_value_t. */ r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { ngx_destroy_pool(r->pool); ngx_http_close_connection(c); return; }nginx中的变量的生命周期是基于请求的,也就是说请求之间的变量时独立的。request的variables相当于变量的值,cmcf->variables相当于变量的定义。为了处理变量,nginx实现了一个小型的脚本引擎,也就是一个解释器。在解析配置文件时生成了代码,在处理请求时去执行这个代码,具体就是在ngx_http_rewrite_module注册的phase handler中处理的,后面介绍nginx变量机制时再详细说明。
这里就是为请求分配所有变量的占用空间。
c->single_connection = 1; c->destroyed = 0; /* 主请求 */ r->main = r; r->count = 1; /* Store start time of the request */ tp = ngx_timeofday(); r->start_sec = tp->sec; r->start_msec = tp->msec; /* HTTP method, e.g. POST, GET, PUT */ r->method = NGX_HTTP_UNKNOWN; r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1; r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; /* 子请求个数的限制 */ r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1; r->http_state = NGX_HTTP_READING_REQUEST_STATE; ctx = c->log->data; ctx->request = r; ctx->current_request = r; r->log_handler = ngx_http_log_error_handler; #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif /* Start processing request */ rev->handler(rev);对连接和请求的属性进行初始化,后面讲解subrequest时再具体分析r->main和r->subrequests。最后调用rev->handler(rev)实际就是调用ngx_http_process_request_line。
这个函数是用来处理请求行的,会不断调用ngx_http_read_request_header从socket读取头部并保存在request的header_in字段中,实际上header_in是一个缓冲区,就是指向连接的临时缓冲区。
c = rev->data; r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http process request 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; }从rev中获取连接以及请求,并判断是否超时。
http请求行格式是:
GET /index.php HTTP/1.1接下来的for循环就是试图从socket中读取足够的信息,然后解析出HTTP Method、URI以及HTTP版本等信息。
if (rc == NGX_AGAIN) { /** * 读取请求行,保存到r->header_in缓冲区 */ n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { return; } }读取request line并保存在header_in缓冲区。
/** * 解析请求行,解析后的信息的是以r->uri_start,r->uri_end,r->arg_start等 * 一些指针存储的,这些指针指向r->header_in */ rc = ngx_http_parse_request_line(r, r->header_in);解析请求行,ngx_http_request_t中比如uri_start、uri_end、arg_start、request_start、request_end等指针用于请求行的解析,识别各个属性在header_in缓冲区中的指针的位置。ngx_http_parse_request_line在ngx_http_parse.c中,用于解析请求行,实际上就是一个状态机的实现,所有的状态包括:
enum { sw_start = 0, sw_method, sw_spaces_before_uri, sw_schema, sw_schema_slash, sw_schema_slash_slash, sw_host, 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;每个状态表示解析到哪一步,根据输入字符决定下一状态。这里不纠结于状态机的实现,只要记住在本方法调用完后请求行解析完毕,所有信息保存在request的字段中;要不就是解析没有完成,进行下一次迭代;要不就是解析出错。
解析成功后,会根据ngx_http_request_t中用于解析的指针去初始化request中的属性,比如uri、args等。如果使用的是complex uri会进入其解析过程。
/* the request line has been parsed successfully */ r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; if (r->args_start) { r->uri.len = r->args_start - 1 - r->uri_start; } else { r->uri.len = r->uri_end - r->uri_start; }初始化request_line属性,是一个ngx_str_t,然后初始化uri的长度。
if (r->complex_uri || r->quoted_uri) { r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1); if (r->uri.data == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); rc = ngx_http_parse_complex_uri(r, cscf->merge_slashes); if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid request"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } } else { r->uri.data = r->uri_start; }初始化uri的data字段,如果使用complex uri就进入其解析过程,否则直接赋值为uri_start。
/* 未解析的uri */ r->unparsed_uri.len = r->uri_end - r->uri_start; r->unparsed_uri.data = r->uri_start; r->valid_unparsed_uri = r->space_in_uri ? 0 : 1; /* http method */ r->method_name.len = r->method_end - r->request_start + 1; r->method_name.data = r->request_line.data; if (r->http_protocol.data) { r->http_protocol.len = r->request_end - r->http_protocol.data; } if (r->uri_ext) { if (r->args_start) { r->exten.len = r->args_start - 1 - r->uri_ext; } else { r->exten.len = r->uri_end - r->uri_ext; } r->exten.data = r->uri_ext; } /* 请求参数 */ if (r->args_start && r->uri_end > r->args_start) { r->args.len = r->uri_end - r->args_start; r->args.data = r->args_start; }接下来对其他的属性初始化。
/* 解析并验证host */ if (r->host_start && r->host_end) { host = r->host_start; n = ngx_http_validate_host(r, &host, r->host_end - r->host_start, 0); if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid host in request line"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } if (n < 0) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } /* 初始化request header的server字段 */ r->headers_in.server.len = n; r->headers_in.server.data = host; }
解析并验证host,然后初始化request header的server字段。
if (r->http_version < NGX_HTTP_VERSION_10) { /** * 根据请求的host,即虚拟主机名,去查找对应的server */ if (ngx_http_find_virtual_server(r, r->headers_in.server.data, r->headers_in.server.len) == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } ngx_http_process_request(r); return; }如果使用的http协议版本小于1.0,那么不支持header以及请求体,所以接下来直接调用ngx_http_process_request处理请求。前面已经介绍过根据请求的addr:port确定了该请求的虚拟主机集合,接下来就要根据请求的host确定具体是哪个虚拟主机来处理该请求,通过ngx_http_find_virtual_server处理,r->headers_in.server存放的就是host。这个函数处理的很简单,因为已经将该addr:port对应的虚拟主机集合组织成map,首先是根据host从这个map中get,如果有对应的虚拟主机直接返回。如果没有,但是nginx提供正则表达式支持,则逐一进行匹配直到找一个为止。找到host对应的虚拟主机后,最重要的是将request的执行环境设置为该虚拟主机对应的配置。ngx_http_process_request后面会详说。对于http0.9然后会直接返回,而1.0或1.1还有没有处理完。
/* Initialise list for request headers */ if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } /* Initialise array for request cookies */ if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } c->log->action = "reading client request headers";因为http1.0和http1.1支持header和cookie,这里先为它们分配空间,后面再解析这些数据。
/* process request headers */ rev->handler = ngx_http_process_request_headers; ngx_http_process_request_headers(rev); return;设置读事件rev的handler为ngx_http_process_request_headers,然后调用ngx_http_process_request_headers进行header解析。
下面先看看for循环的最后部分,主要是错误处理和缓冲区扩容。
if (rc != NGX_AGAIN) { /* there was error while a request line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; }请求解析出错,这里会终结这个请求,并log。
/* NGX_AGAIN: a request line parsing is still incomplete */ if (r->header_in->pos == r->header_in->end) { rv = ngx_http_alloc_large_header_buffer(r, 1); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (rv == NGX_DECLINED) { r->request_line.len = r->header_in->end - r->request_start; r->request_line.data = r->request_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE); return; } }rc等于NGX_AGAIN,说起request line没有处理完毕,如果header_in大小不够,则调用ngx_http_alloc_large_header_buffer进行扩容。
这个函数处理request header。
c = rev->data; r = c->data; 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; }获取连接和请求,并判断超时。接下来for循环会一次解析一个header。
if (r->header_in->pos == r->header_in->end) { rv = ngx_http_alloc_large_header_buffer(r, 0); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (rv == NGX_DECLINED) { p = r->header_name_start; r->lingering_close = 1; if (p == NULL) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too large request"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); return; } len = r->header_in->end - p; if (len > NGX_MAX_ERROR_STR - 300) { len = NGX_MAX_ERROR_STR - 300; p[len++] = '.'; p[len++] = '.'; p[len++] = '.'; } ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%*s\"", len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); return; } } n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { return; }首先还是先判断缓冲区大小是否够用,如果不够用就扩容,扩容失败就会退出。然后调用ngx_http_read_request_header读取请求并存到header_in缓冲区。
rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers);调用ngx_http_parse_header_line解析header,其内部实现和ngx_http_parse_request_line一样,都是状态机,这个函数会一次解析一个header,实际上就是一行。
if (r->invalid_header && cscf->ignore_invalid_headers) { /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%*s\"", r->header_end - r->header_name_start, r->header_name_start); continue; }解析到不合法的header,log然后解析下一个header。否则,说明成功解析了一个header,会进行header的初始化。
/* r->header_hash是当前解析得到的header的hash值 */ h->hash = r->header_hash; h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (h->key.len == r->lowcase_index) { ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { ngx_strlow(h->lowcase_key, h->key.data, h->key.len); }把解析出的header添加到request的headers_in.header链表中,然后对其初始化,包括:header的name、value等。
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { return; }
在http模块的初始化函数ngx_http_block中,会调用ngx_http_init_headers_in_hash将所有可能的header初始化成hash map,然后保存至ngx_http_core_main_conf_t的headers_in_hash字段中。在ngx_http_request.c中,定义的ngx_http_headers_in数组就是所有可能的header。这里会查看解析出的header是否在这个map,如果在的话会调用对应的handler进行初始化。
如果rc等于NGX_HTTP_PARSE_HEADER_DONE,说明header解析完毕,接下来会执行:
if (rc == NGX_HTTP_PARSE_HEADER_DONE) { /* a whole header has been parsed successfully */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); r->request_length += r->header_in->pos - r->header_in->start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; /* 根据host匹配虚拟主机,并对一些header初始化 */ rc = ngx_http_process_request_header(r); if (rc != NGX_OK) { return; } ngx_http_process_request(r); return; }会调用ngx_http_process_request_header函数解析虚拟主机,并对一些header初始化。然后调用ngx_http_process_request进行后续的请求处理。ngx_http_process_request_headers的最后部分就是错误处理,这里不详述,下面看一下请求的处理部分。
这个函数驱动请求的处理,并为处理做些准备。
/* remove timer of read event of the connection */ if (c->read->timer_set) { ngx_del_timer(c->read); }删除掉之前设置的读事件的timer。
c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; r->read_event_handler = ngx_http_block_reading;设置读写事件的handler为ngx_http_request_handler,这个函数貌似是处理subrequest的,还不敢确定。
ngx_http_handler(r); // 处理subrequest ngx_http_run_posted_requests(c);调用ngx_http_handler继续处理请求,ngx_http_run_posted_requests函数是处理子请求的。
为跑一遍所有phase hander做准备。
r->connection->log->action = NULL; r->connection->unexpected_eof = 0; /** * 初始化r->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; }初始化request的phase_handler,实际上就是第一个执行的phase handler在handlers数组中的下标。
r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r);设置request的write_event_handler为ngx_http_core_run_phases,然后再调用这个函数把请求对应的phase handler执行一遍。下面看这个函数。
nginx把请求的处理划分成11个阶段,其中一些阶段可以自定义添加handler,这个函数就是执行请求对应对应所有的phase handler。对应nginx对phase handler的处理会在之后的文章中分析。
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers;ngx_http_core_main_conf_t的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; } }
遍历phase上注册的所有handler,这里是以r->phase_handler为索引组成的链表。在调用每个handler的checker时会更新request的phase_handler,从而实现一个handler的链表。
在跑完所有的phase handler之后,这个请求就被处理完毕。实际的响应内容的输出,是在content phase的handler中调用filter输出实现的。下一篇文章会介绍nginx的phase handler的处理,后面还会介绍filter实现。