547 lines
18 KiB
Lua
547 lines
18 KiB
Lua
local constants = require "kong.constants"
|
|
local helpers = require "spec.helpers"
|
|
local websocket_client = require "resty.websocket.client"
|
|
local cjson = require "cjson"
|
|
|
|
|
|
local MESSAGE = "echo, ping, pong. echo, ping, pong. echo, ping, pong.\n"
|
|
|
|
|
|
local function websocket_send_text_and_get_echo(uri)
|
|
local payload = { message = "hello websocket" }
|
|
local wc = assert(websocket_client:new())
|
|
assert(wc:connect(uri))
|
|
|
|
assert(wc:send_text(cjson.encode(payload)))
|
|
local frame, typ, err = wc:recv_frame()
|
|
assert.is_nil(wc.fatal)
|
|
assert(frame, err)
|
|
assert.equal("text", typ)
|
|
assert.same(payload, cjson.decode(frame))
|
|
|
|
assert(wc:send_close())
|
|
end
|
|
|
|
|
|
for _, strategy in helpers.each_strategy() do
|
|
|
|
-- Might need to be marked as flaky because it may require an arbitrary high port?
|
|
describe("anonymous reports in Admin API #" .. strategy, function()
|
|
local dns_hostsfile
|
|
local reports_server
|
|
|
|
local reports_send_ping = function(opts)
|
|
ngx.sleep(0.01) -- hand over the CPU so other threads can do work (processing the sent data)
|
|
local admin_client = helpers.admin_client()
|
|
local opts = opts or {}
|
|
local port = opts.port or nil
|
|
|
|
local res = admin_client:post("/reports/send-ping" .. (port and "?port=" .. port or ""))
|
|
assert.response(res).has_status(200)
|
|
admin_client:close()
|
|
end
|
|
|
|
local reports_send_stream_ping = function()
|
|
ngx.sleep(0.01) -- hand over the CPU so other threads can do work (processing the sent data)
|
|
|
|
local tcp = ngx.socket.tcp()
|
|
assert(tcp:connect(helpers.get_proxy_ip(false), 19001))
|
|
|
|
local body = assert(tcp:receive("*a"))
|
|
assert.equal("ok", body)
|
|
|
|
tcp:close()
|
|
end
|
|
|
|
lazy_setup(function()
|
|
dns_hostsfile = assert(os.tmpname() .. ".hosts")
|
|
local fd = assert(io.open(dns_hostsfile, "wb"))
|
|
assert(fd:write("127.0.0.1 " .. constants.REPORTS.ADDRESS))
|
|
assert(fd:close())
|
|
|
|
local bp = assert(helpers.get_db_utils(strategy, {
|
|
"services",
|
|
"routes",
|
|
"plugins",
|
|
"certificates",
|
|
"snis",
|
|
}, { "reports-api" }))
|
|
|
|
local http_srv = assert(bp.services:insert {
|
|
name = "mock-service",
|
|
host = helpers.mock_upstream_host,
|
|
port = helpers.mock_upstream_port,
|
|
})
|
|
|
|
bp.routes:insert({ service = http_srv,
|
|
protocols = { "http" },
|
|
hosts = { "http-service.test" } })
|
|
|
|
bp.routes:insert({ service = http_srv,
|
|
protocols = { "https" },
|
|
hosts = { "https-service.test" } })
|
|
|
|
local grpc_srv = bp.services:insert({
|
|
name = "grpc",
|
|
url = helpers.grpcbin_url,
|
|
})
|
|
|
|
bp.routes:insert({
|
|
service = grpc_srv,
|
|
protocols = { "grpc" },
|
|
hosts = { "grpc" },
|
|
})
|
|
|
|
local grpcs_srv = bp.services:insert({
|
|
name = "grpcs",
|
|
url = helpers.grpcbin_ssl_url,
|
|
})
|
|
|
|
bp.routes:insert({
|
|
service = grpcs_srv,
|
|
protocols = { "grpcs" },
|
|
hosts = { "grpcs" },
|
|
})
|
|
|
|
local ws_srv = bp.services:insert({
|
|
name = "ws",
|
|
path = "/ws",
|
|
})
|
|
|
|
bp.routes:insert({
|
|
service = ws_srv,
|
|
protocols = { "http" },
|
|
paths = { "/up-ws" },
|
|
strip_path = true,
|
|
})
|
|
|
|
local tcp_srv = bp.services:insert({
|
|
name = "tcp",
|
|
host = helpers.mock_upstream_host,
|
|
port = helpers.mock_upstream_stream_port,
|
|
protocol = "tcp"
|
|
})
|
|
|
|
bp.routes:insert {
|
|
destinations = {
|
|
{ port = 19000, },
|
|
},
|
|
protocols = {
|
|
"tcp",
|
|
},
|
|
service = tcp_srv,
|
|
}
|
|
|
|
local tls_srv = bp.services:insert({
|
|
name = "tls",
|
|
host = helpers.mock_upstream_host,
|
|
port = helpers.mock_upstream_stream_ssl_port,
|
|
protocol = "tls"
|
|
})
|
|
|
|
bp.routes:insert {
|
|
destinations = {
|
|
{ port = 19443, },
|
|
},
|
|
protocols = {
|
|
"tls",
|
|
},
|
|
service = tls_srv,
|
|
}
|
|
|
|
local reports_srv = bp.services:insert({
|
|
name = "reports-srv",
|
|
host = helpers.mock_upstream_host,
|
|
port = helpers.mock_upstream_stream_port,
|
|
protocol = "tcp"
|
|
})
|
|
|
|
bp.routes:insert {
|
|
destinations = {
|
|
{ port = 19001, },
|
|
},
|
|
protocols = {
|
|
"tcp",
|
|
},
|
|
service = reports_srv,
|
|
}
|
|
|
|
bp.plugins:insert({
|
|
name = "reports-api",
|
|
service = { id = reports_srv.id },
|
|
protocols = { "tcp" },
|
|
config = {}
|
|
})
|
|
|
|
assert(helpers.start_kong({
|
|
nginx_conf = "spec/fixtures/custom_nginx.template",
|
|
database = strategy,
|
|
dns_hostsfile = dns_hostsfile,
|
|
resolver_hosts_file = dns_hostsfile,
|
|
anonymous_reports = true,
|
|
plugins = "reports-api",
|
|
stream_listen = helpers.get_proxy_ip(false) .. ":19000," ..
|
|
helpers.get_proxy_ip(false) .. ":19001," ..
|
|
helpers.get_proxy_ip(true) .. ":19443 ssl",
|
|
|
|
}))
|
|
end)
|
|
|
|
lazy_teardown(function()
|
|
helpers.stop_kong()
|
|
os.remove(dns_hostsfile)
|
|
end)
|
|
|
|
|
|
before_each(function()
|
|
reports_server = helpers.tcp_server(constants.REPORTS.STATS_TLS_PORT, {tls=true})
|
|
end)
|
|
|
|
it("reports http requests", function()
|
|
local proxy_client = assert(helpers.proxy_client())
|
|
local res = proxy_client:get("/", {
|
|
headers = { host = "http-service.test" }
|
|
})
|
|
assert.response(res).has_status(200)
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=1", reports_data)
|
|
assert.match("https_reqs=0", reports_data)
|
|
assert.match("h2c_reqs=0", reports_data)
|
|
assert.match("h2_reqs=0", reports_data)
|
|
assert.match("grpc_reqs=0", reports_data)
|
|
assert.match("grpcs_reqs=0", reports_data)
|
|
assert.match("ws_reqs=0", reports_data)
|
|
assert.match("wss_reqs=0", reports_data)
|
|
|
|
proxy_client:close()
|
|
end)
|
|
|
|
it("reports https requests", function()
|
|
local proxy_ssl_client = assert(helpers.proxy_ssl_client())
|
|
local res = proxy_ssl_client:get("/", {
|
|
headers = { host = "https-service.test" }
|
|
})
|
|
assert.response(res).has_status(200)
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=0", reports_data)
|
|
assert.match("https_reqs=1", reports_data)
|
|
assert.match("h2c_reqs=0", reports_data)
|
|
assert.match("h2_reqs=0", reports_data)
|
|
assert.match("grpc_reqs=0", reports_data)
|
|
assert.match("grpcs_reqs=0", reports_data)
|
|
assert.match("ws_reqs=0", reports_data)
|
|
assert.match("wss_reqs=0", reports_data)
|
|
|
|
proxy_ssl_client:close()
|
|
end)
|
|
|
|
it("when send http request to https port, no other error in error.log", function()
|
|
local https_port = assert(helpers.get_proxy_port(true))
|
|
local proxy_client = assert(helpers.proxy_client(nil, https_port))
|
|
local res = proxy_client:get("/", {
|
|
headers = { host = "http-service.test" }
|
|
})
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
assert.response(res).has_status(400)
|
|
assert.logfile().has.no.line("using uninitialized")
|
|
assert.logfile().has.no.line("could not determine log suffix (scheme=http, proxy_mode=)")
|
|
proxy_client:close()
|
|
end)
|
|
|
|
it("reports h2c requests", function()
|
|
local h2c_client = assert(helpers.proxy_client_h2c())
|
|
local body, headers = h2c_client({
|
|
headers = { [":authority"] = "http-service.test" }
|
|
})
|
|
|
|
assert.equal(200, tonumber(headers:get(":status")))
|
|
assert.is_not_nil(body)
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=0", reports_data)
|
|
assert.match("https_reqs=0", reports_data)
|
|
assert.match("h2c_reqs=1", reports_data)
|
|
assert.match("h2_reqs=0", reports_data)
|
|
assert.match("grpc_reqs=0", reports_data)
|
|
assert.match("grpcs_reqs=0", reports_data)
|
|
assert.match("ws_reqs=0", reports_data)
|
|
assert.match("wss_reqs=0", reports_data)
|
|
end)
|
|
|
|
it("reports h2 requests", function()
|
|
local h2_client = assert(helpers.proxy_client_h2())
|
|
local body, headers = h2_client({
|
|
headers = { [":authority"] = "https-service.test" }
|
|
})
|
|
|
|
assert.equal(200, tonumber(headers:get(":status")))
|
|
assert.is_not_nil(body)
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=0", reports_data)
|
|
assert.match("https_reqs=0", reports_data)
|
|
assert.match("h2c_reqs=0", reports_data)
|
|
assert.match("h2_reqs=1", reports_data)
|
|
assert.match("grpc_reqs=0", reports_data)
|
|
assert.match("grpcs_reqs=0", reports_data)
|
|
assert.match("ws_reqs=0", reports_data)
|
|
assert.match("wss_reqs=0", reports_data)
|
|
end)
|
|
|
|
|
|
it("reports grpc requests", function()
|
|
local grpc_client = helpers.proxy_client_grpc()
|
|
assert(grpc_client({
|
|
service = "hello.HelloService.SayHello",
|
|
opts = {
|
|
["-authority"] = "grpc",
|
|
},
|
|
}))
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=0", reports_data)
|
|
assert.match("https_reqs=0", reports_data)
|
|
assert.match("h2c_reqs=0", reports_data)
|
|
assert.match("h2_reqs=0", reports_data)
|
|
assert.match("grpc_reqs=1", reports_data)
|
|
assert.match("grpcs_reqs=0", reports_data)
|
|
assert.match("ws_reqs=0", reports_data)
|
|
assert.match("wss_reqs=0", reports_data)
|
|
end)
|
|
|
|
it("reports grpcs requests", function()
|
|
local grpcs_client = assert(helpers.proxy_client_grpcs())
|
|
grpcs_client({
|
|
service = "hello.HelloService.SayHello",
|
|
opts = {
|
|
["-authority"] = "grpcs",
|
|
},
|
|
})
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=0", reports_data)
|
|
assert.match("https_reqs=0", reports_data)
|
|
assert.match("h2c_reqs=0", reports_data)
|
|
assert.match("h2_reqs=0", reports_data)
|
|
assert.match("grpc_reqs=0", reports_data)
|
|
assert.match("grpcs_reqs=1", reports_data)
|
|
assert.match("ws_reqs=0", reports_data)
|
|
assert.match("wss_reqs=0", reports_data)
|
|
end)
|
|
|
|
it("reports ws requests", function()
|
|
websocket_send_text_and_get_echo("ws://" .. helpers.get_proxy_ip(false) ..
|
|
":" .. helpers.get_proxy_port(false) .. "/up-ws")
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=0", reports_data)
|
|
assert.match("https_reqs=0", reports_data)
|
|
assert.match("h2c_reqs=0", reports_data)
|
|
assert.match("h2_reqs=0", reports_data)
|
|
assert.match("grpc_reqs=0", reports_data)
|
|
assert.match("grpcs_reqs=0", reports_data)
|
|
assert.match("ws_reqs=1", reports_data)
|
|
assert.match("wss_reqs=0", reports_data)
|
|
end)
|
|
|
|
it("reports wss requests", function()
|
|
websocket_send_text_and_get_echo("wss://" .. helpers.get_proxy_ip(true) ..
|
|
":" .. helpers.get_proxy_port(true) .. "/up-ws")
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("requests=1", reports_data)
|
|
assert.match("http_reqs=0", reports_data)
|
|
assert.match("https_reqs=0", reports_data)
|
|
assert.match("h2c_reqs=0", reports_data)
|
|
assert.match("h2_reqs=0", reports_data)
|
|
assert.match("grpc_reqs=0", reports_data)
|
|
assert.match("grpcs_reqs=0", reports_data)
|
|
assert.match("ws_reqs=0", reports_data)
|
|
assert.match("wss_reqs=1", reports_data)
|
|
end)
|
|
|
|
pending("#stream reports tcp streams", function()
|
|
local tcp = ngx.socket.tcp()
|
|
assert(tcp:connect(helpers.get_proxy_ip(false), 19000))
|
|
|
|
assert(tcp:send(MESSAGE))
|
|
|
|
local body = assert(tcp:receive("*a"))
|
|
assert.equal(MESSAGE, body)
|
|
|
|
tcp:close()
|
|
|
|
reports_send_stream_ping()
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("streams=1", reports_data)
|
|
assert.match("tcp_streams=1", reports_data)
|
|
assert.match("tls_streams=0", reports_data)
|
|
end)
|
|
|
|
pending("#stream reports tls streams", function()
|
|
local tcp = ngx.socket.tcp()
|
|
|
|
assert(tcp:connect(helpers.get_proxy_ip(true), 19443))
|
|
|
|
assert(tcp:sslhandshake(nil, "this-is-needed.test", false))
|
|
|
|
assert(tcp:send(MESSAGE))
|
|
|
|
local body = assert(tcp:receive("*a"))
|
|
assert.equal(MESSAGE, body)
|
|
|
|
tcp:close()
|
|
|
|
reports_send_stream_ping()
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
assert.match("streams=2", reports_data)
|
|
assert.match("tcp_streams=1", reports_data) -- it counts the stream request for the ping
|
|
assert.match("tls_streams=1", reports_data)
|
|
end)
|
|
|
|
it("does not log NGINX-produced errors", function()
|
|
local proxy_client = assert(helpers.proxy_client())
|
|
local res = assert(proxy_client:send {
|
|
method = "GET",
|
|
path = "/",
|
|
headers = {
|
|
["X-Large"] = string.rep("a", 2^10 * 10), -- default large_client_header_buffers is 8k
|
|
}
|
|
})
|
|
|
|
-- send a ping so the tcp server shutdown cleanly and not with a timeout.
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
assert.res_status(400, res)
|
|
proxy_client:close()
|
|
|
|
assert.errlog()
|
|
.has.no.line([[could not determine log suffix]], true)
|
|
end)
|
|
|
|
it("reports route statistics", function()
|
|
local proxy_client = assert(helpers.proxy_client())
|
|
local res = proxy_client:get("/", {
|
|
headers = { host = "http-service.test" }
|
|
})
|
|
assert.response(res).has_status(200)
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
|
|
assert.match([["headers":0]], reports_data)
|
|
assert.match([["routes":5]], reports_data)
|
|
assert.match([["http":3]], reports_data)
|
|
assert.match([["grpc":2]], reports_data)
|
|
assert.match([["stream":0]], reports_data)
|
|
assert.match([["tls_passthrough":0]], reports_data)
|
|
assert.match([["flavor":"traditional_compatible"]], reports_data)
|
|
assert.match([["paths":1]], reports_data)
|
|
assert.match([["regex_routes":0]], reports_data)
|
|
assert.match([["v1":0]], reports_data)
|
|
assert.match([["v0":5]], reports_data)
|
|
proxy_client:close()
|
|
end)
|
|
|
|
if strategy ~= "off" then
|
|
it("reports route statistics after #change", function()
|
|
local admin = helpers.admin_client()
|
|
-- any other route will fail because we are ... routing all traffic to localhost
|
|
assert.res_status(201, admin:send({
|
|
method = "POST",
|
|
path = "/services",
|
|
body = {
|
|
name = "test",
|
|
url = "http://localhost:9001/services/",
|
|
path = "/",
|
|
},
|
|
headers = { ["Content-Type"] = "application/json" },
|
|
}))
|
|
assert.res_status(201, admin:send({
|
|
method = "POST",
|
|
path = "/services/test/routes",
|
|
body = {
|
|
protocols = { "http" },
|
|
headers = { ["x-test"] = { "test" } },
|
|
paths = { "~/test", "/normal" },
|
|
preserve_host = false,
|
|
path_handling = "v1",
|
|
},
|
|
headers = { ["Content-Type"] = "application/json" },
|
|
}))
|
|
|
|
local proxy_client = assert(helpers.proxy_client())
|
|
helpers.pwait_until(function()
|
|
local res = proxy_client:get("/test", {
|
|
headers = { ["x-test"] = "test", host = "http-service2.test" }
|
|
})
|
|
assert.response(res).has_status(200)
|
|
end, 1000)
|
|
|
|
for _ = 1, 2 do
|
|
local res = proxy_client:get("/test", {
|
|
headers = { ["x-test"] = "test", host = "http-service2.test" }
|
|
})
|
|
assert.response(res).has_status(200)
|
|
end
|
|
|
|
for _ = 1, 5 do
|
|
local res = proxy_client:get("/foo", {
|
|
headers = { ["x-test"] = "test", host = "http-service2.test" }
|
|
})
|
|
assert.response(res).has_status(404)
|
|
end
|
|
|
|
reports_send_ping({port=constants.REPORTS.STATS_TLS_PORT})
|
|
|
|
local _, reports_data = assert(reports_server:join())
|
|
|
|
assert.match([["headers":1]], reports_data)
|
|
assert.match([["routes":6]], reports_data)
|
|
assert.match([["http":4]], reports_data)
|
|
assert.match([["grpc":2]], reports_data)
|
|
assert.match([["stream":0]], reports_data)
|
|
assert.match([["tls_passthrough":0]], reports_data)
|
|
assert.match([["flavor":"traditional_compatible"]], reports_data)
|
|
assert.match([["paths":3]], reports_data)
|
|
assert.match([["regex_routes":1]], reports_data)
|
|
assert.match([["v1":1]], reports_data)
|
|
assert.match([["v0":5]], reports_data)
|
|
assert.match([[request_route_cache_hit_pos=2]], reports_data)
|
|
-- the wait_util may trigger cache misses, so we can't assert on accurate values
|
|
assert.match([[request_route_cache_hit_neg=]], reports_data)
|
|
proxy_client:close()
|
|
end)
|
|
end
|
|
end)
|
|
end
|