nginx location 配置及解析 -- ngx_http_core_find_location

2016-01-18 23:11:47   最后更新: 2016-01-29 23:03:07   访问数量:744




在阅读 nginx 源码的过程中,我们多次提到了 nginx 的 location 配置:

Nginx 内核源码解析

http 模块配置解析

 

location 是 ngx_http_core_module 中十分重要的指令,负责了单次 http 请求的各种配置信息的管理

本篇博客主要介绍一下 nginx location 的配置

 

规则([] 表示可选):

location [=|~|~*|^~|@] /uri/ { … }

 

 

这个配置主要是用来供 nginx 根据请求 uri 决定使用哪些配置入口

 

如上所述,有 5 种可选的配置前缀:

  1. ~ -- 正则匹配(区分大小写)
  2. ~*  -- 正则匹配(不区分大小写)
  3. = -- 精确匹配
  4. ^~ -- 只匹配字符串,不查询正则表达式
  5. @ -- 制定一个命名的 location

对于上述所有的前缀,如果前面加 ! 则表示强制不匹配

 

在 nginx rewrite 规则的介绍中,我们曾经介绍过 @ 前缀,事实上,@ 前缀一般来说也只用于 rewrite 规则的配置:

nginx rewrite 规则的配置

 

上述前缀中,只有 ~ 和 ~* 是正则匹配,对于 nginx 来说,优先级最高的是 = 匹配,接下来是普通匹配,最后会去匹配正则匹配

使用如下规则:

  1. 如果 = 匹配符合,会立即使用该 location 配置,而不会对后续 location 配置进行匹配
  2. 对于 @ 匹配,只有请求是内部重定向请求时才会使用,普通请求不会进行匹配
  3. 对于没有匹配成功上述两种匹配的所有普通匹配的 location,nginx 进行最长前缀匹配,匹配结果与配置文件中 location 的编辑顺序并没有关系
  4. 得到普通匹配的最长前缀匹配 location 以后,如果这个 location 不是 ^~ 匹配(标识不查询正则表达式),呢么并不会结束匹配过程,而是会继续进行正则匹配,按照配置文件中 location 的编辑顺序得到首个匹配成功的正则匹配 location
  5. 如果同时匹配到了普通匹配和正则匹配的 location 结果,nginx 会选取正则匹配的 location 作为最终的匹配结果

 

在 nginx 源码 11 个处理阶段的介绍中,我们提到了 NGX_HTTP_FIND_CONFIG_PHASE 阶段,及他的 checker ngx_http_core_find_config_phase:

HTTP 请求处理的 11 个阶段 -- ngx_http_handler

 

下面的代码验证了我们上面提到的过程:

// ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, // ngx_http_phase_handler_t *ph) // NGX_HTTP_FIND_CONFIG_PHASE 阶段 checker,根据请求 URI 匹配 location 表达式 {{{ 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; // 根据 URI 查找 location 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); // 非内部请求访问内部 location 是非法的 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); // 根据匹配的 location 设置 request 的属性 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; } // }}}

 

 

这段代码中,调用了 ngx_http_core_find_location 函数查找对应的 location 配置:

// static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r) // 根据请求 URI 查找对应的 location {{{ static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_core_loc_conf_t *pclcf; #if (NGX_PCRE) ngx_int_t n; ngx_uint_t noregex; ngx_http_core_loc_conf_t *clcf, **clcfp; noregex = 0; #endif pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); // 静态查找最大前缀匹配 rc = ngx_http_core_find_static_location(r, pclcf->static_locations); if (rc == NGX_AGAIN) { #if (NGX_PCRE) clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); noregex = clcf->noregex; #endif /* look up nested locations */ rc = ngx_http_core_find_location(r); } if (rc == NGX_OK || rc == NGX_DONE) { return rc; } /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */ #if (NGX_PCRE) // 通过正则匹配查找 URI 对应的 location if (noregex == 0 && pclcf->regex_locations) { for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "test location: ~ \"%V\"", &(*clcfp)->name); n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri); if (n == NGX_OK) { r->loc_conf = (*clcfp)->loc_conf; /* look up nested locations */ rc = ngx_http_core_find_location(r); return (rc == NGX_ERROR) ? rc : NGX_OK; } if (n == NGX_DECLINED) { continue; } return NGX_ERROR; } } #endif return rc; } // }}}

 

 

这里,首先调用了 ngx_http_core_find_static_location 在 ngx_http_optimize_servers 函数中创建的三叉排序树中递归查询,匹配最大前缀匹配:

// static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r, // ngx_http_location_tree_node_t *node) // 通过最大前缀匹配查找 URI 对应的 location {{{ static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r, ngx_http_location_tree_node_t *node) { u_char *uri; size_t len, n; ngx_int_t rc, rv; len = r->uri.len; uri = r->uri.data; rv = NGX_DECLINED; for ( ;; ) { if (node == NULL) { return rv; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "test location: \"%*s\"", node->len, node->name); n = (len <= (size_t) node->len) ? len : node->len; rc = ngx_filename_cmp(uri, node->name, n); if (rc != 0) { node = (rc < 0) ? node->left : node->right; continue; } if (len > (size_t) node->len) { if (node->inclusive) { r->loc_conf = node->inclusive->loc_conf; rv = NGX_AGAIN; node = node->tree; uri += n; len -= n; continue; } /* exact only */ node = node->right; continue; } if (len == (size_t) node->len) { if (node->exact) { r->loc_conf = node->exact->loc_conf; return NGX_OK; } else { // 需递归查询 r->loc_conf = node->inclusive->loc_conf; return NGX_AGAIN; } } /* len < node->len */ // 完全匹配 if (len + 1 == (size_t) node->len && node->auto_redirect) { r->loc_conf = (node->exact) ? node->exact->loc_conf: node->inclusive->loc_conf; rv = NGX_DONE; } node = node->left; } } // }}}

 

 

接着,对于仍需查询的匹配,调用 ngx_http_regex_exec 使用 pcre 进行正则匹配

 

 

 

http://nginx.org/en/docs/http/ngx_http_core_module.html#location

 






技术帖      服务器      技术分享      nginx      server      webserver      location      ngx_http_core_module      url      uri     


京ICP备15018585号