283 lines
7.0 KiB
Lua
283 lines
7.0 KiB
Lua
local https_server = {}
|
|
https_server.__index = https_server
|
|
|
|
|
|
local fmt = string.format
|
|
local mock_srv_tpl_file = require "spec.fixtures.mock_webserver_tpl"
|
|
local ngx = require "ngx"
|
|
local pl_dir = require "pl.dir"
|
|
local pl_file = require "pl.file"
|
|
local pl_template = require "pl.template"
|
|
local pl_path = require "pl.path"
|
|
local uuid = require "resty.jit-uuid"
|
|
local http_client = require "resty.http"
|
|
local cjson = require "cjson"
|
|
local shell = require "resty.shell"
|
|
|
|
|
|
local Template = require("pl.stringx").Template
|
|
|
|
|
|
-- we need this to get random UUIDs
|
|
math.randomseed(os.time())
|
|
|
|
|
|
local HTTPS_SERVER_START_MAX_RETRY = 10
|
|
|
|
local tmp_root = os.getenv("TMPDIR") or "/tmp"
|
|
local host_regex = [[([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\])(:?[0-9]+)*]]
|
|
|
|
|
|
|
|
local function create_temp_dir(copy_cert_and_key)
|
|
local tmp_name = fmt("nginx_%s", uuid())
|
|
local tmp_path = fmt("%s/%s", tmp_root, tmp_name)
|
|
local _, err = pl_path.mkdir(tmp_path)
|
|
if err then
|
|
return nil, err
|
|
end
|
|
|
|
local _, err = pl_path.mkdir(tmp_path .. "/logs")
|
|
if err then
|
|
return nil, err
|
|
end
|
|
|
|
if copy_cert_and_key then
|
|
local status = pl_dir.copyfile("./spec/fixtures/kong_spec.crt", tmp_path)
|
|
if not status then
|
|
return nil, "could not copy cert"
|
|
end
|
|
|
|
status = pl_dir.copyfile("./spec/fixtures/kong_spec.key", tmp_path)
|
|
if not status then
|
|
return nil, "could not copy private key"
|
|
end
|
|
end
|
|
|
|
return tmp_path
|
|
end
|
|
|
|
|
|
local function create_conf(params)
|
|
local tpl, err = pl_template.compile(mock_srv_tpl_file)
|
|
if err then
|
|
return nil, err
|
|
end
|
|
|
|
local compiled_tpl = Template(tpl:render(params, { ipairs = ipairs }))
|
|
local conf_filename = params.base_path .. "/nginx.conf"
|
|
local conf, err = io.open (conf_filename, "w")
|
|
if err then
|
|
return nil, err
|
|
end
|
|
|
|
conf:write(compiled_tpl:substitute(params))
|
|
conf:close()
|
|
|
|
return conf_filename
|
|
end
|
|
|
|
|
|
local function count_results(logs_dir)
|
|
local results = {
|
|
["ok"] = 0,
|
|
["fail"] = 0,
|
|
["total"] = 0,
|
|
["status_ok"] = 0,
|
|
["status_fail"] = 0,
|
|
["status_total"] = 0
|
|
}
|
|
local error_log_filename = logs_dir .. "/error.log"
|
|
|
|
for line in io.lines(error_log_filename) do
|
|
local m = ngx.re.match(line, [[^.*\[COUNT\] (.+) (\d\d\d)\,.*\, host: \"(.+)\"$]])
|
|
if m then
|
|
local location = m[1]
|
|
local status = m[2]
|
|
local host = m[3]
|
|
if host then
|
|
local host_no_port = ngx.re.match(m[3], host_regex)
|
|
if host_no_port then
|
|
host = host_no_port[2]
|
|
end
|
|
else
|
|
host = "nonamehost"
|
|
end
|
|
if results[host] == nil then
|
|
results[host] = {
|
|
["ok"] = 0,
|
|
["fail"] = 0,
|
|
["status_ok"] = 0,
|
|
["status_fail"] = 0,
|
|
}
|
|
end
|
|
|
|
if location == "slash" then
|
|
if status == "200" then
|
|
results.ok = results.ok + 1
|
|
results[host].ok = results[host].ok + 1
|
|
else
|
|
results.fail = results.fail + 1
|
|
results[host].fail = results[host].fail + 1
|
|
end
|
|
results.total = results.ok + results.fail
|
|
elseif location == "status" then
|
|
if status == "200" then
|
|
results.status_ok = results.status_ok + 1
|
|
results[host].status_ok = results[host].status_ok + 1
|
|
else
|
|
results.status_fail = results.status_fail + 1
|
|
results[host].status_fail = results[host].status_fail + 1
|
|
end
|
|
results.status_total = results.status_ok + results.status_fail
|
|
end
|
|
end
|
|
end
|
|
|
|
return results
|
|
end
|
|
|
|
|
|
function https_server.clear_access_log(self)
|
|
local client = assert(http_client.new())
|
|
|
|
local uri = string.format("%s://%s:%d/clear_log", self.protocol, self.host, self.http_port)
|
|
|
|
local res = assert(client:request_uri(uri, {
|
|
method = "GET"
|
|
}))
|
|
|
|
assert(res.body == "cleared\n")
|
|
end
|
|
|
|
|
|
function https_server.get_access_log(self)
|
|
local client = assert(http_client.new())
|
|
|
|
local uri = string.format("%s://%s:%d/log?do_not_log", self.protocol, self.host, self.http_port)
|
|
|
|
local res = assert(client:request_uri(uri, {
|
|
method = "GET"
|
|
}))
|
|
|
|
return assert(cjson.decode(res.body))
|
|
end
|
|
|
|
|
|
function https_server.start(self)
|
|
if not pl_path.exists(tmp_root) or not pl_path.isdir(tmp_root) then
|
|
error("could not get a temporary path", 2)
|
|
end
|
|
|
|
local err
|
|
self.base_path, err = create_temp_dir(self.protocol == "https")
|
|
if err then
|
|
error(fmt("could not create temp dir: %s", err), 2)
|
|
end
|
|
|
|
local conf_params = {
|
|
base_path = self.base_path,
|
|
delay = self.delay,
|
|
cert_path = "./",
|
|
check_hostname = self.check_hostname,
|
|
logs_dir = self.logs_dir,
|
|
host = self.host,
|
|
hosts = self.hosts,
|
|
http_port = self.http_port,
|
|
protocol = self.protocol,
|
|
worker_num = self.worker_num,
|
|
disable_ipv6 = self.disable_ipv6,
|
|
}
|
|
|
|
local file, err = create_conf(conf_params)
|
|
if err then
|
|
error(fmt("could not create conf: %s", err), 2)
|
|
end
|
|
|
|
for _ = 1, HTTPS_SERVER_START_MAX_RETRY do
|
|
if shell.run("nginx -c " .. file .. " -p " .. self.base_path, nil, 0) then
|
|
return
|
|
end
|
|
|
|
ngx.sleep(1)
|
|
end
|
|
|
|
error("failed starting nginx")
|
|
end
|
|
|
|
|
|
function https_server.shutdown(self)
|
|
local pid_filename = self.base_path .. "/logs/nginx.pid"
|
|
local pid_file = io.open (pid_filename, "r")
|
|
if pid_file then
|
|
local pid, err = pid_file:read()
|
|
if err then
|
|
error(fmt("could not read pid file: %s", tostring(err)), 2)
|
|
end
|
|
|
|
local kill_nginx_cmd = fmt("kill -s TERM %s", tostring(pid))
|
|
local status = shell.run(kill_nginx_cmd, nil, 0)
|
|
if not status then
|
|
error(fmt("could not kill nginx test server. %s was not removed", self.base_path), 2)
|
|
end
|
|
|
|
local pidfile_removed
|
|
local watchdog = 0
|
|
repeat
|
|
pidfile_removed = pl_file.access_time(pid_filename) == nil
|
|
if not pidfile_removed then
|
|
ngx.sleep(0.01)
|
|
watchdog = watchdog + 1
|
|
if(watchdog > 100) then
|
|
error("could not stop nginx", 2)
|
|
end
|
|
end
|
|
until(pidfile_removed)
|
|
end
|
|
|
|
local count, err = count_results(self.base_path .. "/" .. self.logs_dir)
|
|
if err then
|
|
-- not a fatal error
|
|
print(fmt("could not count results: %s", tostring(err)))
|
|
end
|
|
|
|
local _, err = pl_dir.rmtree(self.base_path)
|
|
if err then
|
|
print(fmt("could not remove %s: %s", self.base_path, tostring(err)))
|
|
end
|
|
|
|
return count
|
|
end
|
|
|
|
-- **DEPRECATED**: please use `spec.helpers.http_mock` instead.
|
|
function https_server.new(port, hostname, protocol, check_hostname, workers, delay, disable_ipv6)
|
|
local self = setmetatable({}, https_server)
|
|
local host
|
|
local hosts
|
|
|
|
if type(hostname) == "table" then
|
|
hosts = hostname
|
|
host = ""
|
|
for _, h in ipairs(hostname) do
|
|
host = fmt("%s %s", host, h)
|
|
end
|
|
else
|
|
hosts = {hostname}
|
|
host = hostname
|
|
end
|
|
|
|
self.check_hostname = check_hostname or false
|
|
self.delay = tonumber(delay) or 0
|
|
self.host = host or "localhost"
|
|
self.hosts = hosts
|
|
self.http_port = port
|
|
self.logs_dir = "logs"
|
|
self.protocol = protocol or "http"
|
|
self.worker_num = workers or 2
|
|
self.disable_ipv6 = disable_ipv6
|
|
|
|
return self
|
|
end
|
|
|
|
return https_server
|