lua location capture has empty upstream add

344 views
Skip to first unread message

Gaurav Saxena

unread,
Oct 6, 2017, 12:05:30 AM10/6/17
to openresty-en
Hi All,

I am using lua location capture to make calls to another path within the same config. If I make calls to that path directly, I can see "$upstream_addr" set in that case. But, if I use the lua location capture those variables are not set. 
Is there any particular reason for that? Is there any way for me to set those variables for location capture case?

Thanks for the help.

Gaurav Saxena

unread,
Oct 9, 2017, 1:27:05 PM10/9/17
to openresty-en
Bump! Any help would be appreciated :).

tokers

unread,
Oct 9, 2017, 9:31:58 PM10/9/17
to openresty-en
Could you provide a minimum configuration instance?

Gaurav Saxena

unread,
Oct 10, 2017, 1:38:06 AM10/10/17
to openresty-en
Sure. An example would be below - 

server {
   log_format main ' "$upstream_response_time" "$upstream_header_time"';
   access_log .... main;

   location /test {
      content_by_lua_block {
       ........ additional logic
       local res = ngx.location.capture('/foo')
    }
   }

   location /foo {
      proxy_pass http://127.0.0.1:5000;
   }
}

If I call "/foo" directly, the "$upstream_response_time" is correctly set. But, if I call "/test", the "$upstream_response_time" is empty. Is there any way for me to set the time in the "content_by_lua_block"?

tokers

unread,
Oct 10, 2017, 3:34:59 AM10/10/17
to openresty-en
This is expected, basically visiblility of variables is per-request, since you access the /foo by ngx.location.capture, the $upstream_response_time in your main request is unafftected.

Of course that's ok if you want to shared some variables with your subrequests, see the document about ngx.location.capture for the details.

Gaurav Saxena

unread,
Oct 10, 2017, 12:31:49 PM10/10/17
to openresty-en
Thanks for the quick response. I should have read the documentation more clearly. 

The option to do this would be "share_all_vars", the documentation does say this is a bad idea - 

  • share_all_vars specify whether to share all the Nginx variables of the subrequest with the current (parent) request. modifications of the Nginx variables in the subrequest will affect the current (parent) request. Enabling this option may lead to hard-to-debug issues due to bad side-effects and is considered bad and harmful. Only enable this option when you completely know what you are doing.
Is there a better way to do this? For example, just copy upstream vars from subrequest to the parent request. Also, if making multiple sub requests in the lua code, any way to set upstream as a list of values?

tokers

unread,
Oct 10, 2017, 9:42:06 PM10/10/17
to openresty-en
Maybe you can use the ngx.exec(internal redirect) replace the ngx.location.capture?

Gaurav Saxena

unread,
Oct 11, 2017, 12:16:24 PM10/11/17
to openresty-en
Appreciate the response. 

I have additional logic after location capture which needs to be executed in error conditions. I cannot do that using ngx.exec since the request processing would terminate after that call. 

tokers

unread,
Oct 11, 2017, 9:53:36 PM10/11/17
to openresty-en
All right, i think store the $upstream_addr to a HTTP response header in your subrequest is a good idea, your main request can get this response header easily.

Gaurav Saxena

unread,
Oct 12, 2017, 1:58:13 AM10/12/17
to openresty-en
Thanks for the suggestion, that's a good idea. I'll give that a try.
Appreciate the help :).

Gaurav Saxena

unread,
Oct 13, 2017, 1:22:08 PM10/13/17
to openresty-en
I tried both share_all_vars and the header idea. Unfortunately neither of those actually work.

Even with ngx.location.capture('/bar', {share_all_vars = true}), I don't see upstream variables being set. Are those variables not shared?

If I set upstream response time as a header "more_set_headers 'Upstream_time: $upstream_response_time';", it results in a really strange value e.g.  Upstream_time: 1507915223.173 while its recorded in access log correctly as - 
"127.0.0.1 - - [13/Oct/2017:10:20:23 -0700] "GET /bar HTTP/1.1" 200 14 "-" "curl/7.54.0" "-" "0.086" "0.086""

Not sure what's going on here. Maybe some magical way in which nginx deals with the upstream variables.

tokers

unread,
Oct 14, 2017, 4:36:34 AM10/14/17
to openresty-en
I tried both share_all_vars and the header idea. Unfortunately neither of those actually work.
Even with ngx.location.capture('/bar', {share_all_vars = true}), I don't see upstream variables being set. Are those variables not shared? 

After reading the definition of this variable, i found this variable has the flag NGX_HTTP_VAR_NOCACHEABLE, every time we call ngx.var.upstream_response_time,
the get_handler will be triggered, so we cannot get the subrequest's $upstream_response_time value.

If I set upstream response time as a header "more_set_headers 'Upstream_time: $upstream_response_time';", it results in a really strange value e.g.  Upstream_time: 1507915223.173 while its recorded in access log correctly as - 
"127.0.0.1 - - [13/Oct/2017:10:20:23 -0700] "GET /bar HTTP/1.1" 200 14 "-" "curl/7.54.0" "-" "0.086" "0.086""

The get_handler of $upstream_response_time calculates the value from the  r->upstream->state->response_time, the response_time will be assigned with the current time when connecting to the upstream for the first time and it will be updated when finaizing the request. That's why you see the value "1507915223.173."

There is a compromise way that we can add a body_filter_by_lua hook and check the ngx.arg[2], when it is true, save the $upstream_response_time to our custom varible like "$my_upstream_response_time". I have written a simple configuration and it seems to work.

server {
    listen 8081;
    server_name localhost;
    location / {
        content_by_lua_block {
            local res = ngx.location.capture("/proxy", { share_all_vars = true })
            ngx.say(ngx.var.my_upstream_response_time)
        }
    }

    location /proxy {
        set $my_upstream_response_time "";
        proxy_pass http://127.0.0.1:8082/;
        body_filter_by_lua_block {
            if ngx.arg[2] == true then
                ngx.var.my_upstream_response_time = ngx.var.upstream_response_time
            end
        }
    }
}

server {
    listen 8082;
    server_name _;

    location / {
        return 200 "Plain";
    }
}


Gaurav Saxena

unread,
Oct 18, 2017, 1:45:38 AM10/18/17
to openresty-en
Thanks a lot for looking into this. 

I am assuming body filter by lua will be called before finalizing the request and hence has the right values set. Is Upstream Response time the third parameter to filter? Where did "2" come from? Is there any documentation or code link that I can follow to find out the numbers for other variables as well?

tokers

unread,
Oct 18, 2017, 7:43:26 AM10/18/17
to openresty-en
Thanks a lot for looking into this. 

I am assuming body filter by lua will be called before finalizing the request and hence has the right values set. Is Upstream Response time the third parameter to filter? Where did "2" come from? Is there any documentation or code link that I can follow to find out the numbers for other variables as well?


the second element holds the boolean flag for the "eof" flag indicating the end of the whole output data stream.

Gaurav Saxena

unread,
Oct 20, 2017, 7:11:22 PM10/20/17
to openresty-en
Ah, thanks for the information. 
This is really helpful. I was able to see this working as well. I am assuming that I can't do this without variable sharing since I can't set a header with body_filter. Overall, I should be able to get it work though seems quite complicated. Hopefully, in future there's a better way to do this :).
Reply all
Reply to author
Forward
0 new messages