summaryrefslogtreecommitdiffstats
path: root/modules/http/http_trace.lua
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:26:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:26:00 +0000
commit830407e88f9d40d954356c3754f2647f91d5c06a (patch)
treed6a0ece6feea91f3c656166dbaa884ef8a29740e /modules/http/http_trace.lua
parentInitial commit. (diff)
downloadknot-resolver-830407e88f9d40d954356c3754f2647f91d5c06a.tar.xz
knot-resolver-830407e88f9d40d954356c3754f2647f91d5c06a.zip
Adding upstream version 5.6.0.upstream/5.6.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/http/http_trace.lua')
-rw-r--r--modules/http/http_trace.lua77
1 files changed, 77 insertions, 0 deletions
diff --git a/modules/http/http_trace.lua b/modules/http/http_trace.lua
new file mode 100644
index 0000000..123cd52
--- /dev/null
+++ b/modules/http/http_trace.lua
@@ -0,0 +1,77 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+local ffi = require('ffi')
+local condition = require('cqueues.condition')
+
+-- Trace execution of DNS queries
+local function serve_trace(h, _)
+ local path = h:get(':path')
+ local qname, qtype_str = path:match('/trace/([^/]+)/?([^/]*)')
+ if not qname then
+ return 400, 'expected /trace/<query name>/<query type>'
+ end
+
+ -- Parse query type (or default to A)
+ if not qtype_str or #qtype_str == 0 then
+ qtype_str = 'A'
+ end
+
+ local qtype = kres.type[qtype_str]
+ if not qtype then
+ return 400, string.format('unexpected query type: %s', qtype_str)
+ end
+
+ -- Create logging handler callback
+ local buffer = {}
+ local buffer_log_cb = ffi.cast('trace_log_f', function (_, msg)
+ jit.off(true, true) -- JIT for (C -> lua)^2 nesting isn't allowed
+ table.insert(buffer, ffi.string(msg))
+ end)
+
+ -- Wait for the result of the query
+ -- Note: We can't do non-blocking write to stream directly from resolve callbacks
+ -- because they don't run inside cqueue.
+ local cond = condition.new()
+ local waiting, done = false, false
+ local finish_cb = ffi.cast('trace_callback_f', function (req)
+ jit.off(true, true) -- JIT for (C -> lua)^2 nesting isn't allowed
+ table.insert(buffer, req:selected_tostring())
+ if waiting then
+ cond:signal()
+ end
+ done = true
+ end)
+
+ -- Resolve query and buffer logs into table
+ resolve {
+ name = qname,
+ type = qtype,
+ options = {'TRACE'},
+ init = function (req)
+ req:trace_chain_callbacks(buffer_log_cb, finish_cb)
+ end
+ }
+
+ -- Wait for asynchronous query and free callbacks
+ if not done then
+ waiting = true
+ cond:wait()
+ end
+
+ buffer_log_cb:free()
+ finish_cb:free()
+
+ -- Build the result
+ local result = table.concat(buffer, '')
+ -- Return buffered data
+ if not done then
+ return 504, result
+ end
+ return result
+end
+
+-- Export endpoints
+return {
+ endpoints = {
+ ['/trace'] = {'text/plain', serve_trace},
+ }
+}