容器端口映射导致 302 存在问题 以及 nginx 对于 302 的 Location 的重写
中描述了如果 上游服务 sendRedirect 到所在域的其他服务之后, 来到 nginx 这一层, nginx 会将这个转发的服务更新为 nginx 所在的域
那么 假设上游服务 sendRedirect 到其他域的服务呢 ?
另外 就是在 探究这个问题的时候, 使用了一下 proxy_redirect, 这个 proxy_redirect 又是如何处理的呢?
以下截图, 调试基于 nginx-1.18.0
- location ^~ /api/ {
- root html;
- index index.html index.htm;
- proxy_pass http://localhost:8080/;
- }
sendRedirect 的服务, 是部署在 8080 端口上面
通过 nginx 访问发现出现 302, 跳转的域是 给定的目标域, nginx 没有重写
location 的重写模块这里有一个对于域的判断, 如果 不是当前域直接 DECLINED, 不走后面的 rewrite 的处理
另外还有一个细节是 这个流程的 location 还是存放在 request 的 header 列表, 没有写到 request.headers_out.location 里面
响应的 location 和 proxy_pass 的 域不一致, 直接走 DECLINED
- Breakpoint 1, ngx_http_proxy_rewrite_complex_handler (r=0x7f98e2000a50,
- h=0x7f98e2001000, prefix=0, len=54, pr=0x7f98e0806ac0)
- at src/http/modules/ngx_http_proxy_module.c:2630
- 2630 if (pattern.len > len
- (gdb) print pattern
- $1 = {len = 22, data = 0x7f98e0803b5b "http://localhost:8080/"}
- (gdb) print h->value
- $2 = {len = 54,
- data = 0x7f98e2002859 "http://localhost:8083/HelloWorld/listFormWithoutHeader"}
- (gdb) list
- 2625
- 2626 if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {
- 2627 return NGX_ERROR;
- 2628 }
- 2629
- 2630 if (pattern.len > len
- 2631 || ngx_rstrncmp(h->value.data + prefix, pattern.data,
- 2632 pattern.len) != 0)
- 2633 {
- 2634 return NGX_DECLINED;
- (gdb) next
- 2631 || ngx_rstrncmp(h->value.data + prefix, pattern.data,
- (gdb) next
- 2632 pattern.len) != 0)
- (gdb) next
- 2631 || ngx_rstrncmp(h->value.data + prefix, pattern.data,
- (gdb) next
- 2632 pattern.len) != 0)
- (gdb) next
- 2630 if (pattern.len > len
- (gdb) next
- 2634 return NGX_DECLINED;
增加 proxy_redirect 的配置如下
- location ^~ /api/ {
- root html;
- index index.html index.htm;
- proxy_pass http://localhost:8080/;
- proxy_redirect 80 83;
- proxy_redirect http://localhost http://127.0.0.1;
- }
来到 ngx_http_proxy_rewrite_redirect 的循环处理
可以看到 这里配置到的两个 proxy_redirect 分别为 80 -> 83, http://localhost -> http://127.0.0.1
响应的 location 为 http://localhost:8083/HelloWorld/listFormWithoutHeader
使用 80 -> 83 的配置的时候, 发现 location 不以 80 开头, 放弃处理
使用 http://localhost -> http://127.0.0.1 配置的时候, 执行了替换处理, 更新之后的 location 为 http://127.0.0.1:8083/HelloWorld/listFormWithoutHeader
- Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050,
- h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a8090c0)
- at src/http/modules/ngx_http_proxy_module.c:2630
- 2630 if (pattern.len > len
- (gdb) bt
- #0 ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050,
- h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a8090c0)
- at src/http/modules/ngx_http_proxy_module.c:2630
- #1 0x000000010aacb893 in ngx_http_proxy_rewrite_redirect (r=0x7fd48a814050,
- h=0x7fd48a814600, prefix=0)
- at src/http/modules/ngx_http_proxy_module.c:2524
- #2 0x000000010aa894ed in ngx_http_upstream_rewrite_location (
- r=0x7fd48a814050, h=0x7fd48a82ee70, offset=0)
- at src/http/ngx_http_upstream.c:5100
- #3 0x000000010aa8d9bd in ngx_http_upstream_process_headers (r=0x7fd48a814050,
- u=0x7fd48a82e5e0) at src/http/ngx_http_upstream.c:2806
- #4 0x000000010aa8ede7 in ngx_http_upstream_process_header (r=0x7fd48a814050,
- u=0x7fd48a82e5e0) at src/http/ngx_http_upstream.c:2432
- #5 0x000000010aa8e7e5 in ngx_http_upstream_handler (ev=0x10ac1f208)
- at src/http/ngx_http_upstream.c:1286
- #6 0x000000010aa57c10 in ngx_kqueue_process_events (cycle=0x7fd48a80c050,
- timer=13648, flags=1) at src/event/modules/ngx_kqueue_module.c:669
- #7 0x000000010aa479f6 in ngx_process_events_and_timers (cycle=0x7fd48a80c050)
- at src/event/ngx_event.c:247
- #8 0x000000010aa55ac5 in ngx_worker_process_cycle (cycle=0x7fd48a80c050,
- data=0x0) at src/os/unix/ngx_process_cycle.c:750
- #9 0x000000010aa52cfa in ngx_spawn_process (cycle=0x7fd48a80c050,
- proc=0x10aa55a10 <ngx_worker_process_cycle>, data=0x0,
- name=0x10ab1da1e "worker process", respawn=-3)
- at src/os/unix/ngx_process.c:199
- #10 0x000000010aa54be7 in ngx_start_worker_processes (cycle=0x7fd48a80c050,
- n=1, type=-3) at src/os/unix/ngx_process_cycle.c:359
- #11 0x000000010aa54558 in ngx_master_process_cycle (cycle=0x7fd48a80c050)
- at src/os/unix/ngx_process_cycle.c:131
- #12 0x000000010aa0c8ba in main (argc=3, argv=0x7ffee51f4548)
- at src/core/nginx.c:382
- (gdb) frame 1
- #1 0x000000010aacb893 in ngx_http_proxy_rewrite_redirect (r=0x7fd48a814050,
- h=0x7fd48a814600, prefix=0)
- at src/http/modules/ngx_http_proxy_module.c:2524
- 2524 rc = pr[i].handler(r, h, prefix, len, &pr[i]);
- (gdb) print pr[0]
- $3 = {handler = 0x10aac8a80 <ngx_http_proxy_rewrite_complex_handler>,
- pattern = {complex = {value = {len = 2, data = 0x7fd48a808fef "80"},
- flushes = 0x0, lengths = 0x0, values = 0x0, u = {size = 0}},
- regex = 0x2}, replacement = {value = {len = 2,
- data = 0x7fd48a808ff2 "83"}, flushes = 0x0, lengths = 0x0, values = 0x0,
- u = {size = 0}}}
- (gdb) print pr[1]
- $4 = {handler = 0x10aac8a80 <ngx_http_proxy_rewrite_complex_handler>,
- pattern = {complex = {value = {len = 16,
- data = 0x7fd48a809097 "http://localhost"}, flushes = 0x0,
- lengths = 0x0, values = 0x0, u = {size = 0}}, regex = 0x10},
- replacement = {value = {len = 16, data = 0x7fd48a8090a8 "http://127.0.0.1"},
- flushes = 0x0, lengths = 0x0, values = 0x0, u = {size = 0}}}
- (gdb) print plcf->redirects->nelts
- $5 = 2
- (gdb) print h->value
- $6 = {len = 54,
- data = 0x7fd48a82f059 "http://localhost:8083/HelloWorld/listFormWithoutHeader"}
- (gdb) frame0
- Undefined command: "frame0". Try "help".
- (gdb) frame 0
- #0 ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050,
- h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a8090c0)
- at src/http/modules/ngx_http_proxy_module.c:2630
- 2630 if (pattern.len > len
- (gdb) next
- 2631 || ngx_rstrncmp(h->value.data + prefix, pattern.data,
- (gdb) next
- 2632 pattern.len) != 0)
- (gdb) next
- 2631 || ngx_rstrncmp(h->value.data + prefix, pattern.data,
- (gdb) next
- 2632 pattern.len) != 0)
- (gdb) next
- 2630 if (pattern.len > len
- (gdb) next
- 2634 return NGX_DECLINED;
- (gdb) next
- 2642 }
- (gdb) print pattern
- $7 = {len = 2, data = 0x7fd48a808fef "80"}
- (gdb) c
- Continuing.
-
- Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7fd48a814050,
- h=0x7fd48a814600, prefix=0, len=54, pr=0x7fd48a809128)
- at src/http/modules/ngx_http_proxy_module.c:2630
- 2630 if (pattern.len > len
- (gdb) print pattern
- $8 = {len = 16, data = 0x7fd48a809097 "http://localhost"}
- (gdb) print h->value
- $9 = {len = 54,
- data = 0x7fd48a82f059 "http://localhost:8083/HelloWorld/listFormWithoutHeader"}
- (gdb) next
- 2631 || ngx_rstrncmp(h->value.data + prefix, pattern.data,
- (gdb) next
- 2632 pattern.len) != 0)
- (gdb) next
- 2631 || ngx_rstrncmp(h->value.data + prefix, pattern.data,
- (gdb) next
- 2632 pattern.len) != 0)
- (gdb) next
- 2630 if (pattern.len > len
- (gdb) next
- 2637 if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
- (gdb) next
- 2641 return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement);
- (gdb) next
- 2642 }
- (gdb) print h->value
- $10 = {len = 54,
- data = 0x7fd48a82f059 "http://127.0.0.1:8083/HelloWorld/listFormWithoutHeader"}
可以参见 容器端口映射导致 302 存在问题 以及 nginx 对于 302 的 Location 的重写
的场景
是可以看到 nginx 默认增加了一个 "http://localhost:8080/" -> "/api/" 的配置来处理, nginx 拿到 Location 响应头之后重写为相对路径
我们可以看一下
更新配置为如下
因为携带有 uri, 如果是上游本域的 302 需要将 上游本域信息更新为当前域, 因此有一个 "http://localhost:8080/" -> "/api/" 的替换操作
- location ^~ /api/ {
- root html;
- index index.html index.htm;
- proxy_pass http://localhost:8080/;
- }
可以看到 确实是存在一条 "http://localhost:8080/" -> "/api/" 的替换操作
- Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7fec8a800450,
- h=0x7fec8a800a00, prefix=0, len=54, pr=0x7fec89001ec0)
- at src/http/modules/ngx_http_proxy_module.c:2630
- 2630 if (pattern.len > len
- (gdb) frame 1
- #1 0x0000000100d2d893 in ngx_http_proxy_rewrite_redirect (r=0x7fec8a800450,
- h=0x7fec8a800a00, prefix=0)
- at src/http/modules/ngx_http_proxy_module.c:2524
- 2524 rc = pr[i].handler(r, h, prefix, len, &pr[i]);
- (gdb) print pr[0]
- $11 = {handler = 0x100d2aa80 <ngx_http_proxy_rewrite_complex_handler>,
- pattern = {complex = {value = {len = 22,
- data = 0x7fec88008f5b "http://localhost:8080/"}, flushes = 0x0,
- lengths = 0x0, values = 0x0, u = {size = 0}}, regex = 0x16},
- replacement = {value = {len = 5, data = 0x7fec88007a94 "/api/"},
- flushes = 0x0, lengths = 0x0, values = 0x0, u = {size = 0}}}
- (gdb) print plcf->redirects->nelts
- $12 = 1
这个替换的配置来自于
proxy_pass 带 uri, "/api/" -> "http://10.60.50.16:8081/"
proxy_pass 不带 uri, "/" -> "http://10.60.50.16:8081/"
配置文件如下, 增加了一条无用的 proxy_redirect
- location ^~ /api/ {
- root html;
- index index.html index.htm;
- proxy_pass http://localhost:8080/;
- proxy_redirect 80 83;
- }
可以发现 上游服务响应的是上游服务所在的域的地址, 然后 nginx 拿到该地址之后 没有更新域为当前域
因为 已经存在 proxy_redirect, 没有添加 "http://localhost:8080/" -> "/api/" 的替换操作
拿到运行时的数据信息, 可以看到没有 "http://localhost:8080/" -> "/api/" 的替换操作
- Breakpoint 2, ngx_http_proxy_rewrite_complex_handler (r=0x7ffd25000a50,
- h=0x7ffd25001000, prefix=0, len=54, pr=0x7ffd2400f820)
- at src/http/modules/ngx_http_proxy_module.c:2630
- 2630 if (pattern.len > len
- (gdb) frame 1
- #1 0x0000000107375893 in ngx_http_proxy_rewrite_redirect (r=0x7ffd25000a50,
- h=0x7ffd25001000, prefix=0)
- at src/http/modules/ngx_http_proxy_module.c:2524
- 2524 rc = pr[i].handler(r, h, prefix, len, &pr[i]);
- (gdb) print plcf->redirects->nelts
- $13 = 1
- (gdb) print pr[0]
- $14 = {handler = 0x107372a80 <ngx_http_proxy_rewrite_complex_handler>,
- pattern = {complex = {value = {len = 2, data = 0x7ffd2400f7ef "80"},
- flushes = 0x0, lengths = 0x0, values = 0x0, u = {size = 0}},
- regex = 0x2}, replacement = {value = {len = 2,
- data = 0x7ffd2400f7f2 "83"}, flushes = 0x0, lengths = 0x0, values = 0x0,
- u = {size = 0}}}
- (gdb)
配置如下 proxy_redirect default;
运行时拿到的 替换配置如下 "/api/" -> "http://localhost:8080/"
这个和 “如果 proxy_pass 携带的有 uri, 并且没有配置 proxy_redirect” 创建的替换配置一样
proxy_pass 带 uri, "/api/" -> "http://10.60.50.16:8081/"
proxy_pass 不带 uri, "/" -> "http://10.60.50.16:8081/"
完