之前曾经做过一个测试, 测试结果如下
- nginx 访问文件
- 如果文件存在, 获取文件
- 如果文件不存在, 文件夹存在, 获取文件夹的 index
- 如果都不存在 响应 403/404
然后 后面更加详细的测试了一下, 梳理了一下 结论
- 请求 匹配到 /staticTryFiles 的时候, 查找是否有文件, 如果 有文件, 响应文件
- 如果没有文件, 有文件夹, 跳转到 /staticTryFiles/, 如果有 index 文件, 响应 index
- 如果没有 index 文件, 响应 403
- 如果有文件夹, 跳转到 "/staticTryFiles/", 这个的处理是在 ngx_http_static_module.ngx_http_static_handler 中, 响应了一个 sendRedirect "/staticTryFiles/"
- 如果没有文件夹, 响应 404
- ngx_http_static_module.ngx_http_static_handler 中响应错误码 NGX_HTTP_NOT_FOUND, 外层 ngx_http_core_content_phase 发现错误码不是 NGX_DECLINED, 发送响应给客户端
-
-
- 请求 匹配到 /staticTryFiles/ 的时候, 如果有 index 文件, 响应 index
- 如果没有 index 文件, 响应 403
- 这里是所有的 index 尝试完毕之后, 所有的 phase_checker 都处理完成之后
- 最后一个 static_handler 的结果依然为 DECLINED, 走的 ngx_http_core_module.ngx_http_core_content_phase 之后响应的 FORBIDDEN
- 这个处理是在 ngx_http_index_module 中, 需要确保 r.uri 以 "/" 结尾, 确保 method 为 HEAD/GET/POST
- 获取 index 的相关配置, 然后遍历 index 列表
- 尝试添加 index 到 uri, 然后尝试 打开文件, 如果 打开没有问题, 直接 内部跳转 获取给定的文件返回
- 这个内部跳转是在当前请求的基础上, 更新了 r.uri 之后, 递归走 handler chain
- 如果文件存在, 但是存在其他问题, 权限, 文件名过长 响应 相应的错误信息
- EMLINK : too many links, 响应 403 - FORBIDDEN
- ELOOP : too many symbolic links encountered, 响应 403 - FORBIDDEN
- ENOTDIR : not directory, 响应 404 - NOTFOUND
- ENAMETOOLONG : file name too long, 响应 404 - NOTFOUND
- EACCESS : permission denied, 响应 403 - FORBIDDEN
- ENOENT : 如果是 index文件 不存在, 则 continue 下一个 index
以下截图, 调试基于 nginx-1.18.0
根据上下文 uri 以及 root 拼接完整的请求文件路径的地方
- (gdb) list
- 72 /*
- 73 * ngx_http_map_uri_to_path() allocates memory for terminating '\0'
- 74 * so we do not need to reserve memory for '/' for possible redirect
- 75 */
- 76
- 77 last = ngx_http_map_uri_to_path(r, &path, &root, 0);
- 78 if (last == NULL) {
- 79 return NGX_HTTP_INTERNAL_SERVER_ERROR;
- 80 }
- 81
- (gdb) print r.uri
- $4 = {len = 15, data = 0x7ff9f8800004 "/staticTryFiles HTTP/1.1\r\nHost"}
- (gdb) print path
- $5 = {len = 140711584440536,
- data = 0xf801a118 <error: Cannot access memory at address 0xf801a118>}
- (gdb) next
- 78 if (last == NULL) {
- (gdb) print path
- $7 = {len = 44,
- data = 0x7ff9f8801148 "/usr/local/nginx/html/static/staticTryFiles"}
直接 响应文件内容给客户端, contentType 的配置参见 nginx 是如何自动推导文件的 content-type 的
调试查看运行时的信息, 文件为 "/usr/local/nginx/html/static/staticTryFiles", 大小为 25b
根据后缀推导 contentType 没有找到合适的, 使用的默认的 application/octet-stream
- Breakpoint 3, ngx_http_static_handler (r=0x7ff9f8800450)
- at src/http/modules/ngx_http_static_module.c:268
- 268 return ngx_http_output_filter(r, &out);
- (gdb) print b->file->name
- $11 = {len = 43,
- data = 0x7ff9f8801148 "/usr/local/nginx/html/static/staticTryFiles"}
- (gdb) b->file_pos
- Undefined command: "b->file_pos". Try "help".
- (gdb) print b->file_pos
- $12 = 0
- (gdb) print b->file_last
- $13 = 25
- (gdb) print r->headers_out
- $14 = {headers = {last = 0x7ff9f8800638, part = {elts = 0x7ff9f8800a00,
- nelts = 2, next = 0x0}, size = 48, nalloc = 20, pool = 0x7ff9f8800400},
- trailers = {last = 0x7ff9f8800670, part = {elts = 0x7ff9f8800dc0, nelts = 0,
- next = 0x0}, size = 48, nalloc = 4, pool = 0x7ff9f8800400},
- status = 200, status_line = {len = 0, data = 0x0}, server = 0x0, date = 0x0,
- content_length = 0x0, content_encoding = 0x0, location = 0x0, refresh = 0x0,
- last_modified = 0x0, content_range = 0x0, accept_ranges = 0x7ff9f8800a30,
- www_authenticate = 0x0, expires = 0x0, etag = 0x7ff9f8800a00,
- override_charset = 0x0, content_type_len = 24, content_type = {len = 24,
- data = 0x7ff9f8012495 "application/octet-stream"}, charset = {len = 0,
- data = 0x0}, content_type_lowcase = 0x0, content_type_hash = 0,
- cache_control = {elts = 0x0, nelts = 0, size = 0, nalloc = 0, pool = 0x0},
- link = {elts = 0x0, nelts = 0, size = 0, nalloc = 0, pool = 0x0},
- content_length_n = 25, content_offset = 0, date_time = 0,
- last_modified_time = 1656808030}
查看一下 html/static/staticTryFiles 的文件大小, 确实为 25b
- master:nginx jerry$ ll html/static/
- total 16
- -rw-r--r-- 1 jerry wheel 21 Jun 22 22:42 index.html
- -rw-r--r-- 1 jerry wheel 25 Jul 3 08:27 staticTryFiles
浏览器的默认行为为 下载给定的文件
如果是存在 staticTryFiles 的文件夹, ngx_open_cached_file 正常
然后 走了后面的 staticTryFiles 文件夹的相关操作, 是一个 302 跳转到 "/staticTryFiles/"
- Breakpoint 4, ngx_http_static_handler (r=0x7ff9f8009450)
- at src/http/modules/ngx_http_static_module.c:102
- 102 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 103 != NGX_OK)
- (gdb) next
- 102 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 105 switch (of.err) {
- (gdb) next
- 114 level = NGX_LOG_ERR;
- (gdb) next
- 115 rc = NGX_HTTP_NOT_FOUND;
- (gdb) next
- 116 break;
- (gdb) next
- 135 if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
- (gdb) next
- 136 ngx_log_error(level, log, of.err,
- (gdb) next
- 138 }
- (gdb) next
- 140 return rc;
- (gdb) next
- 269
- (gdb) c
- Continuing.
-
- Breakpoint 2, ngx_http_static_handler (r=0x7ff9f9004e50)
- at src/http/modules/ngx_http_static_module.c:77
- 77 last = ngx_http_map_uri_to_path(r, &path, &root, 0);
- (gdb) c
- Continuing.
-
- Breakpoint 4, ngx_http_static_handler (r=0x7ff9f9004e50)
- at src/http/modules/ngx_http_static_module.c:102
- 102 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 103 != NGX_OK)
- (gdb) next
- 102 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 143 r->root_tested = !r->error_page;
- (gdb) next
- 147 if (of.is_dir) {
- (gdb) next
- 151 ngx_http_clear_location(r);
- (gdb) br ngx_http_static_module.c:191
- Breakpoint 5 at 0x106b03d3d: file src/http/modules/ngx_http_static_module.c, line 191.
- (gdb) c
- Continuing.
-
- Breakpoint 5, ngx_http_static_handler (r=0x7ff9f9004e50)
- at src/http/modules/ngx_http_static_module.c:191
- 191 return NGX_HTTP_MOVED_PERMANENTLY;
- (gdb) delete 5
- (gdb) print location
- $15 = (u_char *) 0x7ff9f9005b64 "/staticTryFiles/"
- (gdb) print r.headers_out
- $16 = {headers = {last = 0x7ff9f9005038, part = {elts = 0x7ff9f9005400,
- nelts = 1, next = 0x0}, size = 48, nalloc = 20, pool = 0x7ff9f9004e00},
- trailers = {last = 0x7ff9f9005070, part = {elts = 0x7ff9f90057c0, nelts = 0,
- next = 0x0}, size = 48, nalloc = 4, pool = 0x7ff9f9004e00}, status = 0,
- status_line = {len = 0, data = 0x0}, server = 0x0, date = 0x0,
- content_length = 0x0, content_encoding = 0x0, location = 0x7ff9f9005400,
- refresh = 0x0, last_modified = 0x0, content_range = 0x0,
- accept_ranges = 0x0, www_authenticate = 0x0, expires = 0x0, etag = 0x0,
- override_charset = 0x0, content_type_len = 0, content_type = {len = 0,
- data = 0x0}, charset = {len = 0, data = 0x0}, content_type_lowcase = 0x0,
- content_type_hash = 0, cache_control = {elts = 0x0, nelts = 0, size = 0,
- nalloc = 0, pool = 0x0}, link = {elts = 0x0, nelts = 0, size = 0,
- nalloc = 0, pool = 0x0}, content_length_n = -1, content_offset = 0,
- date_time = 0, last_modified_time = -1}
- (gdb)
打开文件的时候 发现文件不存在, 响应 404 错误码
外部 http_core_content_phase 发送 404 的响应给客户端
- Breakpoint 2, ngx_http_static_handler (r=0x7ff9fa000450)
- at src/http/modules/ngx_http_static_module.c:77
- 77 last = ngx_http_map_uri_to_path(r, &path, &root, 0);
- (gdb) c
- Continuing.
-
- Breakpoint 4, ngx_http_static_handler (r=0x7ff9fa000450)
- at src/http/modules/ngx_http_static_module.c:102
- 102 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 103 != NGX_OK)
- (gdb) next
- 102 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 105 switch (of.err) {
- (gdb) next
- 114 level = NGX_LOG_ERR;
- (gdb) next
- 115 rc = NGX_HTTP_NOT_FOUND;
- (gdb) next
- 116 break;
- (gdb) next
- 135 if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
- (gdb) next
- 136 ngx_log_error(level, log, of.err,
- (gdb) next
- 138 }
- (gdb) next
- 140 return rc;
- (gdb) print rc
- $19 = 404
- (gdb) b ngx_http_core_module.c:1257
- Breakpoint 6 at 0x106ab0043: file src/http/ngx_http_core_module.c, line 1257.
- (gdb) c
- Continuing.
-
- Breakpoint 6, ngx_http_core_content_phase (r=0x7ff9fa000450, ph=0x7ff9f801eb60)
- at src/http/ngx_http_core_module.c:1257
- warning: Source file is more recent than executable.
- 1257 ngx_http_finalize_request(r, rc);
- (gdb) print rc
- $20 = 404
这个处理是在 ngx_http_index_module 中, 需要确保 r.uri 以 "/" 结尾, 确保 method 为 HEAD/GET/POST 获取 index 的相关配置, 然后遍历 index 列表 尝试添加 index 到 uri, 然后尝试 打开文件, 如果 打开没有问题, 直接 内部跳转 获取给定的文件返回 这个内部跳转是在当前请求的基础上, 更新了 r.uri 之后, 递归走 handler chain 如果文件存在, 但是存在其他问题, 权限, 文件名过长 响应 相应的错误信息 EMLINK : too many links, 响应 403 - FORBIDDEN ELOOP : too many symbolic links encountered, 响应 403 - FORBIDDEN ENOTDIR : not directory, 响应 404 - NOTFOUND ENAMETOOLONG : file name too long, 响应 404 - NOTFOUND EACCESS : permission denied, 响应 403 - FORBIDDEN ENOENT : 如果是 index文件 不存在, 则 continue 下一个 index
- Breakpoint 7, ngx_http_index_handler (r=0x7ff9fa000450)
- at src/http/modules/ngx_http_index_module.c:130
- 130 index = ilcf->indices->elts;
- (gdb) next
- 131 for (i = 0; i < ilcf->indices->nelts; i++) {
- (gdb) next
- 133 if (index[i].lengths == NULL) {
- (gdb) print index[0]
- $24 = {name = {len = 12, data = 0x7ff9f801b05e "index1.html"}, lengths = 0x0,
- values = 0x0}
- (gdb) print index[1]
- $26 = {name = {len = 11, data = 0x7ff9f801b06a "index1.htm"}, lengths = 0x0,
- values = 0x0}
- (gdb) next
- 135 if (index[i].name.data[0] == '/') {
- (gdb) next
- 139 reserve = ilcf->max_index_len;
- (gdb) next
- 140 len = index[i].name.len;
- (gdb) next
- 142 } else {
- (gdb) next
- 162 if (reserve > allocated) {
- (gdb) next
- 164 name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
- (gdb) next
- 165 if (name == NULL) {
- (gdb) print name
- $27 = (u_char *) 0x7ff9fa001174 ""
- (gdb) print path
- $28 = {len = 56,
- data = 0x7ff9fa001148 "/usr/local/nginx/html/static/staticTryFiles/"}
- (gdb) next
- 169 allocated = path.data + path.len - name;
- (gdb) next
- 172 if (index[i].values == NULL) {
- (gdb) next
- 176 ngx_memcpy(name, index[i].name.data, index[i].name.len);
- (gdb) next
- 178 path.len = (name + index[i].name.len - 1) - path.data;
- (gdb) next
- 180 } else {
- (gdb) next
- 203 ngx_memzero(&of, sizeof(ngx_open_file_info_t));
- (gdb) next
- 205 of.read_ahead = clcf->read_ahead;
- (gdb) next
- 206 of.directio = clcf->directio;
- (gdb) print name
- $29 = (u_char *) 0x7ff9fa001174 "index1.html"
- (gdb) print path
- $30 = {len = 55,
- data = 0x7ff9fa001148 "/usr/local/nginx/html/static/staticTryFiles/index1.html"}
- (gdb) next
- 207 of.valid = clcf->open_file_cache_valid;
- (gdb) next
- 208 of.min_uses = clcf->open_file_cache_min_uses;
- (gdb) next
- 209 of.test_only = 1;
- (gdb) next
- 210 of.errors = clcf->open_file_cache_errors;
- (gdb) next
- 211 of.events = clcf->open_file_cache_events;
- (gdb) next
- 213 if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
- (gdb) next
- 217 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 218 != NGX_OK)
- (gdb) next
- 217 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
- (gdb) next
- 262 uri.len = r->uri.len + len - 1;
- (gdb) next
- 264 if (!clcf->alias) {
- (gdb) next
- 265 uri.data = path.data + root;
- (gdb) next
- 267 } else {
- (gdb) next
- 277 return ngx_http_internal_redirect(r, &uri, &r->args);
- (gdb) print uri
- $31 = {len = 27, data = 0x7ff9fa001164 "/staticTryFiles/index1.html"}
如果所有的 index 文件均不存在, ngx_http_core_content_phase 最后响应一个 HTTP_FORBIDDEN
- Breakpoint 7, ngx_http_index_handler (r=0x7ff9f7810050)
- at src/http/modules/ngx_http_index_module.c:130
- 130 index = ilcf->indices->elts;
- (gdb) b ngx_http_core_module.c:1279
- Breakpoint 8 at 0x106ab0129: file src/http/ngx_http_core_module.c, line 1279.
- (gdb) c
- Continuing.
-
- Breakpoint 8, ngx_http_core_content_phase (r=0x7ff9f7810050, ph=0x7ff9f801eb78)
- at src/http/ngx_http_core_module.c:1279
- 1279 ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
- (gdb) list
- 1274 if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
- 1275 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
- 1276 "directory index of \"%s\" is forbidden", path.data);
- 1277 }
- 1278
- 1279 ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
- 1280 return NGX_OK;
- 1281 }
- 1282
- 1283 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
完