05 proxy_pass 携带有 uri 的场景下面的处理

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了05 proxy_pass 携带有 uri 的场景下面的处理相关的知识,希望对你有一定的参考价值。

前言

这里主要是关注 proxy_pass 的配置, 对于 代理都上游服务的请求的相关处理的实现差异 

比如 如下配置是否存在什么差异, 造成的结果是什么 

        location ^~ /api/ 
               root html; 
               index  index.html index.htm;
               proxy_pass http://localhost:8080/api/;
        
        location ^~ /api/ 
               root html; 
               index  index.html index.htm;
               proxy_pass http://localhost:8080/api;
        

现在假设存在一个上游服务 http://localhost:8080/api/HelloWorld/listFormWithoutHeader?param01=p01&param02=p02

上面的配置是可以正常代理的, 但是 下面的配置响应的是 404 

本文探讨的就是类似的问题 

以下截图, 调试基于 nginx-1.18.0

proxy_pass http://localhost:8080/api/ 的场景

这里首先是从 proxy_pass 中获取是否有 uri 参数, 这里为 "/api/" 

然后获取到的 loc_len 为 匹配到的 location 的字符串的长度, 这里为 "/api/" 长度为 5 

然后 下面是拼接 ctx.vars.uri 和 请求路径截取掉 location 匹配的字符串, "/api/" + "HelloWorld/listFormWithoutHeader", 最终得到路径 "/api/HelloWorld/listFormWithoutHeader"

Breakpoint 9, ngx_http_proxy_create_request (r=0x7f8b34801c50)
    at src/http/modules/ngx_http_proxy_module.c:1207
1207	    if (plcf->proxy_lengths && ctx->vars.uri.len) 
(gdb) print ctx->vars
$40 = key_start = len = 21, 
    data = 0x7f8b33808f5b "http://localhost:8080/api/", schema = len = 7, 
    data = 0x7f8b33808f5b "http://localhost:8080/api/", host_header = 
    len = 14, data = 0x7f8b33808f62 "localhost:8080/api/", port = len = 4, 
    data = 0x7f8b33808f6c "8080/api/", uri = len = 5, 
    data = 0x7f8b33808f70 "/api/"
(gdb) next
1215	        loc_len = (r->valid_location && ctx->vars.uri.len) ?
(gdb) next
1216	                      plcf->location.len : 0;
(gdb) next
1215	        loc_len = (r->valid_location && ctx->vars.uri.len) ?
(gdb) next
1218	        if (r->quoted_uri || r->space_in_uri || r->internal) 
(gdb) print loc_len 
$41 = 5
(gdb) c
Continuing.

Breakpoint 10, ngx_http_proxy_create_request (r=0x7f8b34801c50)
    at src/http/modules/ngx_http_proxy_module.c:1332
1332	    if (plcf->proxy_lengths && ctx->vars.uri.len) 
(gdb) next
1339	        if (r->valid_location) 
(gdb) next
1340	            b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
(gdb) next
1343	        if (escape) 
(gdb) next
1349	            b->last = ngx_copy(b->last, r->uri.data + loc_len,
(gdb) print r->uri 
$42 = len = 37, 
  data = 0x7f8b34801804 "/api/HelloWorld/listFormWithoutHeader?param01=p01&param02=p02 HTTP/1.1\\r\\nHost"
(gdb) print loc_len
$43 = 5
(gdb) print b->start 
$44 = (u_char *) 0x7f8b34803408 "GET /api/"
(gdb) next
1353	        if (r->args.len > 0) 
(gdb) print b->start 
$45 = (u_char *) 0x7f8b34803408 "GET /api/HelloWorld/listFormWithoutHeader"
(gdb) next
1354	            *b->last++ = '?';
(gdb) next
1355	            b->last = ngx_copy(b->last, r->args.data, r->args.len);
(gdb) next
1359	    u->uri.len = b->last - u->uri.data;
(gdb) print b->start 
$46 = (u_char *) 0x7f8b34803408 "GET /api/HelloWorld/listFormWithoutHeader?param01=p01&param02=p02"

proxy_pass http://localhost:8080/api 的场景

同样的道理 

这里首先是从 proxy_pass 中获取是否有 uri 参数, 这里为 "/api" 

然后获取到的 loc_len 为 匹配到的 location 的字符串的长度, 这里为 "/api/" 长度为 5 

然后 下面是拼接 ctx.vars.uri 和 请求路径截取掉 location 匹配的字符串, "/api" + "HelloWorld/listFormWithoutHeader", 最终得到路径 "/apiHelloWorld/listFormWithoutHeader"

然后 在上游服务那边, 找不到对应的服务, 因此 响应的是 404 

Breakpoint 9, ngx_http_proxy_create_request (r=0x7fdbf9000450)
    at src/http/modules/ngx_http_proxy_module.c:1207
1207	    if (plcf->proxy_lengths && ctx->vars.uri.len) 
(gdb) print ctx.vars
$47 = key_start = len = 21, 
    data = 0x7fdbf701eb5b "http://localhost:8080/api", schema = len = 7, 
    data = 0x7fdbf701eb5b "http://localhost:8080/api", host_header = 
    len = 14, data = 0x7fdbf701eb62 "localhost:8080/api", port = len = 4, 
    data = 0x7fdbf701eb6c "8080/api", uri = len = 4, 
    data = 0x7fdbf701eb70 "/api"
(gdb) next
1215	        loc_len = (r->valid_location && ctx->vars.uri.len) ?
(gdb) next
1216	                      plcf->location.len : 0;
(gdb) next
1215	        loc_len = (r->valid_location && ctx->vars.uri.len) ?
(gdb) next
1218	        if (r->quoted_uri || r->space_in_uri || r->internal) 
(gdb) print loc_len
$48 = 5
(gdb) c
Continuing.

Breakpoint 10, ngx_http_proxy_create_request (r=0x7fdbf9000450)
    at src/http/modules/ngx_http_proxy_module.c:1332
1332	    if (plcf->proxy_lengths && ctx->vars.uri.len) 
(gdb) next
1339	        if (r->valid_location) 
(gdb) next
1340	            b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
(gdb) next
1343	        if (escape) 
(gdb) print b->start
$49 = (u_char *) 0x7fdbf9001c08 "GET /api"
(gdb) next
1349	            b->last = ngx_copy(b->last, r->uri.data + loc_len,
(gdb) next
1353	        if (r->args.len > 0) 
(gdb) print b->start
$50 = (u_char *) 0x7fdbf9001c08 "GET /apiHelloWorld/listFormWithoutHeader"
(gdb) next
1354	            *b->last++ = '?';
(gdb) next
1355	            b->last = ngx_copy(b->last, r->args.data, r->args.len);
(gdb) next
1359	    u->uri.len = b->last - u->uri.data;
(gdb) print b->start
$51 = (u_char *) 0x7fdbf9001c08 "GET /apiHelloWorld/listFormWithoutHeader?param01=p01&param02=p02"

结论

综合一下结论 

在 proxy_pass 存在 uri 部分的场景下面 

最终向上游服务器请求的路径是 ctx.uri + url中截取掉location之后的子串  

和三个部分是有关系的, 一个是 proxy_pass 影响的是 ctx.uri 

一个是请求的 url, 一个是 location 本身 

以上是关于05 proxy_pass 携带有 uri 的场景下面的处理的主要内容,如果未能解决你的问题,请参考以下文章

proxy_pass使用内置变量报错

Nginx 每个位置/将重写的 uri 传递给 proxy_pass

单独服务器上的 nginx proxy_pass 到多个 Rails 应用程序,乘客独立在不同的盒子中具有子 URI

如何更改 nginx proxy_pass 中的 request_uri?

nginx中配置proxy_pass详解,末尾带不带斜杠/的区别

nginx proxy_pass和rewrite的区别