Happy holidays all,

I ran into some odd behavior with multiple accesses of the `request_id` Nginx variable via the 'ngx.var` API. Consider the following example:

location /orvar {
 content_by_lua_block {
  ngx.say(ngx.var.request_id)
  ngx.say(ngx.var.request_id)
  ngx.say(ngx.var.request_id)
 }
}

This produces three distinct values:

$ curl localhost:8080/orvar
c57276ee45246776f5b1d868da1c2483
cee11ac0ed7926477302c0406ba7d3d1
21ea6af7fabdc0324d98526ea595a7b8

One would expect these values should be identical. NGX_HTTP_VAR_NOCACHEABLE is not set for this variable: https://github.com/nginx/nginx/blob/746fba0d79c6909e9e09b4d1cb9ddbf052ab545e/src/http/ngx_http_variables.c#L291.

Loading or not loading resty.core has no impact on behavior.

When defining the variable into the config directly via the add_header directive, things work as intuition would lead:

    server {
        listen       8080;
        server_name  localhost;

        location /orvar {
add_header X-Foo $request_id;
add_header X-Bar $request_id;

content_by_lua_block {
  ngx.say(ngx.var.request_id)
  ngx.say(ngx.var.request_id)
  ngx.say(ngx.var.request_id)
}
}
}

$ curl -vvv localhost:8080/orvar * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8080 (#0) > GET /orvar HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 200 OK < Server: openresty/1.13.6.1 < Date: Fri, 28 Dec 2018 04:32:11 GMT < Content-Type: application/octet-stream < Transfer-Encoding: chunked < Connection: keep-alive < X-Foo: 004c648700ecbe25db8719b1fe0fb2d8 < X-Bar: 004c648700ecbe25db8719b1fe0fb2d8 < 004c648700ecbe25db8719b1fe0fb2d8 004c648700ecbe25db8719b1fe0fb2d8 004c648700ecbe25db8719b1fe0fb2d8 * Connection #0 to host localhost left intact

However, when the headers are added via a lua-nginx-module header_filter handler, we see the same incorrect behavior:

header_filter_by_lua_block {
  ngx.header["X-Foo"] = ngx.var.request_id
  ngx.header["X-Bar"] = ngx.var.request_id
}
$ curl -vvv localhost:8080/orvar
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /orvar HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
< HTTP/1.1 200 OK
< Server: openresty/1.13.6.1
< Date: Fri, 28 Dec 2018 04:36:15 GMT
< Content-Type: application/octet-stream
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Foo: ead844bb511d73552aa60c66d926b583
< X-Bar: 67c36be8692e9ede3f5433811ef80c1b
45ef1042902969f5380208b2244e60fe
0f5368da218d33a24040b470ecccbc41
473473815be63b11e616fed52d672163
* Connection #0 to host localhost left intact

This makes me feel like there's an odd implementation within the lua-nginx-module var accesses. From what I can grok, I'm guessing it's because the variable is never indexed (http://lxr.nginx.org/source/src/http/ngx_http_variables.c#0673), so the handler is called for every access from the var metatable. I don't see a good immediate solution for this that doesn't involve declaring the $request_id variable to be defined within the Nginx config itself. Anyone run into this before, with a workaround that doesn't involve some hack like using ngx.ctx?
    5 days later
    G'Day!

    The difference behavior between the add_header and ngx_lua's variable API is caused by Nginx's script engine.

    add_header treats the second paramter as a "complex value", and will be parsed by Nginx's script engine, this engine
    indexed any variables in this "complex value", it processes the "complex value" for twice, one for length, the other for
    the value, so index variables can reduce the overhead. Of course some "no cacheable" variables value will be flushed before
    next time this "complex value" is interpreted.

    While ngx_lua just call ngx_http_get_variable, which just fetch them, but never check their flags, like NGX_HTTP_VAR_NOCACHEABLE
    or others, maybe we can post an issue to the repository?


      Write a Reply...