From 9eff4ce99161e86e4766c57f4b9d0cc97ea71a9d Mon Sep 17 00:00:00 2001 From: mtrop-godaddy Date: Thu, 11 Jun 2026 12:23:06 +0100 Subject: [PATCH 1/4] Gate W3C trace support to request phases with ngx.var access The http library reads ngx.var.http_traceparent in send_request to propagate W3C trace context. ngx.var is only readable in request-bearing phases, so this crashes code running in init/init_worker. Gate the read on ngx.get_phase() being in a request-bearing phase via a whitelist, so request-less phases (init, init_worker, timer, etc.) are excluded by default. Behavior is unchanged for real requests. --- lib/resty/http.lua | 24 +++++++++++++-- t/21-traceparent-header.t | 61 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/lib/resty/http.lua b/lib/resty/http.lua index 6c6ad42..5f56155 100644 --- a/lib/resty/http.lua +++ b/lib/resty/http.lua @@ -62,6 +62,23 @@ local EXPECTING_BODY = { } +-- ngx.var is only readable in request-bearing phases. Reading it from a +-- request-less phase (init, init_worker, timer, etc.) raises an error, so we +-- gate any ngx.var access on being in one of these phases. +local NGX_VAR_PHASES = { + set = true, + rewrite = true, + server_rewrite = true, + access = true, + content = true, + preread = true, + header_filter = true, + body_filter = true, + log = true, + balancer = true, +} + + -- Reimplemented coroutine.wrap, returning "nil, err" if the coroutine cannot -- be resumed. This protects user code from infinite loops when doing things like -- repeat @@ -739,8 +756,11 @@ function _M.send_request(self, params) if params.version == 1.0 and not headers["Connection"] then headers["Connection"] = "Keep-Alive" end - -- W3C trace context support with NGINX tracer - if not headers["traceparent"] and ngx.var.http_traceparent then + -- W3C trace context support with NGINX tracer. + -- Only read ngx.var in request-bearing phases; doing so during + -- init/init_worker/timer (config preload, background jobs) raises an error. + if NGX_VAR_PHASES[ngx.get_phase()] + and not headers["traceparent"] and ngx.var.http_traceparent then headers["traceparent"] = ngx.var.http_traceparent end diff --git a/t/21-traceparent-header.t b/t/21-traceparent-header.t index fcb42de..d5ade39 100644 --- a/t/21-traceparent-header.t +++ b/t/21-traceparent-header.t @@ -22,6 +22,46 @@ our $HttpConfig = qq{ } }; +# Same as $HttpConfig, but fires a request from a request-less phase +# (an init_worker timer) where ngx.var is disabled. +our $HttpConfigInitWorker = qq{ + lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;"; + error_log logs/error.log debug; + resolver 8.8.8.8 ipv6=off; + + init_by_lua_block { + if $ENV{TEST_COVERAGE} == 1 then + jit.off() + require("luacov.runner").init() + end + + require("resty.http").debug(true) + } + + init_worker_by_lua_block { + local function make_request(premature) + if premature then + return + end + + local http = require "resty.http" + local httpc = http.new() + local res, err = httpc:request_uri("http://www.google.com") + if err then + ngx.log(ngx.ERR, "init_worker request failed: ", err) + end + ngx.log(ngx.INFO, "init_worker request completed") + end + + -- cosockets are unavailable directly in init_worker, so defer to a + -- timer. The timer phase is also request-less (ngx.var disabled). + local ok, err = ngx.timer.at(0, make_request) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + end + } +}; + no_long_string(); #no_diff(); @@ -90,3 +130,24 @@ GET /lua traceparent: 00-000000000000000019f4e02c82857913-11488c6e00d1d248-01 --- error_log traceparent: 00-00000000000000006633c2d00527dd33-1af98f7e6ecd16ff-01 + + +=== TEST 4: A request from a request-less phase does not error reading ngx.var +--- http_config eval: $::HttpConfigInitWorker +--- config + location /lua { + content_by_lua_block { + -- give the init_worker timer time to fire and finish + ngx.sleep(0.5) + ngx.say("ok") + } + } +--- request +GET /lua +--- response_body +ok +--- no_error_log +[error] +API disabled in the context of ngx.timer +--- error_log +init_worker request completed From 4de42a0c447fe02991df02f85a6856bf7be3cfc2 Mon Sep 17 00:00:00 2001 From: mtrop-godaddy Date: Wed, 17 Jun 2026 10:07:29 +0100 Subject: [PATCH 2/4] Allowlist precontent phase and disallow balancer and preread phases --- lib/resty/http.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/resty/http.lua b/lib/resty/http.lua index 5f56155..52fe106 100644 --- a/lib/resty/http.lua +++ b/lib/resty/http.lua @@ -71,11 +71,10 @@ local NGX_VAR_PHASES = { server_rewrite = true, access = true, content = true, - preread = true, header_filter = true, body_filter = true, log = true, - balancer = true, + precontent = true, } From 9dbca1f96cc5988d1c23e057ef3756f85b3dc88e Mon Sep 17 00:00:00 2001 From: mtrop-godaddy Date: Wed, 17 Jun 2026 10:10:59 +0100 Subject: [PATCH 3/4] Update GHA workflows to use actions that were updated to Node.js 24 --- .github/workflows/test.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0bf72ca..14cd81b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,11 +6,11 @@ jobs: luacheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: leafo/gh-actions-lua@v8 + - uses: actions/checkout@v6 + - uses: leafo/gh-actions-lua@v13 with: luaVersion: "luajit-openresty" - - uses: leafo/gh-actions-luarocks@v5 + - uses: leafo/gh-actions-luarocks@v6 with: luarocksVersion: "3.12.2" - run: luarocks install luacheck @@ -33,7 +33,7 @@ jobs: options: --init steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install deps run: | apk add --no-cache curl perl bash wget git perl-dev libarchive-tools nodejs build-base @@ -43,7 +43,7 @@ jobs: run: curl -s -L https://cpanmin.us | perl - App::cpanminus > /bin/cpanm && chmod +x /bin/cpanm - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: | ~/.cpan From 71c06aa81915851bdf5681175a12ef78d6da4b23 Mon Sep 17 00:00:00 2001 From: mtrop-godaddy Date: Wed, 17 Jun 2026 10:17:26 +0100 Subject: [PATCH 4/4] Remove openresty 1.19.9.1 as a test target (missing symbols break Node.js 24) --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 14cd81b..1929675 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,6 @@ jobs: strategy: matrix: openresty_version: - - 1.19.9.1 - 1.21.4.4 - 1.25.3.2 - 1.27.1.2