Skip to content

Commit 6a19cc1

Browse files
authored
Merge pull request #345 from mtrop-godaddy/fix/traceparent-otel-gating
Gate W3C trace support to request phases with ngx.var access
2 parents cd41479 + 4e9afaa commit 6a19cc1

3 files changed

Lines changed: 87 additions & 8 deletions

File tree

‎.github/workflows/test.yml‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ jobs:
66
luacheck:
77
runs-on: ubuntu-latest
88
steps:
9-
- uses: actions/checkout@v4
10-
- uses: leafo/gh-actions-lua@v8
9+
- uses: actions/checkout@v6
10+
- uses: leafo/gh-actions-lua@v13
1111
with:
1212
luaVersion: "luajit-openresty"
13-
- uses: leafo/gh-actions-luarocks@v5
13+
- uses: leafo/gh-actions-luarocks@v6
1414
with:
1515
luarocksVersion: "3.12.2"
1616
- run: luarocks install luacheck
@@ -20,7 +20,6 @@ jobs:
2020
strategy:
2121
matrix:
2222
openresty_version:
23-
- 1.19.9.1
2423
- 1.21.4.4
2524
- 1.25.3.2
2625
- 1.27.1.2
@@ -33,7 +32,7 @@ jobs:
3332
options: --init
3433

3534
steps:
36-
- uses: actions/checkout@v4
35+
- uses: actions/checkout@v6
3736
- name: Install deps
3837
run: |
3938
apk add --no-cache curl perl bash wget git perl-dev libarchive-tools nodejs build-base
@@ -43,7 +42,7 @@ jobs:
4342
run: curl -s -L https://cpanmin.us | perl - App::cpanminus > /bin/cpanm && chmod +x /bin/cpanm
4443

4544
- name: Cache
46-
uses: actions/cache@v3
45+
uses: actions/cache@v5
4746
with:
4847
path: |
4948
~/.cpan

‎lib/resty/http.lua‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ local EXPECTING_BODY = {
6262
}
6363

6464

65+
-- ngx.var is only readable in request-bearing phases. Reading it from a
66+
-- request-less phase (init, init_worker, timer, etc.) raises an error, so we
67+
-- gate any ngx.var access on being in one of these phases.
68+
local NGX_VAR_PHASES = {
69+
set = true,
70+
rewrite = true,
71+
server_rewrite = true,
72+
access = true,
73+
content = true,
74+
header_filter = true,
75+
body_filter = true,
76+
log = true,
77+
precontent = true,
78+
}
79+
80+
6581
-- Reimplemented coroutine.wrap, returning "nil, err" if the coroutine cannot
6682
-- be resumed. This protects user code from infinite loops when doing things like
6783
-- repeat
@@ -739,8 +755,11 @@ function _M.send_request(self, params)
739755
if params.version == 1.0 and not headers["Connection"] then
740756
headers["Connection"] = "Keep-Alive"
741757
end
742-
-- W3C trace context support with NGINX tracer
743-
if not headers["traceparent"] and ngx.var.http_traceparent then
758+
-- W3C trace context support with NGINX tracer.
759+
-- Only read ngx.var in request-bearing phases; doing so during
760+
-- init/init_worker/timer (config preload, background jobs) raises an error.
761+
if NGX_VAR_PHASES[ngx.get_phase()]
762+
and not headers["traceparent"] and ngx.var.http_traceparent then
744763
headers["traceparent"] = ngx.var.http_traceparent
745764
end
746765

‎t/21-traceparent-header.t‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,46 @@ our $HttpConfig = qq{
2222
}
2323
};
2424

25+
# Same as $HttpConfig, but fires a request from a request-less phase
26+
# (an init_worker timer) where ngx.var is disabled.
27+
our $HttpConfigInitWorker = qq{
28+
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
29+
error_log logs/error.log debug;
30+
resolver 8.8.8.8 ipv6=off;
31+
32+
init_by_lua_block {
33+
if $ENV{TEST_COVERAGE} == 1 then
34+
jit.off()
35+
require("luacov.runner").init()
36+
end
37+
38+
require("resty.http").debug(true)
39+
}
40+
41+
init_worker_by_lua_block {
42+
local function make_request(premature)
43+
if premature then
44+
return
45+
end
46+
47+
local http = require "resty.http"
48+
local httpc = http.new()
49+
local res, err = httpc:request_uri("http://www.google.com")
50+
if err then
51+
ngx.log(ngx.ERR, "init_worker request failed: ", err)
52+
end
53+
ngx.log(ngx.INFO, "init_worker request completed")
54+
end
55+
56+
-- cosockets are unavailable directly in init_worker, so defer to a
57+
-- timer. The timer phase is also request-less (ngx.var disabled).
58+
local ok, err = ngx.timer.at(0, make_request)
59+
if not ok then
60+
ngx.log(ngx.ERR, "failed to create timer: ", err)
61+
end
62+
}
63+
};
64+
2565
no_long_string();
2666
#no_diff();
2767

@@ -93,3 +133,24 @@ GET /lua
93133
traceparent: 00-000000000000000019f4e02c82857913-11488c6e00d1d248-01
94134
--- error_log
95135
traceparent: 00-00000000000000006633c2d00527dd33-1af98f7e6ecd16ff-01
136+
137+
138+
=== TEST 4: A request from a request-less phase does not error reading ngx.var
139+
--- http_config eval: $::HttpConfigInitWorker
140+
--- config
141+
location /lua {
142+
content_by_lua_block {
143+
-- give the init_worker timer time to fire and finish
144+
ngx.sleep(0.5)
145+
ngx.say("ok")
146+
}
147+
}
148+
--- request
149+
GET /lua
150+
--- response_body
151+
ok
152+
--- no_error_log
153+
[error]
154+
API disabled in the context of ngx.timer
155+
--- error_log
156+
init_worker request completed

0 commit comments

Comments
 (0)