openresty/lua-upstream-nginx-module

Implicit upstream not set when proxy_pass is set from a variable

zachwalton opened this issue · 5 comments

Using openresty/1.11.2.3:

Test config:

daemon off;
http {
    server {
        listen 8080;
        location /upstream {
            content_by_lua_block {
                ngx.say("upstream")
            }
        }
        location /works {
            log_by_lua_block {
                local upstream = require 'ngx.upstream'
                print(upstream.current_upstream_name())
            }
            proxy_pass http://localhost:8080/upstream;
        }
        location /doesnt {
            set $proxy_location http://localhost:8080/upstream;
            log_by_lua_block {
                local upstream = require 'ngx.upstream'
                print(upstream.current_upstream_name())
            }
            proxy_pass $proxy_location;
        }
    }
}
root@29e1d2b24175:~# curl http://localhost:8080/works
127.0.0.1 - - [18/May/2017:02:51:01 +0000] "GET /upstream HTTP/1.0" 200 9 "-" "curl/7.35.0"
2017/05/18 02:51:01 [notice] 5656#0: *1 [lua] log_by_lua(test.conf:21):3: localhost:8080 while logging request, client: 127.0.0.1, server: , request: "GET /works HTTP/1.1", upstream: "http://127.0.0.1:8080/upstream", host: "localhost:8080"
127.0.0.1 - - [18/May/2017:02:51:01 +0000] "GET /works HTTP/1.1" 200 9 "-" "curl/7.35.0"
root@29e1d2b24175:~# curl http://localhost:8080/doesnt
127.0.0.1 - - [18/May/2017:02:51:14 +0000] "GET /upstream HTTP/1.0" 200 9 "-" "curl/7.35.0"
2017/05/18 02:51:14 [error] 5656#0: *4 failed to run log_by_lua*: log_by_lua(test.conf:21):3: no srv conf for upstream
stack traceback:
	[C]: in function 'current_upstream_name'
	log_by_lua(test.conf:21):3: in function <log_by_lua(test.conf:21):1> while logging request, client: 127.0.0.1, server: , request: "GET /doesnt HTTP/1.1", upstream: "http://127.0.0.1:8080/upstream", host: "localhost:8080"
127.0.0.1 - - [18/May/2017:02:51:14 +0000] "GET /doesnt HTTP/1.1" 200 9 "-" "curl/7.35.0"

@zachwalton I think it only works with named upstreams, not arbitrary external host names which have no named upstream configuration blocks.

The docs suggest that 'current_upstream_name()' will implicitly include proxy_pass; and indeed it works, just not when setting proxy_pass to the contents of a variable.

I agree that this is a bizarre config that would better be served by named upstreams, so I'm fine with converting our configs over and NTBFing this if you think that's the route to go.

FWIW I was able to work around this with the implicit $proxy_host variable set by nginx_http_proxy_module:

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#variables

        location /also_works{
            set $proxy_location http://localhost:8080/upstream;
            log_by_lua_block {
                print(ngx.var.proxy_host)
            }
            proxy_pass $proxy_location;
        }
root@29e1d2b24175:~# curl http://localhost:8080/also_works
127.0.0.1 - - [18/May/2017:06:05:32 +0000] "GET /upstream HTTP/1.0" 200 9 "-" "curl/7.35.0"
2017/05/18 06:05:32 [notice] 5744#0: *1 [lua] log_by_lua(test.conf:27):2: localhost:8080 while logging request, client: 127.0.0.1, server: , request: "GET /also_works HTTP/1.1", upstream: "http://127.0.0.1:8080/upstream", host: "localhost:8080"
127.0.0.1 - - [18/May/2017:06:05:32 +0000] "GET /also_works HTTP/1.1" 200 9 "-" "curl/7.35.0"

Feel free to close this if you think it's a weird use case, I'm happy with the workaround.

@zachwalton I was talking about this:

upstream foo.com {
    server ...
}

server {
    location / {
        proxy_pass http://foo.com;
        # or
        # set $var "foo.com";
        # proxy_pass http://$var;
    }
}

Makes sense, the problem in our case is that we have a complex config that's already reliant on set $proxy_location http://localhost:8080/upstream; proxy_pass $proxy_location and changing it to use named upstreams would be a bit onerous.

I was willing to do it but ngx.var.proxy_host seems to work with variables even when current_upstream_name() fails. Might be worth updating the docs:

Note that implicit upstreams created by proxy_pass are included, contrary to the output of upstream.get_upstreams().

Changed to:

"Note that implicit upstreams created by proxy_pass are included, contrary to the output of upstream.get_upstreams(), with one exception: setting proxy_pass to the contents of a variable will not work:

set $proxy_location http://127.0.0.1/upstream;
proxy_pass $proxy_location;

You should instead use named upstreams."