在nginx的配置文件nginx.conf中,我们在配置server的时候,会配置一下location指令,这个location指令是提供给用户来配置对于符合指令的http请求,采用该指令内部的处理方式。这里面分成两步
第一步:nginx系统分析用户定义nginx.conf中server的location,将配置信息保存在内存里面,保存的数据结构方式可以参考前面第五部分说的。
location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; include fastcgi_params; }
第二步:nginx启动后,客户端发送来http请求,根据请求的HOST查找到配置的server,根据POST 或者GET等的链接地址,匹配合适的location,选择配置的location配置信息来执行http请求的处理过程。下面是一个简单php脚本,处理用户上传文件
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>test</title> </head> <body> <form action="" method='post' enctype="multipart/form-data" > <h3>添加图片</h3> <input type="hidden" name='ft' value="down" /> 选择要上传的文件: <input type="file" name="img_file" id="img_file" /> <input type="submit" name="submit_file" value="upload" /> </form> </body> </html> <?php var_dump($_FILES);
我们从服务端抓包看到的协议信息,很上传的内容。为了便于阅读,做了简单的处理。
POST(空格)/test/testUpload.php(空格)HTTP/1.1(/r/n) Host:(空格)10.221.20.175(/r/n) User-Agent:(空格)Mozilla/5.0.(Windows.NT.6.1;.rv:35.0).Gecko/20100101.Firefox/35.0(/r/n) Accept:(空格)text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8(/r/n)
Accept-Language:.zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3(/r/n) Accept-Encoding:(空格)gzip,(空格)deflate(/r/n) Referer:(空格)http://10.221.20.175/test/testUpload.php(/r/n) Connection:(空格)keep-alive(/r/n) Content-Type:(空格)multipart/form-data;(空格)boundary=---------------------------19247233154350(/r/n) Content-Length:(空格)450(/r/n)(/r/n)
-----------------------------11242276917535(/r/n)
Content-Disposition:(空格)form-data;(空格)name="ft"(/r/n)(/r/n)
down(/r/n)
-----------------------------11242276917535(/r/n)
Content-Disposition:(空格)form-data;(空格)name="img_file";(空格)filename="default"(/r/n)
Content-Type:.application/octet-stream(/r/n)(/r/n)
#.You.may.add.here.your.#.server.{..
-----------------------------11242276917535(/r/n)
Content-Disposition:(空格)form-data;(空格)name="submit_file"(/r/n)(/r/n)
upload(/r/n)
-----------------------------11242276917535--(/r/n)
下面,我们从nginx的源码来看看整个过程。
用户配置了一个server,其中配置一段location,那么nginx在分析到location的时候主要完成以下几步
1、nginx对server中location指令的分析在ngx_http_core_module.c中
{ ngx_string("location"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, ngx_http_core_location, NGX_HTTP_SRV_CONF_OFFSET, 0, NULL },
2、分析到location指令,会执行ngx_http_core_location函数,该函数完成以下几个步骤
* 申请一个变量ctx,包含三个指针,第一个指针指向http的主配置信息,一个指向该location所在server的配置信息,另外一个指向location的配置信息
* 申请一段内存,内存内每个指针指向各自http模块的loc_conf,初始化loc_conf
* 分析location的参数
* 分析location里面的指令,例如下面的root,fastcgi_pass等,完成配置信息的定制过程。
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { char *rv; u_char *mod; size_t len; ngx_str_t *value, *name; ngx_uint_t i; ngx_conf_t save; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; ngx_http_core_loc_conf_t *clcf, *pclcf; ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; } pctx = cf->ctx; ctx->main_conf = pctx->main_conf; ctx->srv_conf = pctx->srv_conf; ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); if (ctx->loc_conf == NULL) { return NGX_CONF_ERROR; } for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[i]->ctx; if (module->create_loc_conf) { ctx->loc_conf[ngx_modules[i]->ctx_index] = module->create_loc_conf(cf); if (ctx->loc_conf[ngx_modules[i]->ctx_index] == NULL) { return NGX_CONF_ERROR; } } } clcf = ctx->loc_conf[ngx_http_core_module.ctx_index]; clcf->loc_conf = ctx->loc_conf; value = cf->args->elts; if (cf->args->nelts == 3) { len = value[1].len; mod = value[1].data; name = &value[2]; if (len == 1 && mod[0] == '=') { clcf->name = *name; clcf->exact_match = 1; } else if (len == 2 && mod[0] == '^' && mod[1] == '~') { clcf->name = *name; clcf->noregex = 1; } else if (len == 1 && mod[0] == '~') { if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; } } else if (len == 2 && mod[0] == '~' && mod[1] == '*') { if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { return NGX_CONF_ERROR; } } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid location modifier \"%V\"", &value[1]); return NGX_CONF_ERROR; } } else { name = &value[1]; if (name->data[0] == '=') { clcf->name.len = name->len - 1; clcf->name.data = name->data + 1; clcf->exact_match = 1; } else if (name->data[0] == '^' && name->data[1] == '~') { clcf->name.len = name->len - 2; clcf->name.data = name->data + 2; clcf->noregex = 1; } else if (name->data[0] == '~') { name->len--; name->data++; if (name->data[0] == '*') { name->len--; name->data++; if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { return NGX_CONF_ERROR; } } else { if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; } } } else { clcf->name = *name; if (name->data[0] == '@') { clcf->named = 1; } } } pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index]; if (pclcf->name.len) { /* nested location */ #if 0 clcf->prev_location = pclcf; #endif if (pclcf->exact_match) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" cannot be inside " "the exact location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; } if (pclcf->named) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" cannot be inside " "the named location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; } if (clcf->named) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "named location \"%V\" can be " "on the server level only", &clcf->name); return NGX_CONF_ERROR; } len = pclcf->name.len; #if (NGX_PCRE) if (clcf->regex == NULL && ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #else if (ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) #endif { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" is outside location \"%V\"", &clcf->name, &pclcf->name); return NGX_CONF_ERROR; } } if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) { return NGX_CONF_ERROR; } save = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_LOC_CONF; rv = ngx_conf_parse(cf, NULL); *cf = save; return rv; }
3、下面是location参数信息的存储结构,一个server中所有的lcoation参数,例如 location /, location ~ \.php$ 等会经过分析后,存储到每个server的loc_conf指向的ngx_http_core_module配置信息项location指向的双向链表中。
4、nginx将配置信息location分析后,存储到相应的数据结构中,关于对于location参数的处理,例如,location /,location ~ \.php$,参数分别是/ 和~ \.php$ 如何分析,以什么样的形式存入到相应的数据结构中,这里不再展开。 在将location规则存入后,下面就是nginx处理客户端请求,去匹配location规则。
5、ngx_http_request.c 记录了nginx对客户端请求的分析过程,最后,会启用"解析函数组"去处理这次请求
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); ph = cmcf->phase_engine.handlers; while (ph[r->phase_handler].checker) { rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); if (rc == NGX_OK) { return; } } }
6、“解析函数组”可以理解成一种工作流,针对一个请求,整个处理流程中,存在server 重定向,查找location配置,location内部重定向等等,关于细节会在后面再研究,这里我们看查找location配置,ngx_http_core_find_config_phase
ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { u_char *p; size_t len; ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; r->content_handler = NULL; r->uri_changed = 0; rc = ngx_http_core_find_location(r); if (rc == NGX_ERROR) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (!r->internal && clcf->internal) { ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); return NGX_OK; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "using configuration \"%s%V\"", (clcf->noname ? "*" : (clcf->exact_match ? "=" : "")), &clcf->name); ngx_http_update_location_config(r); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http cl:%O max:%O", r->headers_in.content_length_n, clcf->client_max_body_size); if (r->headers_in.content_length_n != -1 && !r->discard_body && clcf->client_max_body_size && clcf->client_max_body_size < r->headers_in.content_length_n) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large body: %O bytes", r->headers_in.content_length_n); r->expect_tested = 1; (void) ngx_http_discard_request_body(r); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE); return NGX_OK; } if (rc == NGX_DONE) { ngx_http_clear_location(r); r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } /* * we do not need to set the r->headers_out.location->hash and * r->headers_out.location->key fields */ if (r->args.len == 0) { r->headers_out.location->value = clcf->name; } else { len = clcf->name.len + 1 + r->args.len; p = ngx_pnalloc(r->pool, len); if (p == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } r->headers_out.location->value.len = len; r->headers_out.location->value.data = p; p = ngx_cpymem(p, clcf->name.data, clcf->name.len); *p++ = '?'; ngx_memcpy(p, r->args.data, r->args.len); } ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY); return NGX_OK; } r->phase_handler++; return NGX_AGAIN; }