Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(active) support map headers #87

Merged
merged 2 commits into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 77 additions & 1 deletion lib/resty/healthcheck.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,46 @@ local ngx_worker_pid = ngx.worker.pid
local ssl = require("ngx.ssl")
local resty_timer = require "resty.timer"

local new_tab
local nkeys
local is_array

do
local pcall = pcall
local ok

ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function () return {} end
end

-- OpenResty branch of LuaJIT New API
ok, nkeys = pcall(require, "table.nkeys")
if not ok then
nkeys = function (tab)
local count = 0
for _, v in pairs(tab) do
if v ~= nil then
count = count + 1
end
end
return count
end
end

ok, is_array = pcall(require, "table.isarray")
if not ok then
is_array = function(t)
for k in pairs(t) do
if type(k) ~= "number" or math.floor(k) ~= k then
return false
end
end
return true
end
end
end

-- constants
local EVENT_SOURCE_PREFIX = "lua-resty-healthcheck"
local LOG_PREFIX = "[healthcheck] "
Expand Down Expand Up @@ -866,8 +906,42 @@ function checker:run_single_check(ip, port, hostname, hostheader)

end

local req_headers = self.checks.active.headers
local headers
flrgh marked this conversation as resolved.
Show resolved Hide resolved
if self.checks.active._headers_str then
headers = self.checks.active._headers_str
else
local headers_length = nkeys(req_headers)
if headers_length > 0 then
if is_array(req_headers) then
self:log(WARN, "array headers is deprecated")
headers = table.concat(req_headers, "\r\n")
else
headers = new_tab(0, headers_length)
local idx = 0
for key, values in pairs(req_headers) do
if type(values) == "table" then
for _, value in ipairs(values) do
idx = idx + 1
headers[idx] = key .. ": " .. tostring(value)
end
else
idx = idx + 1
headers[idx] = key .. ": " .. tostring(values)
end
end
headers = table.concat(headers, "\r\n")
end
if #headers > 0 then
headers = headers .. "\r\n"
end
end
self.checks.active._headers_str = headers or ""
end

local path = self.checks.active.http_path
local request = ("GET %s HTTP/1.0\r\nHost: %s\r\n\r\n"):format(path, hostheader or hostname)
local request = ("GET %s HTTP/1.0\r\n%sHost: %s\r\n\r\n"):format(path, headers, hostheader or hostname or ip)
self:log(DEBUG, "request head: ", request)

local bytes
bytes, err = sock:send(request)
Expand Down Expand Up @@ -1229,6 +1303,7 @@ local defaults = {
concurrency = 10,
http_path = "/",
https_verify_certificate = true,
headers = {""},
healthy = {
interval = 0, -- 0 = disabled by default
http_statuses = { 200, 302 },
Expand Down Expand Up @@ -1300,6 +1375,7 @@ end
-- * `checks.active.concurrency`: number of targets to check concurrently
-- * `checks.active.http_path`: path to use in `GET` HTTP request to run on active checks
-- * `checks.active.https_verify_certificate`: boolean indicating whether to verify the HTTPS certificate
-- * `checks.active.headers`: one or more lists of values indexed by header name
-- * `checks.active.healthy.interval`: interval between checks for healthy targets (in seconds)
-- * `checks.active.healthy.http_statuses`: which HTTP statuses to consider a success
-- * `checks.active.healthy.successes`: number of successes to consider a target healthy
Expand Down
264 changes: 264 additions & 0 deletions t/20-req-headers.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
use Test::Nginx::Socket::Lua 'no_plan';
use Cwd qw(cwd);

workers(1);

my $pwd = cwd();

our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
lua_shared_dict test_shm 8m;
lua_shared_dict my_worker_events 8m;
};

run_tests();

__DATA__

=== TEST 1: headers: {"User-Agent: curl/7.29.0"}
--- http_config eval
qq{
$::HttpConfig

server {
listen 2112;
location = /status {
return 200;
}
}
}
--- config
location = /t {
content_by_lua_block {
local we = require "resty.worker.events"
assert(we.configure{ shm = "my_worker_events", interval = 0.1 })
local healthcheck = require("resty.healthcheck")
local checker = healthcheck.new({
name = "testing",
shm_name = "test_shm",
checks = {
active = {
http_path = "/status",
healthy = {
interval = 0.1
},
headers = {"User-Agent: curl/7.29.0"}
}
}
})
ngx.sleep(0.2) -- wait twice the interval
local ok, err = checker:add_target("127.0.0.1", 2112, nil, true)
ngx.say(ok)
ngx.sleep(0.2) -- wait twice the interval
}
}
--- request
GET /t
--- response_body
true
--- error_log
checking healthy targets: nothing to do
checking healthy targets: #1
GET /status HTTP/1.0
User-Agent: curl/7.29.0
Host: 127.0.0.1



=== TEST 2: headers: {"User-Agent: curl"}
--- http_config eval
qq{
$::HttpConfig

server {
listen 2112;
location = /status {
return 200;
}
}
}
--- config
location = /t {
content_by_lua_block {
local we = require "resty.worker.events"
assert(we.configure{ shm = "my_worker_events", interval = 0.1 })
local healthcheck = require("resty.healthcheck")
local checker = healthcheck.new({
name = "testing",
shm_name = "test_shm",
checks = {
active = {
http_path = "/status",
healthy = {
interval = 0.1
},
headers = {"User-Agent: curl"}
}
}
})
ngx.sleep(0.2) -- wait twice the interval
local ok, err = checker:add_target("127.0.0.1", 2112, nil, true)
ngx.say(ok)
ngx.sleep(0.2) -- wait twice the interval
}
}
--- request
GET /t
--- response_body
true
--- error_log
checking healthy targets: nothing to do
checking healthy targets: #1
GET /status HTTP/1.0
User-Agent: curl
Host: 127.0.0.1


=== TEST 3: headers: { ["User-Agent"] = "curl" }
--- http_config eval
qq{
$::HttpConfig

server {
listen 2112;
location = /status {
return 200;
}
}
}
--- config
location = /t {
content_by_lua_block {
local we = require "resty.worker.events"
assert(we.configure{ shm = "my_worker_events", interval = 0.1 })
local healthcheck = require("resty.healthcheck")
local checker = healthcheck.new({
name = "testing",
shm_name = "test_shm",
checks = {
active = {
http_path = "/status",
healthy = {
interval = 0.1
},
headers = { ["User-Agent"] = "curl" }
}
}
})
ngx.sleep(0.2) -- wait twice the interval
local ok, err = checker:add_target("127.0.0.1", 2112, nil, true)
ngx.say(ok)
ngx.sleep(0.2) -- wait twice the interval
}
}
--- request
GET /t
--- response_body
true
--- error_log
checking healthy targets: nothing to do
checking healthy targets: #1
GET /status HTTP/1.0
User-Agent: curl
Host: 127.0.0.1



=== TEST 4: headers: { ["User-Agent"] = {"curl"} }
--- http_config eval
qq{
$::HttpConfig

server {
listen 2112;
location = /status {
return 200;
}
}
}
--- config
location = /t {
content_by_lua_block {
local we = require "resty.worker.events"
assert(we.configure{ shm = "my_worker_events", interval = 0.1 })
local healthcheck = require("resty.healthcheck")
local checker = healthcheck.new({
name = "testing",
shm_name = "test_shm",
checks = {
active = {
http_path = "/status",
healthy = {
interval = 0.1
},
headers = { ["User-Agent"] = {"curl"} }
}
}
})
ngx.sleep(0.2) -- wait twice the interval
local ok, err = checker:add_target("127.0.0.1", 2112, nil, true)
ngx.say(ok)
ngx.sleep(0.2) -- wait twice the interval
}
}
--- request
GET /t
--- response_body
true
--- error_log
checking healthy targets: nothing to do
checking healthy targets: #1
GET /status HTTP/1.0
User-Agent: curl
Host: 127.0.0.1



=== TEST 5: headers: { ["User-Agent"] = {"curl", "nginx"} }
--- http_config eval
qq{
$::HttpConfig

server {
listen 2112;
location = /status {
return 200;
}
}
}
--- config
location = /t {
content_by_lua_block {
local we = require "resty.worker.events"
assert(we.configure{ shm = "my_worker_events", interval = 0.1 })
local healthcheck = require("resty.healthcheck")
local checker = healthcheck.new({
name = "testing",
shm_name = "test_shm",
checks = {
active = {
http_path = "/status",
healthy = {
interval = 0.1
},
headers = { ["User-Agent"] = {"curl", "nginx"} }
}
}
})
ngx.sleep(0.2) -- wait twice the interval
local ok, err = checker:add_target("127.0.0.1", 2112, nil, true)
ngx.say(ok)
ngx.sleep(0.2) -- wait twice the interval
}
}
--- request
GET /t
--- response_body
true
--- error_log
checking healthy targets: nothing to do
checking healthy targets: #1
GET /status HTTP/1.0
User-Agent: curl
User-Agent: nginx
Host: 127.0.0.1