Skip to content

Commit 9eff4ce

Browse files
committed
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.
1 parent 9cc19b3 commit 9eff4ce

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

lib/resty/http.lua

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ 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+
preread = true,
75+
header_filter = true,
76+
body_filter = true,
77+
log = true,
78+
balancer = true,
79+
}
80+
81+
6582
-- Reimplemented coroutine.wrap, returning "nil, err" if the coroutine cannot
6683
-- be resumed. This protects user code from infinite loops when doing things like
6784
-- repeat
@@ -739,8 +756,11 @@ function _M.send_request(self, params)
739756
if params.version == 1.0 and not headers["Connection"] then
740757
headers["Connection"] = "Keep-Alive"
741758
end
742-
-- W3C trace context support with NGINX tracer
743-
if not headers["traceparent"] and ngx.var.http_traceparent then
759+
-- W3C trace context support with NGINX tracer.
760+
-- Only read ngx.var in request-bearing phases; doing so during
761+
-- init/init_worker/timer (config preload, background jobs) raises an error.
762+
if NGX_VAR_PHASES[ngx.get_phase()]
763+
and not headers["traceparent"] and ngx.var.http_traceparent then
744764
headers["traceparent"] = ngx.var.http_traceparent
745765
end
746766

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

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

0 commit comments

Comments
 (0)