From 96082ffc520beec14de0a0a1a312b2c0544cd34f Mon Sep 17 00:00:00 2001 From: Sebastian Parschauer Date: Wed, 6 May 2026 14:39:47 +0200 Subject: [PATCH 1/2] http: Extend `send_request()` with OTel support NGINX can be used together with OpenTelemetry (OTel) support and the `nginx-otel` module provides it. In the Observability space it is required that all HTTP request tracers in the line do trace propagation or trace forwarding at least. This is usually done by sending out an updated `traceparent` header. It contains for example a trace ID and a span ID so that the Observability backend such as Jaeger can correlate all spans to the same trace. To be able to trace HTTP requests sent from regular NGINX code and also the ones sent by LUA HTTP client code with the same NGINX tracer, the LUA HTTP client code has to be extended to add the `traceparent` header from `ngx.var.http_traceparent`. The value of this `$http_traceparent` variable is set by the `nginx-otel` module. It updates the span ID inside the `traceparent` value if NGINX directive `otel_trace_context propagate;` is set. So extend the `send_request()` function for this, because it already adds other headers as well and is used when calling `request_uri()` as well. Check if the `traceparent` header is not set yet but `ngx.var.http_traceparent` is set and set it from there. Also add a code comment describing this as well. References: * https://github.com/nginxinc/nginx-otel * https://nginx.org/en/docs/ngx_otel_module.html * https://nginx.org/en/docs/http/ngx_http_core_module.html#var_http_ --- lib/resty/http.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/resty/http.lua b/lib/resty/http.lua index a85f85a..231bc08 100644 --- a/lib/resty/http.lua +++ b/lib/resty/http.lua @@ -738,6 +738,10 @@ function _M.send_request(self, params) if params.version == 1.0 and not headers["Connection"] then headers["Connection"] = "Keep-Alive" end + -- OpenTelemetry support with nginx-otel in propagation mode + if not headers["traceparent"] and ngx.var.http_traceparent then + headers["traceparent"] = ngx.var.http_traceparent + end params.headers = headers From 1d1225ad1e35965a257cc43aa0737e4f92e116a4 Mon Sep 17 00:00:00 2001 From: Sebastian Parschauer Date: Thu, 7 May 2026 14:20:54 +0200 Subject: [PATCH 2/2] t: Add 3 traceparent header tests The new traceparent header auto-injection from `ngx.var.http_traceparent` has to be tested. So add a new file `t/21-traceparent-header.t` containing 3 tests for that: * TEST 1: No traceparent header is set * TEST 2: The traceparent header is correctly added when ngx.var.http_traceparent is used * TEST 3: The traceparent header is not modified from ngx.var.http_traceparent if it is already set Use an NGINX directive `set $http_traceparent '00-000...-01';` in front of the LUA block to emulate `nginx-otel` behavior. The case that there is a `traceparent` header added with `request_uri()` and `ngx.var.http_traceparent` is not set, is already covered by tests 1 and 3. --- t/21-traceparent-header.t | 92 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 t/21-traceparent-header.t diff --git a/t/21-traceparent-header.t b/t/21-traceparent-header.t new file mode 100644 index 0000000..fcb42de --- /dev/null +++ b/t/21-traceparent-header.t @@ -0,0 +1,92 @@ +use Test::Nginx::Socket 'no_plan'; +use Cwd qw(cwd); + +my $pwd = cwd(); + +$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; +$ENV{TEST_NGINX_PWD} ||= $pwd; +$ENV{TEST_COVERAGE} ||= 0; + +our $HttpConfig = 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) + } +}; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ +=== TEST 1: No traceparent header is set +--- http_config eval: $::HttpConfig +--- config + location /lua { + content_by_lua ' + require("resty.http").debug(true) + local http = require "resty.http" + local httpc = http.new() + + local res, err = httpc:request_uri("http://www.google.com") + '; + } +--- request +GET /lua +--- no_error_log +[error] +traceparent: + + +=== TEST 2: The traceparent header is correctly added when ngx.var.http_traceparent is used +--- http_config eval: $::HttpConfig +--- config + location /lua { + # emulate nginx-otel behavior here + set $http_traceparent '00-000000000000000019f4e02c82857913-11488c6e00d1d248-01'; + content_by_lua ' + require("resty.http").debug(true) + local http = require "resty.http" + local httpc = http.new() + local res, err = httpc:request_uri("http://www.google.com") + '; + } +--- request +GET /lua +--- no_error_log +[error] +--- error_log +traceparent: 00-000000000000000019f4e02c82857913-11488c6e00d1d248-01 + + +=== TEST 3: The traceparent header is not modified from ngx.var.http_traceparent if it is already set +--- http_config eval: $::HttpConfig +--- config + location /lua { + # emulate nginx-otel behavior here + set $http_traceparent '00-000000000000000019f4e02c82857913-11488c6e00d1d248-01'; + content_by_lua ' + require("resty.http").debug(true) + local http = require "resty.http" + local httpc = http.new() + local req_headers = {} + req_headers["traceparent"] = "00-00000000000000006633c2d00527dd33-1af98f7e6ecd16ff-01" + local res, err = httpc:request_uri("http://www.google.com", {method = GET, headers = req_headers}) + '; + } +--- request +GET /lua +--- no_error_log +[error] +traceparent: 00-000000000000000019f4e02c82857913-11488c6e00d1d248-01 +--- error_log +traceparent: 00-00000000000000006633c2d00527dd33-1af98f7e6ecd16ff-01