diff --git a/assets/kong_EE_versions.ver b/assets/kong_EE_versions.ver
index 5210da08..8e9d3e38 100644
--- a/assets/kong_EE_versions.ver
+++ b/assets/kong_EE_versions.ver
@@ -60,3 +60,4 @@
3.6.1.6
3.7.0.0
3.7.1.1
+test9.9.9.3
diff --git a/kong-versions/test9.9.9.3/kong/Makefile b/kong-versions/test9.9.9.3/kong/Makefile
new file mode 100644
index 00000000..dc632763
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/Makefile
@@ -0,0 +1,242 @@
+OS := $(shell uname | awk '{print tolower($$0)}')
+MACHINE := $(shell uname -m)
+
+DEV_ROCKS = "busted 2.2.0" "busted-hjtest 0.0.5" "luacheck 1.2.0" "lua-llthreads2 0.1.6" "ldoc 1.5.0" "luacov 0.15.0"
+WIN_SCRIPTS = "bin/busted" "bin/kong" "bin/kong-health"
+BUSTED_ARGS ?= -v
+TEST_CMD ?= bin/busted $(BUSTED_ARGS)
+
+BUILD_NAME ?= kong-dev
+BAZEL_ARGS ?= --verbose_failures --action_env=BUILD_NAME=$(BUILD_NAME) --//:skip_webui=true --//:skip_tools=true
+
+ifeq ($(OS), darwin)
+HOMEBREW_DIR ?= /opt/homebrew
+OPENSSL_DIR ?= $(shell brew --prefix)/opt/openssl
+EXPAT_DIR ?= $(HOMEBREW_DIR)/opt/expat
+LIBXML2_DIR ?= $(HOMEBREW_DIR)/opt/libxml2
+GRPCURL_OS ?= osx
+YAML_DIR ?= $(shell brew --prefix)/opt/libyaml
+else
+LIBRARY_PREFIX ?= /usr
+OPENSSL_DIR ?= $(LIBRARY_PREFIX)
+EXPAT_DIR ?= $(LIBRARY_PREFIX)
+LIBXML2_DIR ?= $(LIBRARY_PREFIX)
+GRPCURL_OS ?= $(OS)
+YAML_DIR ?= /usr
+endif
+
+ifeq ($(MACHINE), aarch64)
+GRPCURL_MACHINE ?= arm64
+H2CLIENT_MACHINE ?= arm64
+else
+GRPCURL_MACHINE ?= $(MACHINE)
+H2CLIENT_MACHINE ?= $(MACHINE)
+endif
+
+ifeq ($(MACHINE), aarch64)
+BAZELISK_MACHINE ?= arm64
+else ifeq ($(MACHINE), x86_64)
+BAZELISK_MACHINE ?= amd64
+else
+BAZELISK_MACHINE ?= $(MACHINE)
+endif
+
+.PHONY: install dev \
+ sca test test-integration test-plugins test-all \
+ pdk-phase-check functional-tests \
+ fix-windows release wasm-test-filters
+
+ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+KONG_SOURCE_LOCATION ?= $(ROOT_DIR)
+GRPCURL_VERSION ?= 1.8.5
+BAZLISK_VERSION ?= 1.20.0
+H2CLIENT_VERSION ?= 0.4.4
+BAZEL := $(shell command -v bazel 2> /dev/null)
+VENV = /dev/null # backward compatibility when no venv is built
+
+# Use x86_64 grpcurl v1.8.5 for Apple silicon chips
+ifeq ($(GRPCURL_OS)_$(MACHINE)_$(GRPCURL_VERSION), osx_arm64_1.8.5)
+GRPCURL_MACHINE = x86_64
+endif
+
+PACKAGE_TYPE ?= deb
+
+bin/bazel:
+ @curl -s -S -L \
+ https://github.com/bazelbuild/bazelisk/releases/download/v$(BAZLISK_VERSION)/bazelisk-$(OS)-$(BAZELISK_MACHINE) -o bin/bazel
+ @chmod +x bin/bazel
+
+bin/grpcurl:
+ @curl -s -S -L \
+ https://github.com/fullstorydev/grpcurl/releases/download/v$(GRPCURL_VERSION)/grpcurl_$(GRPCURL_VERSION)_$(GRPCURL_OS)_$(GRPCURL_MACHINE).tar.gz | tar xz -C bin;
+ @$(RM) bin/LICENSE
+
+bin/h2client:
+ @curl -s -S -L \
+ https://github.com/Kong/h2client/releases/download/v$(H2CLIENT_VERSION)/h2client_$(H2CLIENT_VERSION)_$(OS)_$(H2CLIENT_MACHINE).tar.gz | tar xz -C bin;
+ @$(RM) bin/README.md
+
+
+check-bazel: bin/bazel
+ifndef BAZEL
+ $(eval BAZEL := bin/bazel)
+endif
+
+wasm-test-filters:
+ ./scripts/build-wasm-test-filters.sh
+
+build-kong: check-bazel
+ $(BAZEL) build //build:kong $(BAZEL_ARGS)
+
+build-venv: check-bazel
+ $(eval VENV := bazel-bin/build/$(BUILD_NAME)-venv.sh)
+
+ @if [ ! -e bazel-bin/build/$(BUILD_NAME)-venv.sh ]; then \
+ $(BAZEL) build //build:venv $(BAZEL_ARGS); \
+ fi
+
+build-openresty: check-bazel
+
+ @if [ ! -e bazel-bin/build/$(BUILD_NAME)/openresty ]; then \
+ $(BAZEL) build //build:install-openresty --verbose_failures --action_env=BUILD_NAME=$(BUILD_NAME); \
+ else \
+ $(BAZEL) build //build:dev-make-openresty --verbose_failures --action_env=BUILD_NAME=$(BUILD_NAME); \
+ fi
+
+install-dev-rocks: build-venv
+ @. $(VENV) ;\
+ for rock in $(DEV_ROCKS) ; do \
+ if luarocks list --porcelain $$rock | grep -q "installed" ; then \
+ echo $$rock already installed, skipping ; \
+ else \
+ echo $$rock not found, installing via luarocks... ; \
+ LIBRARY_PREFIX=$$(pwd)/bazel-bin/build/$(BUILD_NAME)/kong ; \
+ luarocks install $$rock OPENSSL_DIR=$(OPENSSL_DIR) YAML_DIR=$(YAML_DIR) || exit 1; \
+ fi \
+ done;
+
+dev: build-venv install-dev-rocks bin/grpcurl bin/h2client wasm-test-filters
+
+build-release: check-bazel
+ $(BAZEL) clean --expunge
+ $(BAZEL) build //build:kong --verbose_failures --config release
+
+package/deb: check-bazel build-release
+ $(BAZEL) build --config release :kong_deb
+
+package/apk: check-bazel build-release
+ $(BAZEL) build --config release :kong_apk
+
+package/rpm: check-bazel build-release
+ $(BAZEL) build --config release :kong_el8 --action_env=RPM_SIGNING_KEY_FILE --action_env=NFPM_RPM_PASSPHRASE
+ $(BAZEL) build --config release :kong_el7 --action_env=RPM_SIGNING_KEY_FILE --action_env=NFPM_RPM_PASSPHRASE
+ $(BAZEL) build --config release :kong_aws2 --action_env=RPM_SIGNING_KEY_FILE --action_env=NFPM_RPM_PASSPHRASE
+ $(BAZEL) build --config release :kong_aws2022 --action_env=RPM_SIGNING_KEY_FILE --action_env=NFPM_RPM_PASSPHRASE
+
+functional-tests: dev test
+
+install: dev
+ @$(VENV) luarocks make
+
+clean: check-bazel
+ $(BAZEL) clean
+ $(RM) bin/bazel bin/grpcurl bin/h2client
+
+expunge: check-bazel
+ $(BAZEL) clean --expunge
+ $(RM) bin/bazel bin/grpcurl bin/h2client
+
+sca:
+ @!(grep -R -E -I -n -w '#only|#o' spec && echo "#only or #o tag detected") >&2
+ @!(grep -R -E -I -n -w '#only|#o' spec-ee && echo "#only or #o tag detected") >&2
+ @!(grep -R -E -I -n -- '---\s+ONLY' t && echo "--- ONLY block detected") >&2
+ @$(KONG_SOURCE_LOCATION)/scripts/copyright-header-checker
+
+update-copyright: build-venv
+ bash -c 'OPENSSL_DIR=$(OPENSSL_DIR) EXPAT_DIR=$(EXPAT_DIR) $(VENV) luajit $(KONG_SOURCE_LOCATION)/scripts/update-copyright'
+
+trigger-api-tests:
+ -docker manifest inspect kong/kong-gateway-internal:${DOCKER_IMAGE_TAG} 2>&1 >/dev/null && \
+ curl \
+ -X POST \
+ -H "Accept: application/vnd.github+json" \
+ -H "Authorization: Bearer ${GITHUB_TOKEN}" \
+ https://api.github.com/repos/kong/kong-api-tests/dispatches \
+ -d '{"event_type":"per-commit-test","client_payload":{"docker_image":"kong/kong-gateway-internal:${DOCKER_IMAGE_TAG}"}' \
+
+test: dev
+ @$(VENV) $(TEST_CMD) spec/01-unit
+
+test-integration: dev
+ @$(VENV) $(TEST_CMD) spec/02-integration
+
+test-plugins-spec: dev
+ @$(VENV) $(TEST_CMD) spec/03-plugins
+
+test-all: dev
+ @$(VENV) $(TEST_CMD) spec/
+
+test-ee: dev
+ @$(VENV) $(TEST_CMD) spec-ee/01-unit
+
+test-integration-ee: dev
+ @$(VENV) $(TEST_CMD) spec-ee/02-integration
+
+test-plugins-spec-ee: dev
+ @$(VENV) $(TEST_CMD) spec-ee/03-plugins
+
+test-all-ee: dev
+ @$(VENV) $(TEST_CMD) spec-ee/
+
+test-custom: dev
+ifndef test_spec
+ $(error test_spec variable needs to be set, i.e. make test-custom test_spec=foo/bar/baz_spec.lua)
+endif
+ @$(VENV) $(TEST_CMD) $(test_spec)
+
+pdk-phase-checks: dev
+ rm -f t/phase_checks.stats
+ rm -f t/phase_checks.report
+ PDK_PHASE_CHECKS_LUACOV=1 prove -I. t/01*/*/00-phase*.t
+ luacov -c t/phase_checks.luacov
+ grep "ngx\\." t/phase_checks.report
+ grep "check_" t/phase_checks.report
+
+test-plugins-ee:
+ifndef EE_PLUGIN
+ $(error "Please use make plugins-ee EE_PLUGIN=plugin-name")
+endif
+ scripts/enterprise_plugin.sh build-deps
+ scripts/enterprise_plugin.sh test $(EE_PLUGIN)
+
+fix-windows:
+ @for script in $(WIN_SCRIPTS) ; do \
+ echo Converting Windows file $$script ; \
+ mv $$script $$script.win ; \
+ tr -d '\015' <$$script.win >$$script ; \
+ rm $$script.win ; \
+ chmod 0755 $$script ; \
+ done;
+
+# the following targets are kept for backwards compatibility
+# dev is renamed to dev-legacy
+remove:
+ $(warning 'remove' target is deprecated, please use `make dev` instead)
+ -@luarocks remove kong
+
+dependencies: bin/grpcurl bin/h2client
+ $(warning 'dependencies' target is deprecated, this is now not needed when using `make dev`, but are kept for installation that are not built by Bazel)
+
+ for rock in $(DEV_ROCKS) ; do \
+ if luarocks list --porcelain $$rock | grep -q "installed" ; then \
+ echo $$rock already installed, skipping ; \
+ else \
+ echo $$rock not found, installing via luarocks... ; \
+ luarocks install $$rock OPENSSL_DIR=$(OPENSSL_DIR) YAML_DIR=$(YAML_DIR) || exit 1; \
+ fi \
+ done;
+
+install-legacy:
+ @luarocks make OPENSSL_DIR=$(OPENSSL_DIR) YAML_DIR=$(YAML_DIR)
+
+dev-legacy: remove install-legacy dependencies
diff --git a/kong-versions/test9.9.9.3/kong/bin/busted b/kong-versions/test9.9.9.3/kong/bin/busted
new file mode 100755
index 00000000..34885dbb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/bin/busted
@@ -0,0 +1,106 @@
+#!/usr/bin/env resty
+
+setmetatable(_G, nil)
+
+local pl_path = require("pl.path")
+local pl_file = require("pl.file")
+
+local tools_system = require("kong.tools.system")
+
+local emmy_debugger = require("kong.tools.emmy_debugger")
+
+local cert_path do
+ local busted_cert_file = pl_path.tmpname()
+ local busted_cert_content = pl_file.read("spec/fixtures/kong_spec.crt")
+
+ local system_cert_path, err = tools_system.get_system_trusted_certs_filepath()
+ if system_cert_path then
+ busted_cert_content = busted_cert_content .. "\n" .. pl_file.read(system_cert_path)
+ end
+
+ pl_file.write(busted_cert_file, busted_cert_content)
+ cert_path = busted_cert_file
+end
+
+local DEFAULT_RESTY_FLAGS=string.format(" -c 4096 --http-conf 'lua_ssl_trusted_certificate %s;' ", cert_path)
+
+if not os.getenv("KONG_BUSTED_RESPAWNED") then
+ -- initial run, so go update the environment
+ local script = {}
+ for line in io.popen("set"):lines() do
+ local ktvar, val = line:match("^KONG_TEST_([^=]*)=(.*)")
+ if ktvar then
+ -- reinserted KONG_TEST_xxx as KONG_xxx; append
+ table.insert(script, "export KONG_" .. ktvar .. "=" ..val)
+ end
+
+ local var = line:match("^(KONG_[^=]*)")
+ local var_for_spec = line:match("^(KONG_SPEC_[^=]*)")
+ if var and not var_for_spec then
+ -- remove existing KONG_xxx and KONG_TEST_xxx variables; prepend
+ table.insert(script, 1, "unset " .. var)
+ end
+ end
+ -- add cli recursion detection
+ table.insert(script, "export KONG_BUSTED_RESPAWNED=1")
+
+ -- XXX EE
+ table.insert(script, "export KONG_IS_TESTING=1")
+
+ -- rebuild the invoked commandline, while inserting extra resty-flags
+ local resty_flags = DEFAULT_RESTY_FLAGS
+ local cmd = { "exec", "/usr/bin/env", "resty" }
+ local cmd_prefix_count = #cmd
+ for i = 0, #arg do
+ if arg[i]:sub(1, 12) == "RESTY_FLAGS=" then
+ resty_flags = arg[i]:sub(13, -1)
+
+ else
+ table.insert(cmd, "'" .. arg[i] .. "'")
+ end
+ end
+
+ -- create shared dict
+ resty_flags = resty_flags .. require("spec.fixtures.shared_dict")
+
+ if resty_flags then
+ table.insert(cmd, cmd_prefix_count+1, resty_flags)
+ end
+
+ table.insert(script, table.concat(cmd, " "))
+
+ -- recurse cli command, with proper variables (un)set for clean testing
+ local _, _, rc = os.execute(table.concat(script, "; "))
+ os.exit(rc)
+end
+
+pcall(require, "luarocks.loader")
+
+if os.getenv("BUSTED_EMMY_DEBUGGER") then
+ emmy_debugger.init({
+ debugger = os.getenv("BUSTED_EMMY_DEBUGGER"),
+ host = os.getenv("BUSTED_EMMY_DEBUGGER_HOST"),
+ port = os.getenv("BUSTED_EMMY_DEBUGGER_PORT"),
+ wait = true,
+ source_path = os.getenv("BUSTED_EMMY_DEBUGGER_SOURCE_PATH"),
+ })
+end
+
+require("kong.globalpatches")({
+ cli = true,
+ rbusted = true
+})
+
+-- some libraries used in test like spec/helpers
+-- calls cosocket in module level, and as LuaJIT's
+-- `require` is implemented in C, this throws
+-- "attempt to yield across C-call boundary" error
+-- the following pure-lua implementation is to bypass
+-- this limitation, without need to modify all tests
+_G.require = require "spec.require".require
+
+-- Busted command-line runner
+require 'busted.runner'({ standalone = false })
+
+
+-- vim: set ft=lua ts=2 sw=2 sts=2 et :
diff --git a/kong-versions/test9.9.9.3/kong/bin/kong b/kong-versions/test9.9.9.3/kong/bin/kong
new file mode 100755
index 00000000..f5dce410
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/bin/kong
@@ -0,0 +1,162 @@
+#!/usr/bin/env resty
+
+setmetatable(_G, nil)
+pcall(require, "luarocks.loader")
+package.path = (os.getenv("KONG_LUA_PATH_OVERRIDE") or "") .. "./?.lua;./?/init.lua;" .. package.path
+require("kong.globalpatches")({ cli = true })
+math.randomseed() -- Generate PRNG seed
+
+local pl_app = require "pl.lapp"
+local pl_utils = require "pl.utils"
+local inject_confs = require "kong.cmd.utils.inject_confs"
+
+local options = [[
+ --v verbose
+ --vv debug
+]]
+
+local cmds_arr = {}
+local cmds = {
+ start = true,
+ stop = true,
+ quit = true,
+ restart = true,
+ reload = true,
+ health = true,
+ check = true,
+ prepare = true,
+ migrations = true,
+ version = true,
+ config = true,
+ roar = true,
+ hybrid = true,
+ vault = true,
+ -- [=[ EE
+ runner = true,
+ debug = true,
+ --]=]
+}
+
+-- unnecessary to inject nginx directives for these simple cmds
+local skip_inject_cmds = {
+ version = true,
+ roar = true,
+ check = true,
+ stop = true,
+ quit = true,
+ health = true,
+ hybrid = true,
+ debug = true,
+}
+
+for k in pairs(cmds) do
+ cmds_arr[#cmds_arr+1] = k
+end
+
+table.sort(cmds_arr)
+
+local help = string.format([[
+Usage: kong COMMAND [OPTIONS]
+
+The available commands are:
+ %s
+
+Options:
+%s]], table.concat(cmds_arr, "\n "), options)
+
+local cmd_name = table.remove(arg, 1)
+if not cmd_name then
+ pl_app(help)
+ pl_app.quit()
+elseif not cmds[cmd_name] then
+ pl_app(help)
+ pl_app.quit("No such command: " .. cmd_name)
+end
+
+local cmd = require("kong.cmd." .. cmd_name)
+local cmd_lapp = cmd.lapp
+
+if cmd_lapp then
+ cmd_lapp = cmd_lapp .. options -- append universal options
+ arg = pl_app(cmd_lapp)
+end
+
+-- check sub-commands
+if cmd.sub_commands then
+ local sub_cmd = table.remove(arg, 1)
+ if not sub_cmd then
+ pl_app.quit()
+ elseif not cmd.sub_commands[sub_cmd] then
+ pl_app.quit("No such command for " .. cmd_name .. ": " .. sub_cmd)
+ else
+ arg.command = sub_cmd
+ end
+end
+
+-- inject necessary nginx directives (e.g. lmdb_*, lua_ssl_*)
+-- into the temporary nginx.conf that `resty` will create
+local main_conf = ""
+local http_conf = ""
+local stream_conf = ""
+
+if not skip_inject_cmds[cmd_name] then
+ local pok, confs = xpcall(inject_confs.compile_confs, function(err)
+ if not (arg.v or arg.vv) then
+ err = err:match "^.-:.-:.(.*)$"
+ io.stderr:write("Error: " .. err .. "\n")
+ io.stderr:write("\n Run with --v (verbose) or --vv (debug) for more details\n")
+ else
+ local trace = debug.traceback(err, 2)
+ io.stderr:write("Error: \n")
+ io.stderr:write(trace .. "\n")
+ end
+
+ pl_app.quit(nil, true)
+ end, arg)
+
+ main_conf = confs.main_conf
+ http_conf = confs.http_conf
+ stream_conf = confs.stream_conf
+end
+
+-- construct the args table
+local args_table = { "{" }
+for k, v in pairs(arg) do
+ if type(k) == "string" then
+ k = "\"" .. k .. "\""
+ end
+ if type(v) == "string" then
+ v = "\"" .. v .. "\""
+ end
+
+ table.insert(args_table, string.format("[%s] = %s,", k, v))
+end
+table.insert(args_table, "}")
+
+local args_str = table.concat(args_table, " ")
+
+local inline_code = string.format([[
+setmetatable(_G, nil)
+
+pcall(require, "luarocks.loader")
+
+package.path = (os.getenv("KONG_LUA_PATH_OVERRIDE") or "") .. "./?.lua;./?/init.lua;" .. package.path
+
+require("kong.cmd.init")("%s", %s)
+]], cmd_name, args_str)
+
+local resty_ngx_log_level
+if arg.vv then
+ resty_ngx_log_level = "debug"
+elseif arg.v then
+ resty_ngx_log_level = "info"
+end
+
+local resty_cmd = string.format(
+ "resty %s --main-conf \"%s\" --http-conf \"%s\" --stream-conf \"%s\" -e '%s'",
+ resty_ngx_log_level and ("--errlog-level " .. resty_ngx_log_level) or "", main_conf,
+ http_conf, stream_conf, inline_code)
+
+local _, code = pl_utils.execute(resty_cmd)
+os.exit(code)
+-- vim: set ft=lua ts=2 sw=2 sts=2 et :
diff --git a/kong-versions/test9.9.9.3/kong/bin/kong-health b/kong-versions/test9.9.9.3/kong/bin/kong-health
new file mode 100755
index 00000000..9b39555f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/bin/kong-health
@@ -0,0 +1,79 @@
+#!/usr/bin/env resty
+
+setmetatable(_G, nil)
+package.path = (os.getenv("KONG_LUA_PATH_OVERRIDE") or "") .. "./?.lua;./?/init.lua;" .. package.path
+
+local kill = require "kong.cmd.utils.kill"
+local kong_default_conf = require "kong.templates.kong_defaults"
+local pl_app = require "pl.lapp"
+local pl_config = require "pl.config"
+local pl_path = require "pl.path"
+local pl_stringio = require "pl.stringio"
+
+local KONG_DEFAULT_PREFIX = "/usr/local/kong"
+
+
+local function get_kong_prefix()
+ local prefix = os.getenv("KONG_PREFIX")
+
+ if not prefix then
+ local s = pl_stringio.open(kong_default_conf)
+ local defaults = pl_config.read(s, {
+ smart = false,
+ list_delim = "_blank_" -- mandatory but we want to ignore it
+ })
+ s:close()
+ if defaults then
+ prefix = defaults.prefix
+ end
+
+ end
+
+ return prefix or KONG_DEFAULT_PREFIX
+end
+
+
+local function execute(args)
+ local prefix = args.prefix or get_kong_prefix(args)
+ assert(pl_path.exists(prefix), "no such prefix: " .. prefix)
+
+ local kong_env = pl_path.join(prefix, ".kong_env")
+ assert(pl_path.exists(kong_env), "Kong is not running at " .. prefix)
+
+ print("")
+ local pid_file = pl_path.join(prefix, "pids", "nginx.pid")
+ kill.is_running(pid_file)
+ assert(kill.is_running(pid_file), "Kong is not running at " .. prefix)
+ print("Kong is healthy at ", prefix)
+end
+
+
+local lapp = [[
+Usage: kong-health [OPTIONS]
+Check if the necessary services are running for this node.
+Options:
+ -p,--prefix (optional string) prefix at which Kong should be running
+ --v verbose
+ --vv debug
+]]
+
+local function run(args)
+ args = pl_app(lapp)
+ xpcall(function() execute(args) end, function(err)
+ if not (args.v or args.vv) then
+ err = err:match "^.-:.-:.(.*)$"
+ io.stderr:write("Error: " .. err .. "\n")
+ io.stderr:write("\n Run with --v (verbose) or --vv (debug) for more details\n")
+ else
+ local trace = debug.traceback(err, 2)
+ io.stderr:write("Error: \n")
+ io.stderr:write(trace .. "\n")
+ end
+ pl_app.quit(nil, true)
+ end)
+end
+
+
+run(arg)
+
+-- vim: set ft=lua ts=2 sw=2 sts=2 et :
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/README.md b/kong-versions/test9.9.9.3/kong/spec-ee/README.md
new file mode 100644
index 00000000..4e468446
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/README.md
@@ -0,0 +1,4 @@
+Test helpers for Kong-EE (integration) tests
+============================================
+
+See `spec/README.md` on how to render the test documentation.
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/cookie_helper.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/cookie_helper.lua
new file mode 100644
index 00000000..80e61089
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/cookie_helper.lua
@@ -0,0 +1,141 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local date = require "date"
+
+-- A cookie helper class
+local Cookie = {}
+Cookie.__index = Cookie
+
+-- Create a new cookie
+function Cookie:new(name, value, path)
+ local cookie = {
+ name = name,
+ value = value or "",
+ path = path or "/",
+ expires = nil
+ }
+ setmetatable(cookie, self)
+ return cookie
+end
+
+-- Cookie is able to be converted to a header string
+function Cookie:to_header()
+ local cookie_str = self.name .. "=" .. self.value
+ if self.path then
+ cookie_str = cookie_str .. "; path=" .. self.path
+ end
+ return cookie_str
+end
+
+-- CookieManager is able to manage multiple cookies
+local CookieManager = {}
+CookieManager.__index = CookieManager
+
+-- Create a new cookie manager
+function CookieManager:new()
+ local cookie_mgr = {
+ cookies = {}
+ }
+ setmetatable(cookie_mgr, self)
+ return cookie_mgr
+end
+
+-- Add a cookie to the manager
+function CookieManager:add(name, value, path)
+ local cookie = Cookie:new(name, value, path)
+ table.insert(self.cookies, cookie)
+end
+
+local function trim(s)
+ return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+-- Parse Set-Cookie header and add to the manager
+function CookieManager:from_set_cookie(cookie_string)
+ local parts = cookie_string:gmatch("([^;]+)")
+ local main_part = parts()
+ local name, value = main_part:match("([^=]+)=([^=]+)")
+
+ -- find the cookie in self.cookies if exists
+ local cookie
+ for _, c in ipairs(self.cookies) do
+ if c.name == name then
+ cookie = c
+ break
+ end
+ end
+
+ -- or insert a new one to self.cookies
+ if not cookie then
+ cookie = Cookie:new(name, value)
+ table.insert(self.cookies, cookie)
+ end
+
+ for other_part in parts do
+ local opt_iter = other_part:gmatch("([^=]+)")
+ local opt_name = opt_iter()
+ opt_name = trim(opt_name)
+
+ if opt_name:lower() == "path" then
+ cookie.path = trim(opt_iter())
+ elseif opt_name:lower() == "expires" then
+ local expires = trim(opt_iter())
+ local expires_date = date(expires)
+ if expires_date < date() then
+ -- remove the cookie if it's expired
+ for i, c in ipairs(self.cookies) do
+ if c.name == name then
+ table.remove(self.cookies, i)
+ break
+ end
+ end
+ -- and break the parse loop
+ break
+ else
+ cookie.expires = date(expires)
+ end
+ end
+ end
+end
+
+function CookieManager:parse_set_cookie_headers(headers)
+ if type(headers) == "table" then
+ for _, cookie_string in ipairs(headers) do
+ self:from_set_cookie(cookie_string)
+ end
+ else
+ self:from_set_cookie(headers) -- is cookie_string
+ end
+end
+
+-- Get a cookie by name
+function CookieManager:get(name)
+ for _, cookie in ipairs(self.cookies) do
+ if cookie.name == name then
+ return cookie
+ end
+ end
+end
+
+-- CookieManager is able to be converted to a header string
+function CookieManager:to_header(path)
+ local cookie_str = ""
+ for _, cookie in ipairs(self.cookies) do
+ if path == nil or cookie.path == "/" or cookie.path:find("^" .. path) ~= nil then
+ cookie_str = cookie_str .. cookie:to_header() .. "; "
+ end
+ end
+
+ -- remove trailing "; "
+ return cookie_str:sub(1, -3)
+end
+
+return {
+ Cookie = Cookie,
+ CookieManager = CookieManager
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/.keep b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/event-hooks-tester/handler.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/event-hooks-tester/handler.lua
new file mode 100644
index 00000000..d4118800
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/event-hooks-tester/handler.lua
@@ -0,0 +1,36 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local event_hooks = require "kong.enterprise_edition.event_hooks"
+local get_request_id = require("kong.tracing.request_id").get
+local kong = kong
+
+local EventHooksHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+
+function EventHooksHandler:init_worker()
+ event_hooks.publish("foo", "bar", {
+ fields = { "msg" },
+ })
+end
+
+
+function EventHooksHandler:access()
+ local ok, err = event_hooks.emit("foo", "bar", {
+ msg = "Trigger an event in access phase, request_id: " .. get_request_id(),
+ }, true)
+
+ if not ok then
+ kong.log.warn("failed to emit event: ", err)
+ end
+end
+
+
+return EventHooksHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/event-hooks-tester/schema.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/event-hooks-tester/schema.lua
new file mode 100644
index 00000000..11b4c078
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/event-hooks-tester/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "event-hooks-tester",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/sleeper/handler.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/sleeper/handler.lua
new file mode 100644
index 00000000..28839684
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/sleeper/handler.lua
@@ -0,0 +1,30 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local Sleeper = {
+ VERSION = "1.0.0",
+ PRIORITY = 1,
+}
+
+local HEADERS = {
+ read_body_sleep = "NGX-Req-Get-Body-Data-Sleep",
+}
+
+do
+ local orig_get_body_data = ngx.req.get_body_data
+ ngx.req.get_body_data = function() -- luacheck: ignore
+ local delay = kong.request.get_header(HEADERS.read_body_sleep)
+ if delay then
+ ngx.sleep(tonumber(delay))
+ end
+ return orig_get_body_data()
+ end
+end
+
+
+return Sleeper
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/sleeper/schema.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/sleeper/schema.lua
new file mode 100644
index 00000000..73775a26
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_plugins/kong/plugins/sleeper/schema.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return {
+ name = "sleeper",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_vaults/kong/vaults/lic_required_vault/init.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_vaults/kong/vaults/lic_required_vault/init.lua
new file mode 100644
index 00000000..1f7bac69
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_vaults/kong/vaults/lic_required_vault/init.lua
@@ -0,0 +1,37 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local http = require "resty.luasocket.http"
+
+
+local function init()
+end
+
+
+local function get(conf, resource, version)
+ local client, err = http.new()
+ if not client then
+ return nil, err
+ end
+
+ client:set_timeouts(20000, 20000, 20000)
+ client:request_uri("http://127.0.0.1:" .. conf.port , {
+ method = "GET",
+ path = "/",
+ })
+
+ return resource
+end
+
+
+return {
+ VERSION = "1.0.0",
+ license_required = true,
+ init = init,
+ get = get,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_vaults/kong/vaults/lic_required_vault/schema.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_vaults/kong/vaults/lic_required_vault/schema.lua
new file mode 100644
index 00000000..d2d91b4d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/custom_vaults/kong/vaults/lic_required_vault/schema.lua
@@ -0,0 +1,26 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+return {
+ name = "lic_required_vault",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { prefix = { type = "string", match = [[^[%a_][%a%d_]*$]] } },
+ { port = typedefs.port { required = true, default = 8200 } },
+ { ttl = typedefs.ttl },
+ { neg_ttl = typedefs.ttl },
+ { resurrect_ttl = typedefs.ttl },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/dpop.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/dpop.lua
new file mode 100644
index 00000000..38f1e64c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/dpop.lua
@@ -0,0 +1,142 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local jwa = "kong.openid-connect.jwa"
+
+-- hash == HAvngAt2DlJLxgc7-r5FNlejviK9SirZ8SH4-tvPU9c
+local CLIENT_KEY = {
+ p = "3mwz_NSaJ1h5T3fPnAUq608-VfQI76gZi-s7t0gcmK0nwqMeZtuDSXF8euq3VV6-nwEArsYIougHU29sh73lJDnlmww78fwvim64zP_ZTWBNy-4Igxb5V60Ke3nA50LgHmsD7wsFTSKYKr07CgO5Vlh3W7SWGOG5o_-DeGbonPE",
+ kty = "RSA",
+ q = "wrZnSIxmJiKEgXrL45R_RiO-mthoKOPSS8TKkqEbSZl4fKKqgV1PIxIIot5gG27G34dBJutiuX_zcuTMrTlN-3Seoa7h_dI66V8kvTrJdCIuWfThR_uGnitgRvpXT9iJ6bDk5bo4XQ6tUo9pO35kEOjIoC2gw3qzuq6a-mW2wGU",
+ d = "H1pg3MDIn0rqKwoUF1pyO5fT58zOzvI8PPUka2KX_7Ute7FU7ML1rPfozJQBvCUdOfjF5zSJNd_OELAWhIvn72B7kh8iZcrn4LjhIVuquXpsldqvQeYAMGSNsbKBmeQauMwsCBdtYdOx2Ijh9f5TtiaRI3CmX_yTuQgSYfR5MQO1INxnkL3fj9jFX5sCUHqHOdv1pg02VytezZ9-R4uzRmW6T5Jc2y37MAg_YRUDvAGM9uUj3dXok8r9Ox7EiPp7N-KqCj5HZoyxzaCtBykEF2TvsjmStQtUEkHEv3yCL9XeLb0kVY84EfSCuBUHP98xaMdE1cUXFK_t2Sec6bnfgQ",
+ e = "AQAB",
+ use = "sig",
+ kid = "test",
+ qi = "x2osCFlaRU4IMTWf-hWzWfcpE5qnV9Dj9184js4eEQZufQagcxNiJEvGl7boOAvqL36mZlfevc0nKNBjLtB9P6Zdxs-DqcNOuUdhSaBfSDsw4Vcy0qbnuSeTEXXEjegrNhoMQfuXigaaeMOXDPFSf59B36oM4EP1IApisn-fzEg",
+ dp = "JeYsCZW0WqXxrb_NiVk8EfJjvcOiTivHhpbjivxnmwBOORUQVhhrS3Vh75PU_1_wfSlvk1g9Gn0M2oOu64ZI6B5RaFTyVe5Rr3XkWVHzFgMl3mzF2IhunijnE1kQrJcxlx1HA1FOavFNGmM6Dx_JUdQrKl4gAUddGXutTDPEyRE",
+ alg = "RS512",
+ dq = "uajnTrvg4tfi9Ps70mLUAPMwWcGjf-nLqceZSLspo8IcqusIHZX1UYFujq3vgfjc1GLJcuzbE_m3DoSvzTRo8S2_3Hc-saF13vSDuZOGZ5_4BnqDHPnu4H6HrOYjvtTAm_26JHquJ71I8wIf20Sm8aClPGaFdh9XpNe8mqnF2ik",
+ n = "qSx6DyAxPLveO90UDaCDT1WwJyI94GgaSXYEersmXcZb1E2-nxYDdIEct437B2_c6KWT1TkgjSb9sSIrhB5x9__kvT5FqFj6zqdjvaqutZ9tT4dhF6y_g1MhDcncF-UeaXILIlgMVdkLAckrGYJVVFA-7445FvOkj2cjU1Beikot20W__H6k2_6O_CRpiwNXm0UY-K3j0fmKvFb4EfsH9qOiZ0-ui8GR8lSVvbflKzbbEbkPzTGpPwoDieKdoM5LyEUhpSlaWK6qJa6NYxq1tx1TLxymfuuPcfASMVl2Yx5zQXRHBaR4WWoCQ9ODB5VGCX6okUcGC-SB_FhTt9CrFQ"
+}
+
+local CLIENT_KEY_PUBLIC = {
+ kty = "RSA",
+ e = "AQAB",
+ use = "sig",
+ kid = "test",
+ alg = "RS512",
+ n = "qSx6DyAxPLveO90UDaCDT1WwJyI94GgaSXYEersmXcZb1E2-nxYDdIEct437B2_c6KWT1TkgjSb9sSIrhB5x9__kvT5FqFj6zqdjvaqutZ9tT4dhF6y_g1MhDcncF-UeaXILIlgMVdkLAckrGYJVVFA-7445FvOkj2cjU1Beikot20W__H6k2_6O_CRpiwNXm0UY-K3j0fmKvFb4EfsH9qOiZ0-ui8GR8lSVvbflKzbbEbkPzTGpPwoDieKdoM5LyEUhpSlaWK6qJa6NYxq1tx1TLxymfuuPcfASMVl2Yx5zQXRHBaR4WWoCQ9ODB5VGCX6okUcGC-SB_FhTt9CrFQ"
+}
+
+-- hash == wM3HnqLyId6Mdph9R6tXpNvVCNt8YIdL2utHsximdXo
+local CLIENT_KEY2 = {
+ p = "_gyDtXWWBukWNGY56KkShmpyy7LbvEla8P_OEyHzq6Q2CMtF5ON0hl5tn27PuJW_GZb8LZFwks50iCTdmPeDf3G-vEJG7p929p0Wq2Aw-XEFDqwt1UJ75u8S1Bi-PeA6G6tvrSRh0g8UtASveUPLA-STgdYmdjgZuNUMp2ZpHds",
+ kty = "RSA",
+ q = "vh6Drg7BvdNbwWrRBbga7CSbZVByjhFgDfHao2jFFLY5_8d5iKP5tC2Bv9s9FDr3prCW-Y581iRmekW5IKq1wMkCCppmRRbzKHGuzEcK5q6FgLSkqBhFXlZMES1cbGVesfPFtUZL-Wr3Io-Fm363YZJUMwduv_MgAZem76j6gAU",
+ d = "SzB89kWLwUTB6EzWTYcMiphYdNBh3pj6_VTuDmMgg-na_9A1xPfQkhj7oAFepGwHTUO0XHl3bJS8fi8XzUnrPMUIYNeunu0EQwu4cHG24Nf0zCb2ZkxpULsx5ZCG9V3cn712b5OA9j1uMofzuJyrjI2X8BUaSS7aA-r_hCJo-1jo2sncJ646Rx9PneAgn1HzEuxOwdYy3VuuW8bq1ufYtc04MqJNzLT-KkznEv5H3wBAzO8oEdaLRUXiUWxBMYXTdqcGVWpl0IAS7kpqTUujY_gtNiW-LZBuNWRYL5uRHBaYGeDtMSafR6_tOwzqVT2ZIegVZ282mdL_BtOrzeM8cQ",
+ e = "AQAB",
+ use = "sig",
+ kid = "test",
+ qi = "xot6uLK9LGwhZQxaA-D8IJ8J8fHd-VBUX9ReJanEMvkVD2TREYFSKepNvPcKMWdq2cqA10W8R8igYgvYAwwH8WEf0NSnxVRXP1TpnzTwFOffRoml1PYeGqc46zweXlnw0FZLUxDqvT7nUCXsdkq9k7Zk_ND1TRolxLkUDuatx9s",
+ dp = "YngDTQBIqGEMBD9jTrTJw2PbHu0ykmZ0Y1kjTPMp-Wtqrjnr2232KmbLYrKWvNr9-TM2h4sJ8T0omeSAJ9w4EdvKrDmcOL2CZNA6iy57jROrfCZslW5xi-86gw8cHeudWkA2xwuFBuBli-kNApmuRNICAp84xTW1cpjRkMj3EWU",
+ alg = "RS512",
+ dq = "PZBYB7cTmcqlfb5_LSDu5uT7xRUF42dQ-XMF38B_gTN5GJCZlFu08lmCGISABNsLctjgKrOvTRDAdnu5dRCShnkQxio1T84cs04M0m125DhDVugoIZ6qZ9_-BdnwgdFZlrpfnVHELGIs4O4kz7N64oel6FhRzqqGBL38-sJ3S5k",
+ n = "vKuR5T8VdDu0JTvp6R3WQtEefptI5RLGpMjtn_6T6toEhm6UDlNyZ3tNYlIL26RzH5xGLoRcWg8u-obP4KbKVTLhxTrTCTC-29ToJPQsK_p86d2P9a1PMGfYVI2oqoSTySGQCTGA8sCCUpcf5nDqtAJRaNRJxSl0BSVgxe8m7gyO3J9c8gH22wCzCiUMNeEdr1YI2Nq3TjCN5-O0nngOfR2A4KMB25hxd98ndTrWUNyzgoWJ0VLyTrVd9Qh9RdfU-_zo79sV-6wKsYMJmOc8KDOhfq_9sNbVADwxjMqXrzyDNlc69LlJX1a2XFHVSST4cgkpo07lD3aV19ItbtkVRw"
+}
+
+local CLIENT_KEY2_PUBLIC = {
+ kty = "RSA",
+ e = "AQAB",
+ use = "sig",
+ kid = "test",
+ alg = "RS512",
+ n = "vKuR5T8VdDu0JTvp6R3WQtEefptI5RLGpMjtn_6T6toEhm6UDlNyZ3tNYlIL26RzH5xGLoRcWg8u-obP4KbKVTLhxTrTCTC-29ToJPQsK_p86d2P9a1PMGfYVI2oqoSTySGQCTGA8sCCUpcf5nDqtAJRaNRJxSl0BSVgxe8m7gyO3J9c8gH22wCzCiUMNeEdr1YI2Nq3TjCN5-O0nngOfR2A4KMB25hxd98ndTrWUNyzgoWJ0VLyTrVd9Qh9RdfU-_zo79sV-6wKsYMJmOc8KDOhfq_9sNbVADwxjMqXrzyDNlc69LlJX1a2XFHVSST4cgkpo07lD3aV19ItbtkVRw"
+}
+
+-- "jkt": "HAvngAt2DlJLxgc7-r5FNlejviK9SirZ8SH4-tvPU9c"
+local KEY_ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." ..
+"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE" ..
+"2MjM5MDIyLCJjbmYiOnsiamt0IjoiSEF2bmdBdDJEbEpMeGdjNy1yNUZObGVqdm" ..
+"lLOVNpclo4U0g0LXR2UFU5YyJ9fQ." ..
+"OHqhY9QCVBjWxa7FYMj9AMj5THTADUPq-av_H5e3AHk"
+
+-- "jkt": "wM3HnqLyId6Mdph9R6tXpNvVCNt8YIdL2utHsximdXo"
+local WRONG_KEY_ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." ..
+"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5M" ..
+"DIyLCJjbmYiOnsiamt0Ijoid00zSG5xTHlJZDZNZHBoOVI2dFhwTnZWQ050OFlJZEwydX" ..
+"RIc3hpbWRYbyJ9fQ." ..
+"dlu7x_rj20b-anaAr5TEBEWRr04htBQFITMYNeP-oJ0"
+
+local NO_KEY_ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIi" ..
+"OiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwR" ..
+"JSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
+
+local KEY_INTROSPECTION_DATA = {
+ active = true,
+ aud = { "kong" },
+ client_id = "kong",
+ cnf = {
+ ["jkt"] = "HAvngAt2DlJLxgc7-r5FNlejviK9SirZ8SH4-tvPU9c",
+ },
+ sub = "kong",
+ token_type = "access_token",
+}
+
+local WRONG_KEY_INTROSPECTION_DATA = {
+ active = true,
+ aud = { "kong" },
+ client_id = "kong",
+ cnf = {
+ ["jkt"] = "wM3HnqLyId6Mdph9R6tXpNvVCNt8YIdL2utHsximdXo",
+ },
+ sub = "kong",
+ token_type = "access_token",
+}
+
+local NO_KEY_INTROSPECTION_DATA = {
+ active = true,
+ aud = { "kong" },
+ client_id = "kong",
+ sub = "kong",
+ token_type = "access_token",
+}
+
+
+local function sign_dpop_header(req, nonce, key, pub_key, iat, jti, alg)
+ alg = alg or "SHA512"
+ local jwt_token = jwa.sign(key.alg, key, {
+ header = {
+ typ = "dpop+jwt",
+ alg = key.alg,
+ jwk = pub_key,
+ },
+ payload = {
+ jti = jti or "1234567890",
+ htm = req.method,
+ htu = req.uri,
+ iat = iat or ngx.now(),
+ nonce = nonce,
+ },
+ })
+ return string.format("DPoP %s", jwt_token)
+end
+
+
+
+return {
+ CLIENT_KEY = CLIENT_KEY,
+ CLIENT_KEY_PUBLIC = CLIENT_KEY_PUBLIC,
+ CLIENT_KEY2 = CLIENT_KEY2,
+ CLIENT_KEY2_PUBLIC = CLIENT_KEY2_PUBLIC,
+ KEY_ACCESS_TOKEN = KEY_ACCESS_TOKEN,
+ WRONG_KEY_ACCESS_TOKEN = WRONG_KEY_ACCESS_TOKEN,
+ NO_KEY_ACCESS_TOKEN = NO_KEY_ACCESS_TOKEN,
+ KEY_INTROSPECTION_DATA = KEY_INTROSPECTION_DATA,
+ WRONG_KEY_INTROSPECTION_DATA = WRONG_KEY_INTROSPECTION_DATA,
+ NO_KEY_INTROSPECTION_DATA = NO_KEY_INTROSPECTION_DATA,
+ sign_dpop_header = sign_dpop_header,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/expired_license.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/expired_license.json
new file mode 100644
index 00000000..6761c467
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/expired_license.json
@@ -0,0 +1,16 @@
+{
+ "license": {
+ "version": 1,
+ "payload": {
+ "customer": "ee-build-test",
+ "product_subscription": "Kong Only",
+ "support_plan": "None",
+ "admin_seats": "1",
+ "dataplanes": "1",
+ "license_creation_date": "2020-9-23",
+ "license_expiration_date": "2020-09-24",
+ "license_key": "ASDASDASDASDASDASDASDASDASD_a1VASASD"
+ },
+ "signature": "8229128802796d7a846a6cabf9832af936c39b8ae154b03d49182e4195fd1697cb62cd79b7e7e529eb806b566edd8d5c8833a542363a0948dac31fce57df3a38"
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/factories/plugins.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/factories/plugins.lua
new file mode 100644
index 00000000..5c0afeb5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/factories/plugins.lua
@@ -0,0 +1,140 @@
+-- this software is copyright kong inc. and its licensors.
+-- use of the software is subject to the agreement between your organization
+-- and kong inc. if there is no such agreement, use is governed by and
+-- subject to the terms of the kong master software license agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ end of license 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local helpers = require "spec.helpers"
+
+local BaseEntitiesFactory = require("spec.fixtures.factories.plugins").EntitiesFactory
+local BasePluginFactory = require("spec.fixtures.factories.plugins").PluginFactory
+
+local EntitiesFactory = {}
+
+function EntitiesFactory:setup(strategy)
+ local bp, _ = helpers.get_db_utils(strategy,
+ { "plugins",
+ "routes",
+ "services",
+ "consumers",
+ "consumer_groups",
+ "consumer_group_consumers" },
+ { "key-auth", "request-transformer" })
+
+
+ local ef = BaseEntitiesFactory:setup(strategy)
+
+ local consumer_group_plat = assert(bp.consumer_groups:insert {
+ -- prefix names with A, B and C to ensure their ordering
+ -- as we sort by name in this iteration.
+ id = "00000000-0000-0000-0000-000000000000",
+ name = "A_Platinum",
+ })
+ local consumer_group_gold = assert(bp.consumer_groups:insert {
+ id = "10000000-0000-0000-0000-000000000000",
+ name = "B_Gold",
+ })
+ local consumer_group_silver = assert(bp.consumer_groups:insert {
+ id = "20000000-0000-0000-0000-000000000000",
+ name = "C_Silver",
+ })
+
+ assert(bp.consumer_group_consumers:insert({
+ consumer = { id = ef.alice_id },
+ consumer_group = { id = consumer_group_plat.id },
+ }))
+
+ assert(bp.consumer_group_consumers:insert({
+ consumer = { id = ef.eve_id },
+ consumer_group = { id = consumer_group_gold.id },
+ }))
+ assert(bp.consumer_group_consumers:insert({
+ consumer = { id = ef.eve_id },
+ consumer_group = { id = consumer_group_silver.id },
+ }))
+
+ ef.bp = bp
+ ef.consumer_group_platinum_id = consumer_group_plat.id
+ ef.consumer_group_gold_id = consumer_group_gold.id
+ ef.consumer_group_silver_id = consumer_group_silver.id
+ return ef
+end
+
+
+-- Define the PluginFactory table
+local PluginFactory = {}
+PluginFactory.__index = PluginFactory
+
+-- Inherit from BasePluginFactory
+setmetatable(PluginFactory, {__index = BasePluginFactory})
+
+-- The setup function
+function PluginFactory:setup(ef)
+ -- Create a new instance with the correct metatable
+ local instance = setmetatable({}, self)
+
+ -- Call the setup function on the BasePluginFactory
+ BasePluginFactory.setup(instance, ef)
+
+ -- Add EE only attributes
+ instance.consumer_group_platinum_id = ef.consumer_group_platinum_id
+ instance.consumer_group_gold_id = ef.consumer_group_gold_id
+ instance.consumer_group_silver_id = ef.consumer_group_silver_id
+
+ return instance
+end
+
+function PluginFactory:consumer_group_multiple_groups()
+ local header_name = "x-consumer-group-silver"
+ self:produce(header_name, {
+ -- eve is part of silver
+ consumer_group = { id = self.consumer_group_silver_id},
+ })
+ self:produce("x-consumer-group-gold", {
+ -- eve is part of gold
+ consumer_group = { id = self.consumer_group_gold_id},
+ })
+ -- check if gold was set
+ return "x-consumer-group-gold", header_name
+end
+
+function PluginFactory:consumer_group_service_route()
+ local header_name = "x-consumer-group-and-service-and-route"
+ self:produce(header_name, {
+ consumer_group = { id = self.consumer_group_platinum_id },
+ service = { id = self.service_id },
+ route = { id = self.route_id },
+ })
+ return header_name
+end
+
+function PluginFactory:consumer_group_route()
+ local header_name = "x-consumer-group-and-route"
+ self:produce(header_name, {
+ consumer_group = { id = self.consumer_group_platinum_id },
+ route = { id = self.route_id },
+ })
+ return header_name
+end
+
+function PluginFactory:consumer_group_service()
+ local header_name = "x-consumer-group-and-service"
+ self:produce(header_name, {
+ consumer_group = { id = self.consumer_group_platinum_id },
+ service = { id = self.service_id },
+ })
+ return header_name
+end
+
+function PluginFactory:consumer_group()
+ local header_name = "x-consumer-group"
+ self:produce(header_name, {
+ consumer_group = { id = self.consumer_group_platinum_id }
+ })
+ return header_name
+end
+return {
+ PluginFactory = PluginFactory,
+ EntitiesFactory = EntitiesFactory
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/feature_rate_limit_plugins.conf b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/feature_rate_limit_plugins.conf
new file mode 100644
index 00000000..ccd27096
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/feature_rate_limit_plugins.conf
@@ -0,0 +1,5 @@
+rate_limiting_restrict_redis_only=on
+response_ratelimiting_restrict_redis_only=on
+redis_host=a-redis-host.internal
+redis_port=17812
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/feature_vitals_tsdb.conf b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/feature_vitals_tsdb.conf
new file mode 100644
index 00000000..013d5895
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/feature_vitals_tsdb.conf
@@ -0,0 +1,3 @@
+vitals_prometheus_enable_cluster_level=on
+vitals_prometheus_auth_header=Basic supersecret
+vitals_prometheus_custom_filters=client="client1"
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/client.kong.test.crt b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/client.kong.test.crt
new file mode 100644
index 00000000..9f34c6bc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/client.kong.test.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCAdSgAwIBAgIUC0PiBBaFTNFA3luD/clpwRoUiNAwDQYJKoZIhvcNAQEL
+BQAwRjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJh
+bmNpc2NvMRIwEAYDVQQDEwlrb25nLnRlc3QwIBcNMjMwMTE4MjMyNjAwWhgPMjA1
+MDA2MDUyMzI2MDBaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEAxMQY2xpZW50LmtvbmcudGVzdDBZMBMG
+ByqGSM49AgEGCCqGSM49AwEHA0IABFKUjxEsR1ptgCiI+hQuBWckro5Ye9JByU6U
+w/jm6SLwrKkPI6h21h5+6XKvVjIli0JDbKBQmgqpI/ljVfOWR/2jgZMwgZAwDgYD
+VR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAw
+HQYDVR0OBBYEFPQ7r0wfD9hBwP6d6xuG0wP5e1Q+MB8GA1UdIwQYMBaAFMrYLDOz
+YFEPdpBvVrY50diVTTEDMBsGA1UdEQQUMBKCEGNsaWVudC5rb25nLnRlc3QwDQYJ
+KoZIhvcNAQELBQADggEBAKc+h5jywEdjkPGnP0chG5pWiHRrB3W2Vv3Qg2mVqanh
+4XTePNaovxLsjdniYwV2DUHvak3bW2SZJYBkwsdWyVqpJUPsWT+l2r0/Onzu1TwI
+Uv4EYnfF8PNsXv6aPp0d+ufabzMmfAnVDDxBCkagHSOEndqsMiowgiLMxV331PoY
+bq0w2v2TBK68tvN9dByBvaPO+lqc+1eKAgI0oaF2PahvWuuGAR6xQQ4hzP0X9KuA
+MRqfi3OrOgqblxMLA54Uu60dNRaGCU2hecGB1nZNbqCX79goC9YMtWY5Et9Zr/ZP
+6AzQiV7SSp3B/hycWrEkPMHk1EvKVlbcpVWKpdvlVxo=
+-----END CERTIFICATE-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/client.kong.test.key b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/client.kong.test.key
new file mode 100644
index 00000000..b00441c5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/client.kong.test.key
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIJSbICO7kV4+Js3KMmYIdi97ZuUIPbnRkZXUuKIn7XzeoAoGCCqGSM49
+AwEHoUQDQgAEUpSPESxHWm2AKIj6FC4FZySujlh70kHJTpTD+ObpIvCsqQ8jqHbW
+Hn7pcq9WMiWLQkNsoFCaCqkj+WNV85ZH/Q==
+-----END EC PRIVATE KEY-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/deny.kong.test.crt b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/deny.kong.test.crt
new file mode 100644
index 00000000..9a4e179d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/deny.kong.test.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC6DCCAdCgAwIBAgIUFh5XOK+C6w2Ys9xvBiDb/J29mzwwDQYJKoZIhvcNAQEL
+BQAwRjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJh
+bmNpc2NvMRIwEAYDVQQDEwlrb25nLnRlc3QwIBcNMjMwMTE4MjMyODAwWhgPMjA1
+MDA2MDUyMzI4MDBaMEsxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEXMBUGA1UEAxMOZGVueS5rb25nLnRlc3QwWTATBgcq
+hkjOPQIBBggqhkjOPQMBBwNCAARjodkyL3brvBVWKW/chtT8cYMlcK91FrGb5qsF
+HzIQv4QvHrWqBdpI1E97wpG1az8QqZjYG+zTY/4qQMgKFqx9o4GRMIGOMA4GA1Ud
+DwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0G
+A1UdDgQWBBT0JOdoKQ/7oAD3osEyzfG+E2j76jAfBgNVHSMEGDAWgBTK2Cwzs2BR
+D3aQb1a2OdHYlU0xAzAZBgNVHREEEjAQgg5kZW55LmtvbmcudGVzdDANBgkqhkiG
+9w0BAQsFAAOCAQEAJ+cN0sHdSzEQ0TDA4soe7HNO+h0xI3USvH+xi8IHIoJe9iXo
+AwZQqjKTCf8YUJAE0g8coG52nF+07rVDrapqRFvtdJcrYswJXyK8Pv6OGkOsDDnF
+B6czFsDfNhdKGqM0Ic9rUOR6hZqiFM68s+KRI8w02LNLtg1qKwL/83x/+olLWaef
+rnH2XLuj4zpyAnfR6m6hB3zTi0ubFhfRswDzEprMMYb9sWfAnKZNByn1/K1ViS+X
+LS2C6pEXU9Q5QCKribL0S2hizzKE9urLsJBISQkpaSKPFgdWl0xmz8p3x4jNE9B1
+QPSIfY5weguStTSd/NBvzELwxF3m/c+PKnwXLg==
+-----END CERTIFICATE-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/deny.kong.test.key b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/deny.kong.test.key
new file mode 100644
index 00000000..c63a2e18
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/deny.kong.test.key
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEICpD8KuHRdP9Ak04Yv8DHrHmkPLM7k52UCYxkO9fqgwFoAoGCCqGSM49
+AwEHoUQDQgAEY6HZMi9267wVVilv3IbU/HGDJXCvdRaxm+arBR8yEL+ELx61qgXa
+SNRPe8KRtWs/EKmY2Bvs02P+KkDIChasfQ==
+-----END EC PRIVATE KEY-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/kong.test.ca.crt b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/kong.test.ca.crt
new file mode 100644
index 00000000..31cf620f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/kong.test.ca.crt
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAkSgAwIBAgIUOuMoLPNQa+SQo2HpPRCMPV/1SlkwDQYJKoZIhvcNAQEL
+BQAwRjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJh
+bmNpc2NvMRIwEAYDVQQDEwlrb25nLnRlc3QwHhcNMjMwMTE4MjMyMzAwWhcNMjgw
+MTE3MjMyMzAwWjBGMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcT
+DVNhbiBGcmFuY2lzY28xEjAQBgNVBAMTCWtvbmcudGVzdDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAL4zf3FFaKKktKe9tziQBItYgEKB1RhUFJxKDT2X
+NugZQI7Cvyjeb9kr+oeFJhl2LxLhBNv/6+q2dqOz7Ia3Q6SCp5P2YbF0TIZ7DfjP
+ArXx5Ft17rgCyMg0EnvTU4OoZe/9QbOUVzlWyvGtXZCKCgoPkNApLGuucZE0+h7l
+RSdDbximtmGgg0j6KRtxme5GV8uPYb9yXV0fJdqX/eFYcj07RPBv2ApesZih2Ybi
+VCDrLmDRHb/TYBX82e2M/3AQvuDOyIsDYrvFJEModZGKleOL3qO/fXfPe5kJ99/v
+8PFrM/F4w8dw72A3VU4pxctBkzvwJECcPkLaYZhvQsKY6RcCAwEAAaNCMEAwDgYD
+VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMrYLDOzYFEP
+dpBvVrY50diVTTEDMA0GCSqGSIb3DQEBCwUAA4IBAQBMnvmz+aGDfiWr1wj8nhRz
+aB5rBnRAUKcoJnnLL2++dlZfFVXXe7E0Qr4AulIy9mr+zpFpIzMMCmSMhKnOtYoS
+uPsex4BqaNP/xQCIjfIQ17bv5ahtMpd9RkXqM/djVPhhadUJqtz3demkqe+tOqfY
+RIOP6x5cv0bvY3ezQfSYHKhNP3cx0qHEaIgDevgb1lxpkyPvRigr09ZmI9a6MD6F
+M7XdbQf2Gb2HX9kcL4FJw2LqzL9LADtK7RxvipCsgdW04xe1GonMr0z0xIajEb3K
+1ZPyl3R04HRz080Ft3OqPRmiSBOAZJFIybRKWLx8gZ1Gh2Nse6w4QNsq5+r6rlDZ
+-----END CERTIFICATE-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/kong.test.ca.key b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/kong.test.ca.key
new file mode 100644
index 00000000..43b394b2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/kong.test.ca.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+M39xRWiipLSn
+vbc4kASLWIBCgdUYVBScSg09lzboGUCOwr8o3m/ZK/qHhSYZdi8S4QTb/+vqtnaj
+s+yGt0OkgqeT9mGxdEyGew34zwK18eRbde64AsjINBJ701ODqGXv/UGzlFc5Vsrx
+rV2QigoKD5DQKSxrrnGRNPoe5UUnQ28YprZhoINI+ikbcZnuRlfLj2G/cl1dHyXa
+l/3hWHI9O0Twb9gKXrGYodmG4lQg6y5g0R2/02AV/NntjP9wEL7gzsiLA2K7xSRD
+KHWRipXji96jv313z3uZCfff7/DxazPxeMPHcO9gN1VOKcXLQZM78CRAnD5C2mGY
+b0LCmOkXAgMBAAECggEACYfjFvX3bE78jns/NyJgV+szYiHHX9S2w64iQ7fUFIVP
+atfvZEjhKey+PRQ6+IOnkDaeW7fTRoumHkXh0E5WoWO6qftqoc3GF+cKqDEXXa6l
+Vr/uGmJpVT7mvjW0taA2m3QBWtJQHJtwx/N8AMYqAp8NDqFBjJPNNQ2Giw3KDVGJ
+k/dbG2vtIj6aMc7ut6UedReUDpOW1HoDsro9RxKRX4ypVQRMzPRM8VWPtONpku8G
+5NrBsZoJSGcCo92Ltwy8JiQokBA3ZHdEU2sFFS9IHlfhWu8TLwru+zkMMcifII+c
+p/G4fU9lgJYPA2ARKSvl9+cC25vI/HZcXp71TsftgQKBgQDqYmjD5QnM7xwPyxgw
+pTFiAZnPIvlZppsXZ1uhsFwnojHNDo6gfGkXZjz7LnnnsrDPHohOi6YphZvuNE3D
+T8NkgaoBHPWGQ/QwDZE36+9V7xrwa1jiqWdmiC0ryQVn310CqiZGa4NpFrdhbsFq
+/cTeWjgArZIilHkY6xLG9REj5wKBgQDPvfbs+CSLdpM6pWMJW33guZYRWhVvmIQK
+9lvaX8UQwTtKOf8Ccr7jCLo5hCFzG4zcTlEHnUd+F18f4mRfoGxI/2R9gzJizpry
+wLYSSeeK+b346hoLhKc5EXveRBDRUcOS6UfaqtNsuDpy+zsZiW3Od1BmuN/3fcmt
+UR3Qex1rUQKBgH+DBkf8pPZO509YSFK+rD0LbegYuNsjHqD+5AT0803EmZ9bWhDJ
+XksSnf5w7HU266XikHla1A0m2QwgfGXTUROuzxRH8vMWqRQzbVulCdIQgPFnFTZK
+vywyzZBSjpRSGkHG24fYGefkKePh42wXnzDwmCMWSrhxWehKiy6eSLLlAoGBAMFN
+rFS4WZkEDt+FQXBVl/3h8sfWoRekPUnOD45VixOaGkdpS+bFSuYLGu2mC1IGkr30
+ieWh7gJgUP1kNa3doqUSMDmyONJEGV5E/6Zo6nEhDipt+yo4lzm9ERuzDksX3/n1
+5AyO5AjILGsNZuLORETYNRJAePbcLyt8zVdPGALhAoGBAIhgcrmDbgjteeuo31qg
+HJXkEBSKdWS44JnLfMfl86pF8RY/3Pi98JqL5IA0qBgWxmL6OUMpXMZOjJIkn2Su
++5YlNlbElCEERm8Leopo50Z/S/7OMzMKWHXgnj/0k1LaxO2vAytqPLDbIYyCr+7w
+OnJrCl23GuoSTOk34ibL3S7H
+-----END PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/other.test.crt b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/other.test.crt
new file mode 100644
index 00000000..485b4640
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/other.test.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC4DCCAcigAwIBAgIUBiyvfYNDTho/aBcrEZoQ5rdE9v4wDQYJKoZIhvcNAQEL
+BQAwRjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJh
+bmNpc2NvMRIwEAYDVQQDEwlrb25nLnRlc3QwIBcNMjMwMTE5MDA0NzAwWhgPMjA1
+MDA2MDYwMDQ3MDBaMEcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzETMBEGA1UEAxMKb3RoZXIudGVzdDBZMBMGByqGSM49
+AgEGCCqGSM49AwEHA0IABNAor5xiXRFBmtcS0DLm8EUaObDQIZP19WzCKt2PLXyS
+mErLiI9NXFGR9BLjIXsmRklf7IOCKO6pw08x2vYP42CjgY0wgYowDgYDVR0PAQH/
+BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0O
+BBYEFJv7fXcaZ+Qg90Dyr++puZvSBeZ/MB8GA1UdIwQYMBaAFMrYLDOzYFEPdpBv
+VrY50diVTTEDMBUGA1UdEQQOMAyCCm90aGVyLnRlc3QwDQYJKoZIhvcNAQELBQAD
+ggEBAK6ttR7DgFdE5wL6iks01yTcyGmLOIFYd+pWNPlqsDbY50FqZz4ES2lFZiu3
+ml3bOzXIkt4cEL2Ali3f78h/Qz+t2ue9GCqbuz8MiGtmzpFiMaNw4OQQtfBmmRcv
+0Za/sBqvdqe04W7FFNnUYlzR5TG68n+deaI1ECK/I7bdeF+iAOshZn7NA/9ObQtc
+/1MIOkeknY0VD2aARkGBBc8WXeOM3ZWnzpkmM1xRX5SdsHO5ABNBjsd1HyUKbwO1
+EnaHBQgS2WfRZecmFqAGKqJT94yj3fJALlHleiI5yxvmeaHkX6Qe80l3g1nzNnEV
+49IgLj1KiFOstLi/9NGWPPKn5Ac=
+-----END CERTIFICATE-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/other.test.key b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/other.test.key
new file mode 100644
index 00000000..b7cd5044
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/other.test.key
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIEcfRJyD43xdzLtm2zu9O2I4Gc1dgWClsQHUGRj11TUOoAoGCCqGSM49
+AwEHoUQDQgAE0CivnGJdEUGa1xLQMubwRRo5sNAhk/X1bMIq3Y8tfJKYSsuIj01c
+UZH0EuMheyZGSV/sg4Io7qnDTzHa9g/jYA==
+-----END EC PRIVATE KEY-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/server.kong.test.crt b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/server.kong.test.crt
new file mode 100644
index 00000000..40db9ae2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/server.kong.test.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCAdSgAwIBAgIUbIesJK6G+cSeGC5sPOB19EpLi1EwDQYJKoZIhvcNAQEL
+BQAwRjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJh
+bmNpc2NvMRIwEAYDVQQDEwlrb25nLnRlc3QwIBcNMjMwMTE4MjMyNzAwWhgPMjA1
+MDA2MDUyMzI3MDBaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEAxMQc2VydmVyLmtvbmcudGVzdDBZMBMG
+ByqGSM49AgEGCCqGSM49AwEHA0IABHTTPmOTHh8kPrGs4bfgyeM7HaMjAflXeRM+
+pOXXBXZNa5pD52UV8NkB4QzXsSixLYXeuPrpMB0/k6UpYD9Ne1mjgZMwgZAwDgYD
+VR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAw
+HQYDVR0OBBYEFOmB/rv752qDKbJiVQSl+cevDB9EMB8GA1UdIwQYMBaAFMrYLDOz
+YFEPdpBvVrY50diVTTEDMBsGA1UdEQQUMBKCEHNlcnZlci5rb25nLnRlc3QwDQYJ
+KoZIhvcNAQELBQADggEBAB1kwS9Mbe5HdqJNBnKeSsbDaXst1FmoPIilCEm77jc7
+2PCywxLwelSiHr4eqKopbc5B3lX27eqW6fnyHxL0cazIdUfKf1e2A3MKeQ0lzl5B
+BNN6Rrde0Y5QVfnmWNTJxeJO1Sh2ewdlmDyWEPLzV8BLGm3wtcCLmhs/VN0+Cj4n
+58avNrkJztRyjf9AxVObSH1BUCP67IlUUpgBSF4PCynoyCq0bpoox9UHV9sdI2Y3
+p3ci5KZpD/PF9coDas+pM+/o6vpgbR5vI7/G4LGF7BDmn5eXk60RZQBKO3IrUpvv
+XJmYWeviI9CZINGtkFZqBCvYNkoyalNeEHDNhhJsbpc=
+-----END CERTIFICATE-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/server.kong.test.key b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/server.kong.test.key
new file mode 100644
index 00000000..5acedffc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/hybrid-pki/server.kong.test.key
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEILAtN8/p8eAdPDnaKP2Z8RF9izmj41ToELVEh67jnsMnoAoGCCqGSM49
+AwEHoUQDQgAEdNM+Y5MeHyQ+sazht+DJ4zsdoyMB+Vd5Ez6k5dcFdk1rmkPnZRXw
+2QHhDNexKLEthd64+ukwHT+TpSlgP017WQ==
+-----END EC PRIVATE KEY-----
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keycloak_api.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keycloak_api.lua
new file mode 100644
index 00000000..cf089e36
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keycloak_api.lua
@@ -0,0 +1,185 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- references docs
+-- https://www.keycloak.org/docs-api/23.0.4/rest-api/index.html
+
+local fmt = string.format
+
+local KEYCLOAK_CONTEXT_PATH = ""
+local KEYCLOAK_HOSTNAME = os.getenv("KONG_SPEC_TEST_KEYCLOAK_HOST") or "keycloak"
+local KEYCLOAK_PORT = os.getenv("KONG_SPEC_TEST_KEYCLOAK_PORT_8080") or "8080"
+local KEYCLOAK_SSL_PORT = os.getenv("KONG_SPEC_TEST_KEYCLOAK_PORT_8443") or "8443"
+local KEYCLOAK_CLIENT_ID = "admin-cli"
+local KEYCLOAK_USERNAME = "admin"
+local KEYCLOAK_PASSWORD = "test" -- retrieve the password from the openid-connect/.pongo/keycloak.yml
+local KEYCLOAK_GRANT_TYPE = "password"
+local KEYCLOAK_REALM = "demo"
+
+local KONG_CLIENT_ID = "kong-client-secret"
+local KONG_CLIENT_SECRET = "38beb963-2786-42b8-8e14-a5f391b4ba93"
+
+local _keycloak = {}
+
+local function api_uri(self, path, ...)
+ local prefix = fmt("%s/admin/realms/%s", self.config.context_path, self.config.realm)
+ return prefix .. fmt(path, ...)
+end
+
+-- return 200
+function _keycloak:auth(client)
+ local url = fmt("%s/realms/master/protocol/openid-connect/token", self.config.context_path)
+ return client:send {
+ method = "POST",
+ path = url,
+ body = {
+ client_id = KEYCLOAK_CLIENT_ID,
+ username = KEYCLOAK_USERNAME,
+ password = KEYCLOAK_PASSWORD,
+ grant_type = KEYCLOAK_GRANT_TYPE,
+ },
+ headers = {
+ ["Content-Type"] = "application/x-www-form-urlencoded",
+ }
+ }
+end
+
+-- return 201
+function _keycloak:add_user(client, access_token, body)
+ return client:send {
+ method = "POST",
+ body = body,
+ path = api_uri(self, "/users"),
+ headers = {
+ ["Content-Type"] = "application/json",
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+-- return 204
+function _keycloak:delete_user(client, access_token, user_id)
+ return client:send {
+ method = "DELETE",
+ path = api_uri(self, "/users/%s", user_id),
+ headers = {
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+-- return 200,
+-- return userList
+-- https://www.keycloak.org/docs-api/23.0.4/rest-api/index.html#_users
+function _keycloak:get_users(client, access_token, username)
+ return client:send {
+ method = "GET",
+ path = api_uri(self, "/users?exact=true&max=10&username=%s", username),
+ headers = {
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+-- return 201, no body
+-- https://www.keycloak.org/docs-api/23.0.4/rest-api/index.html#_groups
+function _keycloak:add_group(client, access_token, body)
+ return client:send {
+ method = "POST",
+ path = api_uri(self, "/groups"),
+ body = body,
+ headers = {
+ ["Content-Type"] = "application/json",
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+-- return 204, no body
+function _keycloak:delete_group(client, access_token, group_id)
+ return client:send {
+ method = "DELETE",
+ path = api_uri(self, "/groups/%s", group_id),
+ headers = {
+ ["Content-Type"] = "application/json",
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+-- return 200
+-- return groups
+function _keycloak:get_groups(client, access_token, name)
+ return client:send {
+ method = "GET",
+ path = api_uri(self, "/groups?exact=true&max=10&search=%s", name),
+ headers = {
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+-- return 204, no body
+function _keycloak:add_group_to_user(client, access_token, user_id, group_id)
+ return client:send {
+ method = "PUT",
+ path = api_uri(self, "/users/%s/groups/%s", user_id, group_id),
+ headers = {
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+-- return 204, no body
+function _keycloak:delete_group_from_user(client, access_token, user_id, group_id)
+ return client:send {
+ method = "DELETE",
+ path = api_uri(self, "/users/%s/groups/%s", user_id, group_id),
+ headers = {
+ ["Authorization"] = access_token,
+ }
+ }
+end
+
+local _M = {}
+
+function _M.new(keycloak_config)
+ keycloak_config = keycloak_config or {}
+ local config = {
+ context_path = keycloak_config.context_path or KEYCLOAK_CONTEXT_PATH,
+ realm = keycloak_config.realm or KEYCLOAK_REALM,
+ host_name = keycloak_config.host_name or KEYCLOAK_HOSTNAME,
+ port = keycloak_config.port or KEYCLOAK_PORT,
+ ssl_port = keycloak_config.ssl_port or KEYCLOAK_SSL_PORT,
+ client_id = keycloak_config.client_id or KONG_CLIENT_ID,
+ client_secret = keycloak_config.client_secret or KONG_CLIENT_SECRET,
+ }
+
+ config.realm_path = fmt("%s/realms/%s", config.context_path, config.realm)
+ config.host = config.host_name .. ":" .. config.port
+ config.ssl_host = config.host_name .. ":" .. config.ssl_port
+ config.issuer = fmt("http://%s%s/realms/%s", config.host, config.context_path, config.realm)
+ config.ssl_issuer = fmt("https://%s%s/realms/%s", config.ssl_host, config.context_path, config.realm)
+ config.issuer_discovery = fmt("http://%s%s/realms/%s/.well-known/openid-configuration",
+ config.host,
+ config.context_path,
+ config.realm
+ )
+ config.ssl_issuer_discovery = fmt("https://%s%s/realms/%s/.well-known/openid-configuration",
+ config.ssl_host,
+ config.context_path,
+ config.realm
+ )
+
+ local self = {
+ config = config
+ }
+
+ return setmetatable(self, { __index = _keycloak })
+end
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/crypto_cert.pem b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/crypto_cert.pem
new file mode 100644
index 00000000..f40836e3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/crypto_cert.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmYYPKcMZWEp2RUX0bEwN
+2rLDWHb10cM0+yQRE/O4eyFRrgCxdDnXY0yBcpkMHRxZHNmsktVJ34U8HzScsGRc
+xbEIWGJEhs0Lj6RM0/YxRNwtUu8JwgzhxTpiXN4vya16WmSF4or4C5dvLPHKHzB0
+fNKJWrRtcOsP4ZxWmhj0kjT2YoAQiR+9r5I1HgbPMoJhS/PVevSypYrH83iB1slu
+LegBkrmaif81O9E+sWMQkyyCgYXa1OplIRZdUkyPY4Alvs9WDFgYKJanBXab2Oql
+WjzcfNX9t/hmc4KpFOzL7IkELintw2l8owMUSVxbaTSV1TRejhMX9jxLqPGJyM7U
+DwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/crypto_key.pem b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/crypto_key.pem
new file mode 100644
index 00000000..116c6559
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/crypto_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAmYYPKcMZWEp2RUX0bEwN2rLDWHb10cM0+yQRE/O4eyFRrgCx
+dDnXY0yBcpkMHRxZHNmsktVJ34U8HzScsGRcxbEIWGJEhs0Lj6RM0/YxRNwtUu8J
+wgzhxTpiXN4vya16WmSF4or4C5dvLPHKHzB0fNKJWrRtcOsP4ZxWmhj0kjT2YoAQ
+iR+9r5I1HgbPMoJhS/PVevSypYrH83iB1sluLegBkrmaif81O9E+sWMQkyyCgYXa
+1OplIRZdUkyPY4Alvs9WDFgYKJanBXab2OqlWjzcfNX9t/hmc4KpFOzL7IkELint
+w2l8owMUSVxbaTSV1TRejhMX9jxLqPGJyM7UDwIDAQABAoIBAGYDarf5aDH+6LAH
+V+9OajZUavGxuo50BBSNVrvHS2KmNTxGQaRVewYEpE+d2yrM13oQOlgnWjsosVab
+AzNCvVKv3De+tIJOZcxBBLwj74EuPP/KztUe8ja4IPxMt7xvhcni2gQ7C6/IJJvc
+3s3yyLTYwWSAV+iciaUE2Zbz1i//8etfTARBK49fPdULoFPhwEe2eivJgDj842xn
+s/Wc/rhU/HCjnKVbjZ8IHBbQowsMkJ54xMMmYYCRq+Bsr+mgg0nyeDUHsvlxMm6K
+3dGEFQ5qHPPPzxV/65d3e/u6O+/00lMTY4glugNocutyaOVljc2wpqY8JYow83yZ
+7aim+QECgYEAyM25BFpjoKkEkIXphppRM5odl33GOhcBhQyEHBDkq5yrSHM0LQmg
+Obm2uJIDAyCylVj4RSOmBZkISc3S4pzLjt+FowLYRmyrKH9s1kfu6DMq0+swE2+P
+oGcGC7eO2c3+ho8RCypArDH9/VpaMzWTo90lqu6l9oYs1xAqaxQ46yECgYEAw7lP
+w2KNpnahIRO+cUDrGt/onoZ7vSIg+RtTfLcY9bP+N7bn5eONmAEMC1ZY6rHlh5sb
+Q0Z+Zw/s5RyJ8d7+QRPhBk3bGgilYBj+q77TNWnbG5qsUUqOD5TgA0e+i19diqAf
+K/EqfproaXgo0AAwRPqVIXzwG0NkYRIgb/qniS8CgYByZxKT8lN0gxcAp+qmL2E3
+/wQA/T1kdxr8jGbv/ofpZfcH7eq6BhPtD2uUuEtTCtvzzp6T9X1vDwLz2Xnedepb
+WemvN720UdEp339Ije8FB7HMzt+zEwpHHqsJHekbGgt66iORst7pvAmywDlHl6yn
+3STKv/B6BXOii6k/HGRZQQKBgQCKUjCYuNG6zyPYqIdyqpp9B7jyL8DLTY9X/YPn
+I88z9dy3q0AAK+0pe7PzXza/xrGcQHXn+a3jUFHvKVgj46B0hYQGXId3wpx9aayx
+286QdRswSwN0/+eeA2y9ZLq47NDC456kA3AwwXF2+EB/n2J9nUo/vHhEDECoiZL7
+EH3OaQKBgQCbEpR5Z3jTAF2i8lQY2vSUuzPxy8nIWVtldLoAIQL19GidRRtSkFZa
+Kl09C+BYNqzpNCw0G7VJx9se2u/JkcuZHBOMg/nHtVHXpUETXf/YrAB6pF7bbZzM
+mwtea0ZgW5t/nII9X2VqRtRaW4wy85GqA8YiTtfT5Dku7YSBH+GlnQ==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/key.pem b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/key.pem
new file mode 100644
index 00000000..f881e008
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA2q4Xg7nrWYhwU3xMlIValpB/BkdWEkoOluk1e7U5elXEITkE
+UaXm8BzLU+PU0yHiqWe5s1wiceaEXNvpwgVDhpzisBMutmpoxEnVMNC+n6LS1IIO
+dup6NbhPD/zI+2wJD9YD40kmHEtpoUR1ZrRIitrkP4S+iKamVhKRxAVvZqEfftEp
+aDwN+V9YlXbSGFMPC/Hkjsi3WkynS9BMl0GDH7k7qFr5SDxkCZiL7MvgQrrIrB1m
+YhOF7HNmiWTOlbX7qbitw/H3vvnZzPz4RDloPti51c22dRTredvEdE+PYpwSrzkX
+ESuQwxLJGT9LUENHXJUWtWp6i2uav1KIHbWzUQIDAQABAoIBAGWj9z/9oTs7JGJZ
+asq5dNJ4lOHZNFNJ4qAYAwjdwbpvtLpwYuaEQuOlrofuZFRB8rUkhfasw0HHkAwN
+FwhcI/ekYeTO9a5JpSpe2t1WmNNf/bhSPuDcuTLQ2Maois35d/jJU4NH05PhJVaU
+mDDFaNZJST+QtnKTasqk1OWUVSQQsa6MBb6TYuGJ8sj83eT6S0q+XTPzNFLemhPM
+WGyo/B76ZjLKBv38WqWS+zgQngVBgSm9EFicH0xBJSzEOYU06G2clcyCKYzqB+z4
+8ZPJB848D5kvOiU+IfM6K+GMh9r78I6KPVoC7MrknN7z1Sg5SwXiwQCz34F+3IDu
+HYSW+DECgYEA8hhUwnjY3HFqz4Bs771H/wSmNGDdHNzX8hEeOBhtRpJBuAHcqXW4
+EwZXRFij0MUjdm64jqJAgpKzp4hnErZroJUsmlAC2oFqqPBPicD/dTZFE9xBr3Iv
+ndgpkdockL3GyRl6ju1+b/+AT3LlTeL+jJPROh2UYKpDT85unbiz8A0CgYEA5z15
+2ml7gMIL4Szo0JC19aOXXCbS4mu/tzwJZ/c9pvEJCiqOECbvHJAR/9cWvR9IhSIw
+Cd3BjMoeIUQyMuWqZuRmQxht93AScMApxF0ZkCOhhmo9fvGMEOIUXbfap6XVwOdR
+KRxwHX8Kf1WupPjmeX2Xa9jaKJeXqkEv5ws0O1UCgYEAsArRV7jYuTQgH1Ob45kY
+SXDwCxaEswBEZ1nbR587lx2zfEKeWvunJu5tdt2eAanY574LpmyFzG0xBppBmXHd
+QaA4Ft4ntQx2qvJUZC9bk7gq8w4vFY1K4tTVJaIdM4NMkd9dJ6G7V2XLv/oklEaE
+I2U5t7DavJAS8m2CMl6lOeECgYA/6rM61vtHTNvBwwsbU1SocC6PTz/tTAEzIcor
+zaBh+7AW+kpIVqqzR3NiN07WucXu3tNOdthgFSIc6d/CR89BH5LQqvkCcxz1w3gI
+Cf/d4qtsHwOeih/ALu2YNhCfVGxwJMA/T9gBnzMEnWdnvFDgdgGb0tYCcFyAuAII
+qAUo/QKBgQCXlyLVOeyHobmWb2qaoXqUXbnKMImEz5CHqFNr0zYzG1AgSC1Fa/2W
+FTNqF6lSzCAIyDZ2r8DOqKHjohOYjrMtmFRF0Y8+YFFh40VKwDLRmwLJYmEanZ9r
+5rVqmejnImL2BYTQntzxbWs075wzojWuf3KG2l2TPo076cfrrx70tQ==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/pub.pem b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/pub.pem
new file mode 100644
index 00000000..d24914f5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/keyring/pub.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2q4Xg7nrWYhwU3xMlIVa
+lpB/BkdWEkoOluk1e7U5elXEITkEUaXm8BzLU+PU0yHiqWe5s1wiceaEXNvpwgVD
+hpzisBMutmpoxEnVMNC+n6LS1IIOdup6NbhPD/zI+2wJD9YD40kmHEtpoUR1ZrRI
+itrkP4S+iKamVhKRxAVvZqEfftEpaDwN+V9YlXbSGFMPC/Hkjsi3WkynS9BMl0GD
+H7k7qFr5SDxkCZiL7MvgQrrIrB1mYhOF7HNmiWTOlbX7qbitw/H3vvnZzPz4RDlo
+Pti51c22dRTredvEdE+PYpwSrzkXESuQwxLJGT9LUENHXJUWtWp6i2uav1KIHbWz
+UQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_distributions_constants.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_distributions_constants.lua
new file mode 100644
index 00000000..487eca15
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_distributions_constants.lua
@@ -0,0 +1,84 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local c = {}
+
+c.plugins = {
+ -- HACK: when adding correlation-id plugin through the admin api
+ -- restrict it as an enterprise plugin
+ "correlation-id",
+ "kafka-upstream",
+}
+
+c.featureset = {
+ full = {
+ conf = {},
+ },
+ full_expired = {
+ conf = {},
+ allow_admin_api = {
+ ["/licenses"] = { ["*"] = true },
+ ["/licenses/:licenses"] = { ["*"] = true },
+ },
+ allow_ee_entity = { READ = true, WRITE = false },
+ disabled_ee_entities = {
+ ["workspaces"] = true,
+ ["event_hooks"] = true,
+ ["consumer_groups"] = true,
+ ["consumer_group_plugins"] = true,
+ ["rbac_role_endpoints"] = true,
+ ["rbac_role_entities"] = true,
+ ["rbac_roles"] = true,
+ ["rbac_user_roles"] = true,
+ ["rbac_users"] = true,
+ },
+ },
+ free = {
+ conf = {
+ enforce_rbac = "off",
+ -- XXX need to keep this alias to enforce_rbac
+ rbac = "off",
+ vitals = false,
+ anonymous_reports = true,
+ portal = false,
+ fips = false,
+ event_hooks_enabled = false,
+ -- NOOP (unset it)
+ admin_gui_auth = function() end,
+ },
+ allow_admin_api = {
+ -- Allow these granularly
+ ["/workspaces"] = { GET = true, OPTIONS = true },
+ ["/workspaces/:workspaces"] = { GET = true, OPTIONS = true },
+ },
+ deny_admin_api = {
+ -- Deny any other
+ ["/workspaces"] = { ["*"] = true },
+ ["/workspaces/:workspaces"] = { ["*"] = true },
+ },
+ -- deny a particular entity (and related api methods)
+ -- deny_entity = { ["some_entity_name"] = true },
+ -- disable running of enterprise plugins
+ allow_ee_entity = { READ = false, WRITE = false },
+ disabled_ee_entities = {
+ ["workspaces"] = false,
+ ["event_hooks"] = true,
+ ["consumer_groups"] = true,
+ ["consumer_group_plugins"] = true,
+ ["rbac_role_endpoints"] = true,
+ ["rbac_role_entities"] = true,
+ ["rbac_roles"] = true,
+ ["rbac_user_roles"] = true,
+ ["rbac_users"] = true,
+ },
+ }
+}
+
+-- This is a flag is being used to indicate a generated release
+c.release = false
+
+return setmetatable(c, {__index = function() return {} end })
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_expired_license.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_expired_license.json
new file mode 100644
index 00000000..739d437d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_expired_license.json
@@ -0,0 +1,16 @@
+{
+ "license": {
+ "payload": {
+ "admin_seats": "5",
+ "customer": "automation",
+ "dataplanes": "100",
+ "license_creation_date": "2022-03-10",
+ "license_expiration_date": "2022-04-20",
+ "license_key": "ASDASDASDASDASDASDASDASDASD_a1VASASD",
+ "product_subscription": "Konnect Enterprise",
+ "support_plan": "None"
+ },
+ "signature": "9b4a2bcc3046a8c8078971001be0df01a3f9b875c78376fdcb58962b46dfd251e322d5de8c02b670beb3fde1da03a35606f109f8496a6e03294650ba20dc6bee",
+ "version": "1"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_feature_flags.conf b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_feature_flags.conf
new file mode 100644
index 00000000..dacc0a93
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_feature_flags.conf
@@ -0,0 +1,4 @@
+foo=on
+bar_value=42
+baz = off
+multi = kong,strong
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_grace_period_license_tmpl.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_grace_period_license_tmpl.json
new file mode 100644
index 00000000..3e1f6e6b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_grace_period_license_tmpl.json
@@ -0,0 +1,16 @@
+{
+ "license": {
+ "version": 1,
+ "payload": {
+ "customer": "ee-build-test",
+ "product_subscription": "Kong Only",
+ "support_plan": "None",
+ "admin_seats": "1",
+ "dataplanes": "1",
+ "license_creation_date": "2020-9-23",
+ "license_expiration_date": "%s",
+ "license_key": "ASDASDASDASDASDASDASDASDASD_a1VASASD"
+ },
+ "signature": "DUMMY"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_license.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_license.json
new file mode 100644
index 00000000..9b165065
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_license.json
@@ -0,0 +1,14 @@
+{
+ "license": {
+ "payload": {
+ "customer": "",
+ "product_subscription": "Enterprise",
+ "support_plan": "Silver",
+ "admin_seats": "5",
+ "license_creation_date": "2018-01-01",
+ "license_expiration_date": "2099-12-31",
+ "license_key": "0014100000LyLlf_00641000008di8T"
+ },
+ "signature": "LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpvd0did012TXdDVjJyL3J6aHlkL2I4OWdQTzJXeEJDNXc3Zk15TlFpMFRnbEtkWFFJTm5Dd3NMY3dNdzh6Y0xTCnhNelV4TUxjMGpMWjBDakpNdEU0emRna05jblEwdExJd2lBcE9TMHQwU3paMk5BbzBUVEoxQ3pOcktPVWhVR00KaTBGV1RKRkZhSjd2SWUrNVA0cDIzRzZUZ05uRHlnU3loSUdMVXdBbXNzYVo0Wi9HalhZRmhXTEhlNnZhOXlabgp1OWJVeVRaT3kxOGI3WDFyMXFrVlltRzdObnN3L05OZjBLK2xYc3BaYXhjNldhSi9hdUw1aElaTEowdVk1dDZkCmI3cW9VUEQrVm5ZQQo9ZnV1cgotLS0tLUVORCBQR1AgTUVTU0FHRS0tLS0tCg=="
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_portal_and_vitals_key.txt b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_portal_and_vitals_key.txt
new file mode 100644
index 00000000..d2d6d7d6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_portal_and_vitals_key.txt
@@ -0,0 +1 @@
+753252c37f163b4bb601f84f25f0ab7609878673019082d50776196b97536880
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_s3.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_s3.lua
new file mode 100644
index 00000000..72cdb390
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mock_s3.lua
@@ -0,0 +1,77 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local query = ngx.req.get_uri_args()
+local headers = ngx.req.get_headers()
+local full_path = ngx.var.uri
+local bucket, path = full_path:match("^/([^/]+)/?(.*)")
+if ngx.req.get_method() == "PUT" then
+ ngx.shared.objects:set(full_path, body or "")
+
+ local date = headers["X-Amz-Date"] or "20231221T000000Z"
+ local etag = headers["ETag"] or headers["X-Amz-Content-Sha256"] or "d41d8cd98f00b204e9800998ecf8427e"
+ local content_type = headers["Content-Type"] or "application/octet-stream"
+
+ ngx.shared.metadata:set(full_path, table.concat(
+ {date, etag, content_type}, "\n"
+ ))
+
+ return ngx.exit(200)
+elseif ngx.req.get_method() == "GET" then
+ local prefix = query["prefix"]
+ local list_type = query["list-type"]
+ if not path or path == "" then
+ if list_type == "2" then
+ local contents = {}
+
+ local metadata = ngx.shared.metadata:get_keys()
+
+ for _, fpath in ipairs(metadata) do
+ local meta = ngx.shared.metadata:get(fpath)
+ local date, etag, content_type = meta:match("^(.-)\n(.-)\n(.-)$")
+ local key = fpath:sub(#bucket + 3)
+ if key:sub(1, #prefix) == prefix then
+ table.insert(contents, string.format(
+ [[
+ ]] .. key .. [[
+ ]] .. date .. [[
+ "]] .. etag .. [["
+ 0
+ STANDARD
+ ]]
+ ))
+ end
+ end
+
+ local body = [[
+
+ ]] .. bucket .. [[
+ ]] .. prefix .. [[
+ false
+ ]] .. table.concat(contents) .. [[
+
+ ]]
+ ngx.header["Content-Type"] = "application/xml"
+ ngx.header["Content-Length"] = #body
+ ngx.print(body)
+ return ngx.exit(200)
+ end
+ else
+ local body = ngx.shared.objects:get(full_path)
+ if not body then
+ return ngx.exit(404)
+ end
+ ngx.header["Content-Length"] = #body
+ ngx.print(body)
+ local meta = ngx.shared.metadata:get(full_path)
+ local date, etag, content_type = meta:match("^(.-)\n(.-)\n(.-)$")
+ ngx.header["Last-Modified"] = date
+ ngx.header["ETag"] = etag
+ ngx.header["Content-Type"] = content_type
+ return ngx.exit(200)
+ end
+end
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mtls.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mtls.lua
new file mode 100644
index 00000000..48a78be4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/mtls.lua
@@ -0,0 +1,84 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- hash == hh_XBSxIT3qG46n5igJA0MsFEgXosYoWvzeRZfRCknY
+local CLIENT_CERT = [[
+-----BEGIN CERTIFICATE-----
+MIICvjCCAaYCFHYzBjKWczsVgX/S8QMBtuG63E7/MA0GCSqGSIb3DQEBCwUAMCYx
+JDAiBgNVBAMMG2ludGVybWVkaWF0ZS55b3VyZG9tYWluLmNvbTAgFw0yMzA5Mjcw
+ODQ2MDNaGA8yMTIzMDkwMzA4NDYwM1owDzENMAsGA1UEAwwEa29uZzCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALkDb0lH8uUUfiiFF24mO7Wg7oAWULOt
+HnoK/WIesO1qzfPZrEGfUghPZKhfYfJBAjhzAEr0TkXxXJIk7p1v3GScJUpvSRtU
+8kCKqp+HuF1psSuULyuYnTDI5wuXOBKOss2RU3xWdFz2Mug6LbsZ0g5AYxk88saD
+tM0OhV02F4kRipLtnKst5NR17SeJtdskvgyV1BCsXvveCs0t5I1fykyvwbpNhzv6
+V1UPHDq506H9VaT0SZ6mJu202KNeStibm13cmXlVMYP5V3raN1f4ZlKAuf6cp7Pv
+bzP5K8dOjc9fTOf3m2ryjlGL1SsoK4Z2qBjm8a7m61Z0l6qy/w4RqicCAwEAATAN
+BgkqhkiG9w0BAQsFAAOCAQEADAZlkXFXaSj3NK9MGtRlP6la05/sVGGbEHG3JeYd
+d2TjQDVJCgY3eceP2fxpwKxzQdOzPd4EfbhDbpOYqV5+cZdlZBYX/Yz0lBOknTTS
+5ywSPq/rnU6FSGdnHxY+Edkz+oKiu3ADOMdatZ1pPyFslGTsY48/bj5/+jlnTx2u
+yfuX1qRM2B/YFv1P4NkTjEgjbFM7J313RBGkVws+TN6HwEQS8a6FpNt1JdHrhj8O
++jqcl6m9M4/KysxCladGVo6WTMVr3Yq2m+J3rYkaJkK5w16gMaYvx2iDVr/aaAUB
+EjkqX1fpGhhkqsLSggd1CZsLyOAlQ6b/q+JBeeyY0SD08w==
+-----END CERTIFICATE-----
+]]
+
+-- "x5t#S256": "hh_XBSxIT3qG46n5igJA0MsFEgXosYoWvzeRZfRCknY"
+local CERT_ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI" ..
+"xMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJjbmYiOnsie" ..
+"DV0I1MyNTYiOiJoaF9YQlN4SVQzcUc0Nm41aWdKQTBNc0ZFZ1hvc1lvV3Z6ZVJaZlJDa25ZIn1" ..
+"9.huU_oO4QCv13bVnV31L7P1bz60wVOiPsMn_e8KSN7S0"
+
+-- "x5t#S256": "hh_XBSxIT3qG46n5igJA0MsFEgXosYoWvzeRZfRCknZ"
+local WRONG_CERT_ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd" ..
+"WIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJjbmY" ..
+"iOnsieDV0I1MyNTYiOiJoaF9YQlN4SVQzcUc0Nm41aWdKQTBNc0ZFZ1hvc1lvV3Z6ZVJaZlJDa" ..
+"25aIn19.NhJmrrhQyXxzQ0hGxwxjLgXpKbPx1oTjJwmlRcsX7KE"
+
+local NO_CERT_ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIi" ..
+"OiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwR" ..
+"JSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
+
+local CERT_INTROSPECTION_DATA = {
+ active = true,
+ aud = { "kong" },
+ client_id = "kong",
+ cnf = {
+ ["x5t#S256"] = "hh_XBSxIT3qG46n5igJA0MsFEgXosYoWvzeRZfRCknY",
+ },
+ sub = "kong",
+ token_type = "access_token",
+}
+
+local WRONG_CERT_INTROSPECTION_DATA = {
+ active = true,
+ aud = { "kong" },
+ client_id = "kong",
+ cnf = {
+ ["x5t#S256"] = "hh_XBSxIT3qG46n5igJA0MsFEgXosYoWvzeRZfRCknZ",
+ },
+ sub = "kong",
+ token_type = "access_token",
+}
+
+local NO_CERT_INTROSPECTION_DATA = {
+ active = true,
+ aud = { "kong" },
+ client_id = "kong",
+ sub = "kong",
+ token_type = "access_token",
+}
+
+
+return {
+ CLIENT_CERT = CLIENT_CERT,
+ CERT_ACCESS_TOKEN = CERT_ACCESS_TOKEN,
+ WRONG_CERT_ACCESS_TOKEN = WRONG_CERT_ACCESS_TOKEN,
+ NO_CERT_ACCESS_TOKEN = NO_CERT_ACCESS_TOKEN,
+ CERT_INTROSPECTION_DATA = CERT_INTROSPECTION_DATA,
+ WRONG_CERT_INTROSPECTION_DATA = WRONG_CERT_INTROSPECTION_DATA,
+ NO_CERT_INTROSPECTION_DATA = NO_CERT_INTROSPECTION_DATA,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_host_v2.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_host_v2.json
new file mode 100644
index 00000000..422d10d9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_host_v2.json
@@ -0,0 +1,153 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore3",
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "basePath": "/v3",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/pets": {
+ "get": {
+ "summary": "List all pets",
+ "operationId": "listPets",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "How many items to return at one time (max 100)",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "An paged array of pets",
+ "headers": {
+ "x-next": {
+ "type": "string",
+ "description": "A link to the next page of responses"
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a pet",
+ "operationId": "createPets",
+ "tags": [
+ "pets"
+ ],
+ "responses": {
+ "201": {
+ "description": "Null response"
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/pets/{petId}": {
+ "get": {
+ "summary": "Info for a specific pet",
+ "operationId": "showPetById",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "required": true,
+ "description": "The id of the pet to retrieve",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Expected response to a valid request",
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "Pet": {
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "Pets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "Error": {
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_host_v2.yaml b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_host_v2.yaml
new file mode 100644
index 00000000..f24edd9a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_host_v2.yaml
@@ -0,0 +1,699 @@
+openapi: '2.0'
+info:
+ description: 'This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.'
+ version: 1.0.0
+ title: Swagger Petstore
+ termsOfService: 'http://swagger.io/terms/'
+ contact:
+ email: apiteam@swagger.io
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+basePath: /v2
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: 'http://swagger.io'
+ - name: store
+ description: Access to Petstore orders
+ - name: user
+ description: Operations about user
+ externalDocs:
+ description: Find out more about our store
+ url: 'http://swagger.io'
+schemes:
+ - https
+ - http
+paths:
+ /pet:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: ''
+ operationId: addPet
+ consumes:
+ - application/json
+ - application/xml
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: Pet object that needs to be added to the store
+ required: true
+ schema:
+ $ref: '#/definitions/Pet'
+ responses:
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ description: ''
+ operationId: updatePet
+ consumes:
+ - application/json
+ - application/xml
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: Pet object that needs to be added to the store
+ required: true
+ schema:
+ $ref: '#/definitions/Pet'
+ responses:
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ '405':
+ description: Validation exception
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ /pet/findByStatus:
+ get:
+ tags:
+ - pet
+ summary: Finds Pets by status
+ description: Multiple status values can be provided with comma separated strings
+ operationId: findPetsByStatus
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: status
+ in: query
+ description: Status values that need to be considered for filter
+ required: true
+ type: array
+ items:
+ type: string
+ enum:
+ - available
+ - pending
+ - sold
+ default: available
+ collectionFormat: multi
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/Pet'
+ '400':
+ description: Invalid status value
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ /pet/findByTags:
+ get:
+ tags:
+ - pet
+ summary: Finds Pets by tags
+ description: 'Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.'
+ operationId: findPetsByTags
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: tags
+ in: query
+ description: Tags to filter by
+ required: true
+ type: array
+ items:
+ type: string
+ collectionFormat: multi
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/Pet'
+ '400':
+ description: Invalid tag value
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ deprecated: true
+ '/pet/{petId}':
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to return
+ required: true
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/Pet'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ security:
+ - api_key: []
+ post:
+ tags:
+ - pet
+ summary: Updates a pet in the store with form data
+ description: ''
+ operationId: updatePetWithForm
+ consumes:
+ - application/x-www-form-urlencoded
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet that needs to be updated
+ required: true
+ type: integer
+ format: int64
+ - name: name
+ in: formData
+ description: Updated name of the pet
+ required: false
+ type: string
+ - name: status
+ in: formData
+ description: Updated status of the pet
+ required: false
+ type: string
+ responses:
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ delete:
+ tags:
+ - pet
+ summary: Deletes a pet
+ description: ''
+ operationId: deletePet
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: api_key
+ in: header
+ required: false
+ type: string
+ - name: petId
+ in: path
+ description: Pet id to delete
+ required: true
+ type: integer
+ format: int64
+ responses:
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ '/pet/{petId}/uploadImage':
+ post:
+ tags:
+ - pet
+ summary: uploads an image
+ description: ''
+ operationId: uploadFile
+ consumes:
+ - multipart/form-data
+ produces:
+ - application/json
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to update
+ required: true
+ type: integer
+ format: int64
+ - name: additionalMetadata
+ in: formData
+ description: Additional data to pass to server
+ required: false
+ type: string
+ - name: file
+ in: formData
+ description: file to upload
+ required: false
+ type: file
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/ApiResponse'
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ /store/inventory:
+ get:
+ tags:
+ - store
+ summary: Returns pet inventories by status
+ description: Returns a map of status codes to quantities
+ operationId: getInventory
+ produces:
+ - application/json
+ parameters: []
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ type: object
+ additionalProperties:
+ type: integer
+ format: int32
+ security:
+ - api_key: []
+ /store/order:
+ post:
+ tags:
+ - store
+ summary: Place an order for a pet
+ description: ''
+ operationId: placeOrder
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: order placed for purchasing the pet
+ required: true
+ schema:
+ $ref: '#/definitions/Order'
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/Order'
+ '400':
+ description: Invalid Order
+ '/store/order/{orderId}':
+ get:
+ tags:
+ - store
+ summary: Find purchase order by ID
+ description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions
+ operationId: getOrderById
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: orderId
+ in: path
+ description: ID of pet that needs to be fetched
+ required: true
+ type: integer
+ maximum: 10
+ minimum: 1
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/Order'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Order not found
+ delete:
+ tags:
+ - store
+ summary: Delete purchase order by ID
+ description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors
+ operationId: deleteOrder
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: orderId
+ in: path
+ description: ID of the order that needs to be deleted
+ required: true
+ type: integer
+ minimum: 1
+ format: int64
+ responses:
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Order not found
+ /user:
+ post:
+ tags:
+ - user
+ summary: Create user
+ description: This can only be done by the logged in user.
+ operationId: createUser
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: Created user object
+ required: true
+ schema:
+ $ref: '#/definitions/User'
+ responses:
+ default:
+ description: successful operation
+ /user/createWithArray:
+ post:
+ tags:
+ - user
+ summary: Creates list of users with given input array
+ description: ''
+ operationId: createUsersWithArrayInput
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: List of user object
+ required: true
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/User'
+ responses:
+ default:
+ description: successful operation
+ /user/createWithList:
+ post:
+ tags:
+ - user
+ summary: Creates list of users with given input array
+ description: ''
+ operationId: createUsersWithListInput
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: List of user object
+ required: true
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/User'
+ responses:
+ default:
+ description: successful operation
+ /user/login:
+ get:
+ tags:
+ - user
+ summary: Logs user into the system
+ description: ''
+ operationId: loginUser
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: username
+ in: query
+ description: The user name for login
+ required: true
+ type: string
+ - name: password
+ in: query
+ description: The password for login in clear text
+ required: true
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ type: string
+ headers:
+ X-Rate-Limit:
+ type: integer
+ format: int32
+ description: calls per hour allowed by the user
+ X-Expires-After:
+ type: string
+ format: date-time
+ description: date in UTC when token expires
+ '400':
+ description: Invalid username/password supplied
+ /user/logout:
+ get:
+ tags:
+ - user
+ summary: Logs out current logged in user session
+ description: ''
+ operationId: logoutUser
+ produces:
+ - application/xml
+ - application/json
+ parameters: []
+ responses:
+ default:
+ description: successful operation
+ '/user/{username}':
+ get:
+ tags:
+ - user
+ summary: Get user by user name
+ description: ''
+ operationId: getUserByName
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: username
+ in: path
+ description: 'The name that needs to be fetched. Use user1 for testing. '
+ required: true
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/User'
+ '400':
+ description: Invalid username supplied
+ '404':
+ description: User not found
+ put:
+ tags:
+ - user
+ summary: Updated user
+ description: This can only be done by the logged in user.
+ operationId: updateUser
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: username
+ in: path
+ description: name that need to be updated
+ required: true
+ type: string
+ - in: body
+ name: body
+ description: Updated user object
+ required: true
+ schema:
+ $ref: '#/definitions/User'
+ responses:
+ '400':
+ description: Invalid user supplied
+ '404':
+ description: User not found
+ delete:
+ tags:
+ - user
+ summary: Delete user
+ description: This can only be done by the logged in user.
+ operationId: deleteUser
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: username
+ in: path
+ description: The name that needs to be deleted
+ required: true
+ type: string
+ responses:
+ '400':
+ description: Invalid username supplied
+ '404':
+ description: User not found
+securityDefinitions:
+ petstore_auth:
+ type: oauth2
+ authorizationUrl: 'https://petstore.swagger.io/oauth/dialog'
+ flow: implicit
+ scopes:
+ 'write:pets': modify pets in your account
+ 'read:pets': read your pets
+ api_key:
+ type: apiKey
+ name: api_key
+ in: header
+definitions:
+ Order:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ petId:
+ type: integer
+ format: int64
+ quantity:
+ type: integer
+ format: int32
+ shipDate:
+ type: string
+ format: date-time
+ status:
+ type: string
+ description: Order Status
+ enum:
+ - placed
+ - approved
+ - delivered
+ complete:
+ type: boolean
+ default: false
+ xml:
+ name: Order
+ User:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ username:
+ type: string
+ firstName:
+ type: string
+ lastName:
+ type: string
+ email:
+ type: string
+ password:
+ type: string
+ phone:
+ type: string
+ userStatus:
+ type: integer
+ format: int32
+ description: User Status
+ xml:
+ name: User
+ Category:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Category
+ Tag:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Tag
+ Pet:
+ type: object
+ required:
+ - name
+ - photoUrls
+ properties:
+ id:
+ type: integer
+ format: int64
+ category:
+ $ref: '#/definitions/Category'
+ name:
+ type: string
+ example: doggie
+ photoUrls:
+ type: array
+ xml:
+ name: photoUrl
+ wrapped: true
+ items:
+ type: string
+ tags:
+ type: array
+ xml:
+ name: tag
+ wrapped: true
+ items:
+ $ref: '#/definitions/Tag'
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ xml:
+ name: Pet
+ ApiResponse:
+ type: object
+ properties:
+ code:
+ type: integer
+ format: int32
+ type:
+ type: string
+ message:
+ type: string
+externalDocs:
+ description: Find out more about Swagger
+ url: 'http://swagger.io'
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_servers_v3.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_servers_v3.json
new file mode 100644
index 00000000..a961305b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_servers_v3.json
@@ -0,0 +1,148 @@
+{
+ "openapi": "3.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore3",
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/pets": {
+ "get": {
+ "summary": "List all pets",
+ "operationId": "listPets",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "How many items to return at one time (max 100)",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "An paged array of pets",
+ "headers": {
+ "x-next": {
+ "type": "string",
+ "description": "A link to the next page of responses"
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a pet",
+ "operationId": "createPets",
+ "tags": [
+ "pets"
+ ],
+ "responses": {
+ "201": {
+ "description": "Null response"
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/pets/{petId}": {
+ "get": {
+ "summary": "Info for a specific pet",
+ "operationId": "showPetById",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "required": true,
+ "description": "The id of the pet to retrieve",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Expected response to a valid request",
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "Pet": {
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "Pets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "Error": {
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_servers_v3.yaml b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_servers_v3.yaml
new file mode 100644
index 00000000..6a08cc0d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/missing_servers_v3.yaml
@@ -0,0 +1,107 @@
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: Swagger Petstore
+ license:
+ name: MIT
+paths:
+ /pets:
+ get:
+ summary: List all pets
+ operationId: listPets
+ tags:
+ - pets
+ parameters:
+ - name: limit
+ in: query
+ description: How many items to return at one time (max 100)
+ required: false
+ schema:
+ type: integer
+ format: int32
+ responses:
+ '200':
+ description: A paged array of pets
+ headers:
+ x-next:
+ description: A link to the next page of responses
+ schema:
+ type: string
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pets"
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ post:
+ summary: Create a pet
+ operationId: createPets
+ tags:
+ - pets
+ responses:
+ '201':
+ description: Null response
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ /pets/{petId}:
+ get:
+ summary: Info for a specific pet
+ operationId: showPetById
+ tags:
+ - pets
+ parameters:
+ - name: petId
+ in: path
+ required: true
+ description: The id of the pet to retrieve
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Expected response to a valid request
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pets"
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+components:
+ schemas:
+ Pet:
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ tag:
+ type: string
+ Pets:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pet"
+ Error:
+ required:
+ - code
+ - message
+ properties:
+ code:
+ type: integer
+ format: int32
+ message:
+ type: string
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2.json
new file mode 100644
index 00000000..1703cd7e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2.json
@@ -0,0 +1,154 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "host": "petstore.swagger.io:9999",
+ "basePath": "/yeeee",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/pets": {
+ "get": {
+ "summary": "List all pets",
+ "operationId": "listPets",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "How many items to return at one time (max 100)",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "An paged array of pets",
+ "headers": {
+ "x-next": {
+ "type": "string",
+ "description": "A link to the next page of responses"
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a pet",
+ "operationId": "createPets",
+ "tags": [
+ "pets"
+ ],
+ "responses": {
+ "201": {
+ "description": "Null response"
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/pets/{petId}": {
+ "get": {
+ "summary": "Info for a specific pet",
+ "operationId": "showPetById",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "required": true,
+ "description": "The id of the pet to retrieve",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Expected response to a valid request",
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "Pet": {
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "Pets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "Error": {
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2.yaml b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2.yaml
new file mode 100644
index 00000000..5a6628ce
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2.yaml
@@ -0,0 +1,228 @@
+openapi: '2.0'
+info:
+ description: 'This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.'
+ version: 1.0.0
+ title: Swagger Petstore
+ termsOfService: 'http://swagger.io/terms/'
+ contact:
+ email: apiteam@swagger.io
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+host: petstore.swagger.io:9999
+basePath: /yeeee
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: 'http://swagger.io'
+ - name: store
+ description: Access to Petstore orders
+ - name: user
+ description: Operations about user
+ externalDocs:
+ description: Find out more about our store
+ url: 'http://swagger.io'
+schemes:
+ - https
+ - http
+paths:
+ /pets:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: ''
+ operationId: addPet
+ consumes:
+ - application/json
+ - application/xml
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: Pet object that needs to be added to the store
+ required: true
+ schema:
+ $ref: '#/definitions/Pet'
+ responses:
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ get:
+ tags:
+ - pet
+ summary: List all pets
+ description: ''
+ operationId: listPets
+ produces:
+ - application/xml
+ - application/json
+ '/pets/{petId}':
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to return
+ required: true
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/Pet'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ security:
+ - api_key: []
+securityDefinitions:
+ petstore_auth:
+ type: oauth2
+ authorizationUrl: 'https://petstore.swagger.io/oauth/dialog'
+ flow: implicit
+ scopes:
+ 'write:pets': modify pets in your account
+ 'read:pets': read your pets
+ api_key:
+ type: apiKey
+ name: api_key
+ in: header
+definitions:
+ Order:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ petId:
+ type: integer
+ format: int64
+ quantity:
+ type: integer
+ format: int32
+ shipDate:
+ type: string
+ format: date-time
+ status:
+ type: string
+ description: Order Status
+ enum:
+ - placed
+ - approved
+ - delivered
+ complete:
+ type: boolean
+ default: false
+ xml:
+ name: Order
+ User:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ username:
+ type: string
+ firstName:
+ type: string
+ lastName:
+ type: string
+ email:
+ type: string
+ password:
+ type: string
+ phone:
+ type: string
+ userStatus:
+ type: integer
+ format: int32
+ description: User Status
+ xml:
+ name: User
+ Category:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Category
+ Tag:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Tag
+ Pet:
+ type: object
+ required:
+ - name
+ - photoUrls
+ properties:
+ id:
+ type: integer
+ format: int64
+ category:
+ $ref: '#/definitions/Category'
+ name:
+ type: string
+ example: doggie
+ photoUrls:
+ type: array
+ xml:
+ name: photoUrl
+ wrapped: true
+ items:
+ type: string
+ tags:
+ type: array
+ xml:
+ name: tag
+ wrapped: true
+ items:
+ $ref: '#/definitions/Tag'
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ xml:
+ name: Pet
+ ApiResponse:
+ type: object
+ properties:
+ code:
+ type: integer
+ format: int32
+ type:
+ type: string
+ message:
+ type: string
+externalDocs:
+ description: Find out more about Swagger
+ url: 'http://swagger.io'
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2_service_patch.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2_service_patch.json
new file mode 100644
index 00000000..4763a6ad
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2_service_patch.json
@@ -0,0 +1,154 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "host": "new.swagger.io:8000",
+ "basePath": "/wooo",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/pets": {
+ "get": {
+ "summary": "List all pets",
+ "operationId": "listPets",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "How many items to return at one time (max 100)",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "An paged array of pets",
+ "headers": {
+ "x-next": {
+ "type": "string",
+ "description": "A link to the next page of responses"
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a pet",
+ "operationId": "createPets",
+ "tags": [
+ "pets"
+ ],
+ "responses": {
+ "201": {
+ "description": "Null response"
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/pets/{petId}": {
+ "get": {
+ "summary": "Info for a specific pet",
+ "operationId": "showPetById",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "required": true,
+ "description": "The id of the pet to retrieve",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Expected response to a valid request",
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "Pet": {
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "Pets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "Error": {
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2_service_patch.yaml b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2_service_patch.yaml
new file mode 100644
index 00000000..7d9f2619
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v2_service_patch.yaml
@@ -0,0 +1,228 @@
+openapi: '2.0'
+info:
+ description: 'This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.'
+ version: 1.0.0
+ title: Swagger Petstore
+ termsOfService: 'http://swagger.io/terms/'
+ contact:
+ email: apiteam@swagger.io
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+host: new.swagger.io:8000
+basePath: /wooo
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: 'http://swagger.io'
+ - name: store
+ description: Access to Petstore orders
+ - name: user
+ description: Operations about user
+ externalDocs:
+ description: Find out more about our store
+ url: 'http://swagger.io'
+schemes:
+ - https
+ - http
+paths:
+ /pets:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: ''
+ operationId: addPet
+ consumes:
+ - application/json
+ - application/xml
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: Pet object that needs to be added to the store
+ required: true
+ schema:
+ $ref: '#/definitions/Pet'
+ responses:
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ get:
+ tags:
+ - pet
+ summary: List all pets
+ description: ''
+ operationId: listPets
+ produces:
+ - application/xml
+ - application/json
+ '/pets/{petId}':
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ produces:
+ - application/xml
+ - application/json
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to return
+ required: true
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/Pet'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ security:
+ - api_key: []
+securityDefinitions:
+ petstore_auth:
+ type: oauth2
+ authorizationUrl: 'https://petstore.swagger.io/oauth/dialog'
+ flow: implicit
+ scopes:
+ 'write:pets': modify pets in your account
+ 'read:pets': read your pets
+ api_key:
+ type: apiKey
+ name: api_key
+ in: header
+definitions:
+ Order:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ petId:
+ type: integer
+ format: int64
+ quantity:
+ type: integer
+ format: int32
+ shipDate:
+ type: string
+ format: date-time
+ status:
+ type: string
+ description: Order Status
+ enum:
+ - placed
+ - approved
+ - delivered
+ complete:
+ type: boolean
+ default: false
+ xml:
+ name: Order
+ User:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ username:
+ type: string
+ firstName:
+ type: string
+ lastName:
+ type: string
+ email:
+ type: string
+ password:
+ type: string
+ phone:
+ type: string
+ userStatus:
+ type: integer
+ format: int32
+ description: User Status
+ xml:
+ name: User
+ Category:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Category
+ Tag:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Tag
+ Pet:
+ type: object
+ required:
+ - name
+ - photoUrls
+ properties:
+ id:
+ type: integer
+ format: int64
+ category:
+ $ref: '#/definitions/Category'
+ name:
+ type: string
+ example: doggie
+ photoUrls:
+ type: array
+ xml:
+ name: photoUrl
+ wrapped: true
+ items:
+ type: string
+ tags:
+ type: array
+ xml:
+ name: tag
+ wrapped: true
+ items:
+ $ref: '#/definitions/Tag'
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ xml:
+ name: Pet
+ ApiResponse:
+ type: object
+ properties:
+ code:
+ type: integer
+ format: int32
+ type:
+ type: string
+ message:
+ type: string
+externalDocs:
+ description: Find out more about Swagger
+ url: 'http://swagger.io'
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3.json
new file mode 100644
index 00000000..7f03c238
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3.json
@@ -0,0 +1,156 @@
+{
+ "openapi": "3.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "http://petstore.swagger.io:9999/yeeee"
+ },
+ {
+ "url": "https://petstore.swagger.io:9999/yeeee"
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/pets": {
+ "get": {
+ "summary": "List all pets",
+ "operationId": "listPets",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "How many items to return at one time (max 100)",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "An paged array of pets",
+ "headers": {
+ "x-next": {
+ "type": "string",
+ "description": "A link to the next page of responses"
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a pet",
+ "operationId": "createPets",
+ "tags": [
+ "pets"
+ ],
+ "responses": {
+ "201": {
+ "description": "Null response"
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/pets/{petId}": {
+ "get": {
+ "summary": "Info for a specific pet",
+ "operationId": "showPetById",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "required": true,
+ "description": "The id of the pet to retrieve",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Expected response to a valid request",
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "Pet": {
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "Pets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "Error": {
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3.yaml b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3.yaml
new file mode 100644
index 00000000..1c6fa278
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3.yaml
@@ -0,0 +1,110 @@
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: Swagger Petstore
+ license:
+ name: MIT
+servers:
+ - url: http://petstore.swagger.io:9999/yeeee
+ - url: https://petstore.swagger.io:9999/yeeee
+paths:
+ /pets:
+ get:
+ summary: List all pets
+ operationId: listPets
+ tags:
+ - pets
+ parameters:
+ - name: limit
+ in: query
+ description: How many items to return at one time (max 100)
+ required: false
+ schema:
+ type: integer
+ format: int32
+ responses:
+ '200':
+ description: A paged array of pets
+ headers:
+ x-next:
+ description: A link to the next page of responses
+ schema:
+ type: string
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pets"
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ post:
+ summary: Create a pet
+ operationId: createPets
+ tags:
+ - pets
+ responses:
+ '201':
+ description: Null response
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ /pets/{petId}:
+ get:
+ summary: Info for a specific pet
+ operationId: showPetById
+ tags:
+ - pets
+ parameters:
+ - name: petId
+ in: path
+ required: true
+ description: The id of the pet to retrieve
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Expected response to a valid request
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pets"
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+components:
+ schemas:
+ Pet:
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ tag:
+ type: string
+ Pets:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pet"
+ Error:
+ required:
+ - code
+ - message
+ properties:
+ code:
+ type: integer
+ format: int32
+ message:
+ type: string
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3_service_patch.json b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3_service_patch.json
new file mode 100644
index 00000000..139e9c16
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3_service_patch.json
@@ -0,0 +1,156 @@
+{
+ "openapi": "3.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "http://new.swagger.io:8000/wooo"
+ },
+ {
+ "url": "https://new.swagger.io:8000/wooo"
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/pets": {
+ "get": {
+ "summary": "List all pets",
+ "operationId": "listPets",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "How many items to return at one time (max 100)",
+ "required": false,
+ "type": "integer",
+ "format": "int32"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "An paged array of pets",
+ "headers": {
+ "x-next": {
+ "type": "string",
+ "description": "A link to the next page of responses"
+ }
+ },
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a pet",
+ "operationId": "createPets",
+ "tags": [
+ "pets"
+ ],
+ "responses": {
+ "201": {
+ "description": "Null response"
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/pets/{petId}": {
+ "get": {
+ "summary": "Info for a specific pet",
+ "operationId": "showPetById",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "required": true,
+ "description": "The id of the pet to retrieve",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Expected response to a valid request",
+ "schema": {
+ "$ref": "#/definitions/Pets"
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "Pet": {
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "Pets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "Error": {
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3_service_patch.yaml b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3_service_patch.yaml
new file mode 100644
index 00000000..f11116f2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/oas_config/petstore_v3_service_patch.yaml
@@ -0,0 +1,110 @@
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: Swagger Petstore
+ license:
+ name: MIT
+servers:
+ - url: http://new.swagger.io:8000/wooo
+ - url: https://new.swagger.io:8000/wooo
+paths:
+ /pets:
+ get:
+ summary: List all pets
+ operationId: listPets
+ tags:
+ - pets
+ parameters:
+ - name: limit
+ in: query
+ description: How many items to return at one time (max 100)
+ required: false
+ schema:
+ type: integer
+ format: int32
+ responses:
+ '200':
+ description: A paged array of pets
+ headers:
+ x-next:
+ description: A link to the next page of responses
+ schema:
+ type: string
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pets"
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ post:
+ summary: Create a pet
+ operationId: createPets
+ tags:
+ - pets
+ responses:
+ '201':
+ description: Null response
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ /pets/{petId}:
+ get:
+ summary: Info for a specific pet
+ operationId: showPetById
+ tags:
+ - pets
+ parameters:
+ - name: petId
+ in: path
+ required: true
+ description: The id of the pet to retrieve
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Expected response to a valid request
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Pets"
+ default:
+ description: unexpected error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+components:
+ schemas:
+ Pet:
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ tag:
+ type: string
+ Pets:
+ type: array
+ items:
+ $ref: "#/components/schemas/Pet"
+ Error:
+ required:
+ - code
+ - message
+ properties:
+ code:
+ type: integer
+ format: int32
+ message:
+ type: string
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body-body_size_invalid.conf b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body-body_size_invalid.conf
new file mode 100644
index 00000000..01448af7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body-body_size_invalid.conf
@@ -0,0 +1,2 @@
+response_transformation_enable_limit_body=on
+response_transformation_limit_body_size=wow
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body-body_size_not_defined.conf b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body-body_size_not_defined.conf
new file mode 100644
index 00000000..5055e397
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body-body_size_not_defined.conf
@@ -0,0 +1 @@
+response_transformation_enable_limit_body=on
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body.conf b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body.conf
new file mode 100644
index 00000000..f59c0b3d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body.conf
@@ -0,0 +1,2 @@
+response_transformation_enable_limit_body=on
+response_transformation_limit_body_size=16384
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body_chunked.conf b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body_chunked.conf
new file mode 100644
index 00000000..9ba66f4d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/response_transformer/feature_response_transformer_limit_body_chunked.conf
@@ -0,0 +1,2 @@
+response_transformation_enable_limit_body=on
+response_transformation_limit_body_size=5
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/vaults/mock.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/vaults/mock.lua
new file mode 100644
index 00000000..2102fab8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/vaults/mock.lua
@@ -0,0 +1,358 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+-- using the full path so that we don't have to modify package.path in
+-- this context
+local test_vault = require "spec.fixtures.custom_vaults.kong.vaults.test"
+local utils = require "kong.tools.utils"
+local cjson = require "cjson"
+local assert = require("luassert")
+
+
+-- AWS dependencies
+local aws = require "resty.aws"
+local EnvironmentCredentials = require "resty.aws.credentials.EnvironmentCredentials"
+
+
+-- Azure dependencies
+local azure = require "resty.azure"
+
+
+-- GCP dependencies
+local gcp = require "resty.gcp"
+local access_token = require "resty.gcp.request.credentials.accesstoken"
+
+
+-- HCV dependencies
+local hcv = require "kong.vaults.hcv"
+
+
+--- A vault test harness is a driver for vault backends, which implements
+--- all the necessary glue for initializing a vault backend and performing
+--- secret read/write operations.
+---
+--- All functions defined here are called as "methods" (e.g. harness:fn()), so
+--- it is permitted to keep state on the harness object (self).
+---
+---@class harness
+---
+---@field name string
+---
+--- this table is passed directly to kong.db.vaults:insert()
+---@field config table
+---
+--- create_secret() is called once per test run for a given secret
+---@field create_secret fun(self: harness, secret: string, value: string, opts?: table)
+---
+--- update_secret() may be called more than once per test run for a given secret
+---@field update_secret fun(self: harness, secret: string, value: string, opts?: table)
+----
+--- delete_secret() may be called more than once per test run for a given secret
+---@field delete_secret fun(self: harness, secret: string)
+---
+--- setup() is called before kong is started and before any DB entities
+--- have been created and is best used for things like validating backend
+--- credentials and establishing a connection to a backend
+---@field setup fun(self: harness)
+---
+--- teardown() is exactly what you'd expect
+---@field teardown fun(self: harness)
+---
+--- fixtures() output is passed directly to `helpers.start_kong()`
+---@field fixtures fun(self: harness):table|nil
+---
+---
+---@field prefix string # generated by the test suite
+---@field host string # generated by the test suite
+
+
+---@type harness[]
+local VAULTS = {
+ {
+ name = "test",
+
+ config = {
+ default_value = "DEFAULT",
+ default_value_ttl = 1,
+ },
+
+ create_secret = function(self, _, value)
+ -- Currently, create_secret is called _before_ starting Kong.
+ --
+ -- This means our backend won't be available yet because it is
+ -- piggy-backing on Kong as an HTTP mock fixture.
+ --
+ -- We can, however, inject a default value into our configuration.
+ -- The test vault implementation uses the defaults just once, so that
+ -- secrets can be loaded during startup.
+ self.config.default_value = cjson.encode({secret = value})
+ end,
+
+ update_secret = function(_, secret, value, opts)
+ return test_vault.client.put(secret, cjson.encode({secret = value}), opts)
+ end,
+
+ delete_secret = function(_, secret)
+ return test_vault.client.delete(secret)
+ end,
+
+ fixtures = function()
+ return {
+ http_mock = {
+ test_vault = test_vault.http_mock,
+ }
+ }
+ end,
+ },
+
+ {
+ name = "aws",
+
+ config = {
+ region = "us-east-1",
+ },
+
+ -- lua-resty-aws sdk object
+ AWS = nil,
+
+ -- lua-resty-aws secrets-manager client object
+ sm = nil,
+
+ -- secrets that were created during the test run, for cleanup purposes
+ secrets = {},
+
+ setup = function(self)
+ assert(os.getenv("AWS_ACCESS_KEY_ID"),
+ "missing AWS_ACCESS_KEY_ID environment variable")
+
+ assert(os.getenv("AWS_SECRET_ACCESS_KEY"),
+ "missing AWS_SECRET_ACCESS_KEY environment variable")
+
+ self.AWS = aws({ credentials = EnvironmentCredentials.new() })
+ self.sm = assert(self.AWS:SecretsManager(self.config))
+ end,
+
+ create_secret = function(self, secret, value, _)
+ assert(self.sm, "secrets manager is not initialized")
+ local res, err = self.sm:createSecret({
+ ClientRequestToken = utils.uuid(),
+ Name = secret,
+ SecretString = cjson.encode({secret = value}),
+ })
+
+ assert.is_nil(err)
+ assert.is_equal(200, res.status)
+
+ table.insert(self.secrets, res.body.ARN)
+ end,
+
+ update_secret = function(self, secret, value, _)
+ local res, err = self.sm:putSecretValue({
+ ClientRequestToken = utils.uuid(),
+ SecretId = secret,
+ ForceDeleteWithoutRecovery = true,
+ RecoveryWindowInDays = 0,
+ SecretString = cjson.encode({secret = value}),
+ })
+
+ assert.is_nil(err)
+ assert.is_equal(200, res.status)
+ end,
+
+ delete_secret = function(self, secret)
+ local res, err = self.sm:deleteSecret({
+ SecretId = secret,
+ })
+
+ assert.is_nil(err)
+ assert.is_equal(200, res.status)
+ end
+ },
+
+ {
+ name = "azure",
+
+ -- lua-resty-azure sdk object
+ AZURE = nil,
+
+ -- lua-resty-azure secrets-manager client object
+ sm = nil,
+
+ -- secrets that were created during the test run, for cleanup purposes
+ secrets = {},
+
+ setup = function(self)
+ assert(os.getenv("AZURE_TENANT_ID"),
+ "missing AZURE_TENANT_ID environment variable")
+
+ assert(os.getenv("AZURE_CLIENT_ID"),
+ "missing AZURE_CLIENT_ID environment variable")
+
+ assert(os.getenv("AZURE_CLIENT_SECRET"),
+ "missing AZURE_CLIENT_SECRET environment variable")
+
+ local uri = assert(os.getenv("AZURE_VAULT_URI"),
+ "missing AZURE_VAULT_URI environment variable")
+
+ self.config = {
+ location = "eastus",
+ type = "secrets",
+ vault_uri = uri,
+ }
+
+ self.AZURE = azure:new(self.config)
+ self.sm = assert(self.AZURE:secrets(uri))
+ end,
+
+ create_secret = function(self, secret, value, _)
+ assert(self.sm, "secrets manager is not initialized")
+ local err, res
+ assert
+ .with_timeout(360)
+ .with_step(5)
+ .eventually(function()
+ res, err = self.sm:create(secret, cjson.encode({secret = value}))
+ if res and
+ res.error and
+ res.error.innererror and
+ res.error.innererror.code == "ObjectIsDeletedButRecoverable" then
+ -- We need to purge or recover a secret after it has been deleted.
+ -- This is the azure way of accidential deletion protection.
+ self.sm:purge(secret)
+ end
+ return err == nil and res.value ~= nil
+ end).is_truthy("Could not create secret in time " .. (err or ""))
+ assert.is_nil(err)
+ -- assert.is_equal(res.value, value)
+ assert.is_table(res.attributes)
+ assert.is_true(res.attributes.enabled)
+ end,
+
+ -- Azure does not have a concept of updating a secret, you rather increment the
+ -- version number of the secret by "creating" a new one
+ update_secret = function(self, secret, value, _)
+ self:create_secret(secret, value)
+ end,
+
+ delete_secret = function(self, secret)
+ self.sm:delete(secret)
+ end
+ },
+
+
+ {
+ name = "gcp",
+
+ GCP = nil,
+
+ access_token = nil,
+
+ secrets = {},
+
+ config = {},
+
+ setup = function(self)
+ local service_account = assert(os.getenv("GCP_SERVICE_ACCOUNT"), "missing GCP_SERVICE_ACCOUNT environment variable")
+
+ self.GCP = gcp()
+ self.config.project_id = assert(cjson.decode(service_account).project_id)
+ self.access_token = access_token.new()
+ end,
+
+ create_secret = function(self, secret, value, _)
+ local res, err = self.GCP.secretmanager_v1.secrets.create(
+ self.access_token,
+ {
+ projectsId = self.config.project_id,
+ secretId = secret,
+ },
+ {
+ replication = {
+ automatic = {}
+ }
+ }
+ )
+ assert.is_nil(err)
+ assert.is_nil(res.error)
+
+ self:update_secret(secret, value, _)
+
+ table.insert(self.secrets, secret)
+ end,
+
+ update_secret = function(self, secret, value)
+ local res, err = self.GCP.secretmanager_v1.secrets.addVersion(
+ self.access_token,
+ {
+ projectsId = self.config.project_id,
+ secretsId = secret,
+ },
+ {
+ payload = {
+ data = ngx.encode_base64(cjson.encode({secret = value})),
+ }
+ }
+ )
+ assert.is_nil(err)
+ assert.is_nil(res.error)
+ end,
+
+ delete_secret = function(self, secret)
+ local res, err = self.GCP.secretmanager_v1.secrets.delete(
+ self.access_token,
+ {
+ projectsId = self.config.project_id,
+ secretsId = secret,
+ }
+ )
+ assert.is_nil(err)
+ assert.is_nil(res.error)
+ end
+ },
+
+ -- hashi vault
+ {
+ name = "hcv",
+
+ config = {
+ token = "vault-plaintext-root-token",
+ host = "localhost",
+ port = 8200,
+ kv = "v2",
+ },
+
+ create_secret = function(self, ...)
+ return self:update_secret(...)
+ end,
+
+ update_secret = function(self, secret, value, _)
+ local _, err = hcv._request(
+ self.config,
+ secret,
+ nil,
+ {
+ method = "POST",
+ body = cjson.encode({data = { secret = value }})
+ })
+ assert.is_nil(err)
+ end,
+
+ delete_secret = function(self, secret)
+ local _, err = hcv._request(
+ self.config,
+ secret,
+ nil,
+ {
+ method = "DELETE",
+ })
+ assert.is_nil(err)
+ end
+ }
+}
+
+return VAULTS
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket.lua
new file mode 100644
index 00000000..57fdc1c3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket.lua
@@ -0,0 +1,133 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local const = require "spec-ee.fixtures.websocket.constants"
+
+local fmt = string.format
+
+local PORTS = const.ports
+
+local function mock_upstream(root_path)
+ if not root_path then
+ local str = debug.getinfo(1, "S").source:sub(2)
+ local path = str:match("(.*/)")
+ if path:sub(1, 1) ~= "/" then -- relative path
+ path = lfs.currentdir() .. "/" .. path
+ end
+ -- spec-ee/fixtures/../../
+ root_path = path .. "../../"
+ end
+
+ return fmt([[
+ lua_shared_dict kong_test_websocket_fixture 64m;
+
+ server {
+ listen %s;
+ listen %s ssl;
+
+ server_name ws_fixture;
+
+ ssl_certificate %s/spec/fixtures/mtls_certs/example.com.crt;
+ ssl_certificate_key %s/spec/fixtures/mtls_certs/example.com.key;
+
+ ssl_client_certificate %s/spec/fixtures/mtls_certs/ca.crt;
+ ssl_verify_client optional;
+
+ ssl_session_tickets off;
+ ssl_session_cache off;
+ keepalive_requests 0;
+
+ lua_check_client_abort on;
+
+ # we use sock:receiveany() in the WS session fixture in order to forward
+ # bytes blindly, so a large buffer size helps with performance and test
+ # reliability
+ lua_socket_buffer_size 64k;
+
+ lingering_close off;
+
+ rewrite_by_lua_block {
+ require("spec-ee.fixtures.websocket.upstream").rewrite()
+ }
+
+ location / {
+ content_by_lua_block {
+ require("spec-ee.fixtures.websocket.upstream").echo()
+ }
+ }
+
+ location ~ "^/status/(?\d{3})$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local code = tonumber(ngx.var.code)
+ if not code then
+ return ngx.exit(ngx.HTTP_NOT_FOUND)
+ end
+ ngx.status = code
+ return mu.send_default_json_response({
+ code = code,
+ })
+ }
+ }
+
+ location = /session/client {
+ content_by_lua_block {
+ require("spec-ee.fixtures.websocket.upstream").client()
+ }
+ }
+
+ location = /session/listen {
+ content_by_lua_block {
+ require("spec-ee.fixtures.websocket.upstream").listen()
+ }
+ }
+
+ location ~ ^/log/(?.+)$ {
+ content_by_lua_block {
+ require("spec-ee.fixtures.websocket.upstream").get_log()
+ }
+ }
+ }
+ ]], PORTS.ws, PORTS.wss, root_path, root_path, root_path)
+end
+
+
+---@param wc ws.test.client|string
+---@param timeout? integer
+---@return kong.log.serialized.entry
+local function get_session_log(wc, timeout)
+ local id = wc
+ if type(id) == "table" then
+ id = assert(wc.id)
+ end
+ timeout = timeout or 5
+
+ local httpc = require("resty.http").new()
+ assert(httpc:connect({
+ scheme = "http",
+ host = "127.0.0.1",
+ port = PORTS.ws,
+ }))
+
+ local res, err = httpc:request({
+ method = "GET",
+ path = "/log/" .. id,
+ query = { timeout = timeout },
+ })
+
+ assert(res, err)
+ assert(res.status == 200, "non-200 response: " .. (tostring(res.status)))
+
+ return require("cjson").decode(res:read_body())
+end
+
+
+return {
+ get_session_log = get_session_log,
+ const = const,
+ mock_upstream = mock_upstream,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/action.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/action.lua
new file mode 100644
index 00000000..26693fe4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/action.lua
@@ -0,0 +1,646 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local const = require "spec-ee.fixtures.websocket.constants"
+
+local fmt = string.format
+local find = string.find
+local re_find = ngx.re.find
+local OPCODES = const.opcode
+local sleep = ngx.sleep
+
+---
+-- WebSocket "actions" represent small pieces of code that are to be
+-- consumed/executed by the `spec-ee.fixtures.websocket.session` module in
+-- order to validate correct behavior.
+--
+-- They consist of a name, callback function, and a target identifier. The target
+-- informs the session of the object that is to be passed in to the callback
+-- function--one of `client`, `server`, or `session` (for "meta" actions that
+-- act upon the session object itself).
+--
+-- Action callbacks should return `true` on success or `nil` and an error string
+-- on failure.
+--
+-- Example:
+--
+-- ```lua
+-- session:assert({
+-- {
+-- name = "send_ping",
+-- target = "client",
+-- fn = function(client)
+-- if client:send_ping("ping") then
+-- return true
+-- end
+-- return nil, "failed sending ping"
+-- end,
+-- }
+-- })
+--
+---@class ws.session.action
+---@field name string
+---@field target '"server"'|'"client"'|'"session"'
+---@field fn ws.session.action.callback
+
+---@alias ws.session.action.callback fun(target:ws.test.client|ws.session, ...):boolean|nil, string|nil
+
+---@alias ws.session.action.factory fun(...):ws.session.action
+
+---@alias ws.session.action.collection table
+
+local function is_timeout(err)
+ return type(err) == "string" or find(err, "timeout") ~= nil
+end
+
+local function is_closed(err)
+ return type(err) == "string" and find(err, "closed") ~= nil
+end
+
+local function is_fin(err)
+ return err ~= "again"
+end
+
+---@param ws ws.test.client
+---@return boolean ok
+---@return string? error
+local function recv_any(ws)
+ local data, _, err = ws:recv_frame()
+ return data ~= nil, err
+end
+
+---@param timeout integer
+---@return ws.session.action.callback
+local function recv_timeout(timeout)
+ ---@param ws ws.test.client
+ ---@param session ws.session
+ ---@return boolean ok
+ ---@return string? error
+ return function(ws, session)
+ if timeout then
+ ws.client.sock:settimeouts(nil, nil, timeout)
+ end
+
+ local data, typ, err = ws:recv_frame()
+
+ if timeout then
+ ws.client.sock:settimeouts(nil, nil, session.timeout)
+ end
+
+ if data ~= nil then
+ return nil, fmt("expected timeout but received %s frame", typ)
+ end
+
+ if not is_timeout(err) then
+ return nil, fmt("expected timeout but received non-timeout error: %q", err)
+ end
+
+ return true
+ end
+end
+
+---@param exp_err string
+---@return ws.session.action.callback
+local function recv_error(exp_err)
+ assert(type(exp_err) == "string", "expected error string is required")
+
+ ---@param ws ws.test.client
+ return function(ws)
+ local data, typ, err = ws:recv_frame()
+ if data ~= nil then
+ return nil, fmt("expected error but received %s frame", typ)
+ end
+
+ if is_timeout(err) then
+ return nil, "expected error but received timeout"
+ end
+
+ if not re_find(err, exp_err, "oj") then
+ return nil, fmt("receied error (%q) did not match %q", err, exp_err)
+ end
+
+ return true
+ end
+end
+
+
+---@type ws.session.action.callback
+---@param ws ws.test.client
+---@param check_err? boolean
+local function close_conn(ws, check_err)
+ local ok, err = ws:close()
+
+ if not ok and check_err and not is_closed(err) then
+ return nil, fmt("ws client did not close cleanly: %q", err)
+ end
+
+ return true
+end
+
+
+---@param ws ws.test.client
+---@param exp_type resty.websocket.protocol.type
+---@param exp_data? string
+---@param exp_status? integer
+local function recv_type(ws, exp_type, exp_data, exp_status)
+ local data, typ, err = ws:recv_frame()
+ if not data then
+ if is_timeout(err) then
+ return nil, fmt("expected %s frame but got timeout", exp_type)
+
+ elseif is_closed(err) then
+ return nil, fmt("expected %s frame but connection is closed", exp_type)
+ end
+
+ return nil, fmt("expected %s frame but got an error: %q", exp_type, err)
+
+ elseif typ ~= exp_type then
+ return nil, fmt("expected %s frame but got %s frame", exp_type, typ)
+
+ elseif exp_data and data ~= exp_data then
+ return nil, fmt("expected payload: %q, received: %q", exp_data, data)
+
+ elseif typ == "close"
+ and exp_status
+ and exp_status ~= err
+ then
+ return nil, fmt("expected close status %s but received %s", exp_status, err)
+
+ end
+
+ return true
+end
+
+
+---@param target '"client"'|'"server"'
+---@param exp_type resty.websocket.protocol.type
+---@param exp_data? string
+---@param exp_status? integer
+---@return ws.session.action
+local function receiver(target, exp_type, exp_data, exp_status)
+ return {
+ name = "recv_" .. exp_type,
+ target = target,
+ ---@param ws ws.test.client
+ fn = function(ws)
+ return recv_type(ws, exp_type, exp_data, exp_status)
+ end,
+ }
+end
+
+---@param target '"client"'|'"server"'
+---@param fn string
+---@param data? string
+---@param status? integer
+---@return ws.session.action
+local function sender(target, fn, data, status)
+ return {
+ name = fn,
+ target = target,
+ fn = function(ws)
+ return ws[fn](ws, data, status)
+ end,
+ }
+end
+
+---@param ws ws.test.client
+---@param exp_typ? resty.websocket.protocol.type
+local function handle_echo(ws, exp_typ)
+ local data, typ, err = ws:recv_frame()
+ if not data then
+ return nil, fmt("expected %s frame but got error: %q", exp_typ, err)
+
+ elseif exp_typ and typ ~= exp_typ then
+ return nil, fmt("expected %s frame but got %s frame", exp_typ, typ)
+ end
+
+ local sent
+ if typ == "ping" then
+ sent, err = ws:send_pong(data)
+
+ elseif typ == "pong" then
+ sent, err = ws:send_pong(data)
+
+ elseif typ == "close" then
+ sent, err = ws:send_close(data, err)
+
+ else
+ local fin = is_fin(err)
+ local opcode = OPCODES[typ]
+ sent, err = ws:send_frame(fin, opcode, data)
+ end
+
+ if not sent then
+ return nil, fmt("failed sending echo response: %q", err)
+ end
+
+ return true
+end
+
+
+local function echoer(typ, data, status)
+ local exp_type = typ
+ if typ == "ping" then exp_type = "pong"
+ elseif typ == "pong" then exp_type = "ping"
+ end
+
+ return {
+ name = "echo_" .. typ,
+ target = "session",
+ ---@param sess ws.session
+ fn = function(sess)
+ local client = sess.client
+ local fn = client["send_" .. typ]
+ local ok, err = fn(client, data, status)
+ if not ok then
+ return nil, err
+ end
+
+ if sess.server_echo then
+ ok, err = handle_echo(sess.server, typ)
+ if not ok then
+ return nil, err
+ end
+ end
+
+ return recv_type(client, exp_type, data, status)
+ end,
+ }
+end
+
+
+---@param target '"client"'|'"server"'
+local function send_actions(target)
+ return {
+ ---
+ -- Send a ping frame
+ --
+ ---@param data? string
+ ---@return ws.session.action
+ ping = function(data)
+ return sender(target, "send_ping", data)
+ end,
+
+ ---
+ -- Send a pong frame
+ --
+ ---@param data? string
+ ---@return ws.session.action
+ pong = function(data)
+ return sender(target, "send_pong", data)
+ end,
+
+ ---
+ -- Send a text frame
+ --
+ ---@param data string
+ ---@return ws.session.action
+ text = function(data)
+ return sender(target, "send_text", data)
+ end,
+
+ ---
+ -- Send a binary frame
+ --
+ ---@param data string
+ ---@return ws.session.action
+ binary = function(data)
+ return sender(target, "send_binary", data)
+ end,
+
+ ---
+ -- Send a close frame
+ --
+ ---@param data? string
+ ---@param status? integer
+ ---@return ws.session.action
+ close = function(data, status)
+ return sender(target, "send_close", data, status)
+ end,
+
+
+ ---
+ -- Send a continuation frame
+ --
+ ---@param data string
+ ---@return ws.session.action
+ continue = function(data)
+ return sender(target, "send_continue", data)
+ end,
+
+ ---
+ -- Send the first frame of a fragmented text message
+ --
+ ---@param data string
+ ---@return ws.session.action
+ text_fragment = function(data)
+ return sender(target, "init_text_fragment", data)
+ end,
+
+ ---
+ -- Send the first frame of a fragmented binary message
+ --
+ ---@param data string
+ ---@return ws.session.action
+ binary_fragment = function(data)
+ return sender(target, "init_binary_fragment", data)
+ end,
+
+ ---
+ -- Send the final frame of a fragmented message
+ --
+ ---@param data string
+ ---@return ws.session.action
+ final_fragment = function(data)
+ return sender(target, "send_final_fragment", data)
+ end,
+ }
+end
+
+---@param target '"client"'|'"server"'
+local function recv_actions(target)
+ return {
+ ---
+ -- Expect a text frame and validate its payload
+ --
+ ---@param data string
+ text = function(data)
+ return receiver(target, "text", data)
+ end,
+
+ ---
+ -- Expect a binary frame and validate its payload
+ --
+ ---@param data string
+ binary = function(data)
+ return receiver(target, "binary", data)
+ end,
+
+ ---
+ -- Expect a ping frame and validate its payload
+ --
+ ---@param data string
+ ping = function(data)
+ return receiver(target, "ping", data)
+ end,
+
+ ---
+ -- Expect a pong frame and validate its payload
+ --
+ ---@param data string
+ pong = function(data)
+ return receiver(target, "pong", data)
+ end,
+
+ ---
+ -- Expect a continuation frame and validate its payload
+ --
+ ---@param data string
+ continue = function(data)
+ return receiver(target, "continue", data)
+ end,
+
+ ---
+ -- Expect a close frame and validate its payload and status code
+ --
+ ---@param data? string
+ ---@param status? integer
+ close = function(data, status)
+ return receiver(target, "close", data, status)
+ end,
+
+
+ ---
+ -- Recieve a single frame of any type
+ ---@return ws.session.action
+ any = function()
+ return {
+ target = target,
+ fn = recv_any,
+ }
+ end,
+
+ ---
+ -- Call recv_frame() and ensure that the read operation times out
+ -- with no frame having been received
+ --
+ ---@param timeout? integer
+ ---@return ws.session.action
+ timeout = function(timeout)
+ return {
+ target = target,
+ fn = recv_timeout(timeout),
+ }
+ end,
+
+ ---
+ -- Call recv_frame() and expect an error
+ --
+ ---@param err string
+ ---@return ws.session.action
+ error = function(err)
+ return {
+ target = target,
+ fn = recv_error(err),
+ }
+ end,
+ }
+end
+
+---@param target '"client"'|'"server"'
+---@return ws.session.action.factory
+local function close_action(target)
+ ---
+ -- Close the WebSocket connection
+ return function()
+ return {
+ name = "close",
+ target = target,
+ fn = close_conn,
+ }
+ end
+end
+
+
+local function server_echo()
+ local t = {
+ ---
+ -- Enable automatic echo replies from the server
+ enable = function()
+ return {
+ name = "enable_echo",
+ target = "session",
+ fn = function(sess)
+ sess.server_echo = true
+ return true
+ end,
+ }
+ end,
+
+ ---
+ -- Disable automatic echo replies from the server
+ disable = function()
+ return {
+ name = "disable_echo",
+ target = "session",
+ fn = function(sess)
+ sess.server_echo = false
+ return true
+ end,
+ }
+ end,
+ }
+
+ setmetatable(t, {
+ __call = function()
+ return {
+ name = "echo",
+ target = "server",
+ fn = function(ws)
+ return handle_echo(ws)
+ end,
+ }
+ end,
+ })
+
+ return t
+end
+
+local function client_echo()
+ return {
+ ---
+ -- Send a text frame from the client and validate that the server echoes it
+ -- back to us.
+ ---@param data string
+ text = function(data)
+ return echoer("text", data)
+ end,
+
+ ---
+ -- Send a binary frame from the client and validate that the server echoes it
+ -- back to us.
+ ---@param data string
+ binary = function(data)
+ return echoer("binary", data)
+ end,
+
+ ---
+ -- Send a ping frame from the client and validate that the server responds
+ -- with a matching pong frame
+ ---@param data? string
+ ping = function(data)
+ return echoer("ping", data)
+ end,
+
+ ---
+ -- Send a close frame from the client and validate that the server responds
+ -- with a matching close frame
+ ---@param data? string
+ ---@param status? integer
+ close = function(data, status)
+ return echoer("close", data, status)
+ end,
+ }
+end
+
+
+local function client_actions()
+ return {
+ send = send_actions("client"),
+ recv = recv_actions("client"),
+ close = close_action("client"),
+ echo = client_echo(),
+ }
+end
+
+local function server_actions()
+ return {
+ send = send_actions("server"),
+ recv = recv_actions("server"),
+ close = close_action("server"),
+ echo = server_echo(),
+ }
+end
+
+
+local actions = {
+ client = client_actions(),
+ server = server_actions(),
+ echo = client_echo(),
+
+ ---
+ -- Gracefully close the session.
+ close = function()
+ return {
+ name = "graceful_close",
+ target = "session",
+ ---@param sess ws.session
+ fn = function(sess)
+ sess.server:send_close()
+ sess.client:recv_frame()
+ sess.client:send_close()
+ sess.server:recv_frame()
+ sess.server:close()
+ sess.client:close()
+ return true
+ end,
+ }
+ end,
+
+ ---@type ws.session.action.factory
+ ---@param duration number
+ sleep = function(duration)
+ return {
+ name = "sleep",
+ target = "session",
+ fn = function()
+ sleep(duration)
+ return true
+ end,
+ }
+ end,
+
+ ---@type ws.session.action.factory
+ ---@param timeout integer
+ set_recv_timeout = function(timeout)
+ return {
+ name = "set_recv_timeout",
+ target = "session",
+ ---@param session ws.session
+ fn = function(session)
+ session.client.client.sock:settimeouts(nil, nil, timeout)
+ session.server.client.sock:settimeouts(nil, nil, timeout)
+ return true
+ end,
+ }
+ end,
+}
+
+-- The labels `client` and `server` were chosen because they are the same
+-- string length, and the alignment makes tests more readable:
+--
+-- local client, server = actions.client, actions.server
+-- session:assert({
+-- client.send.text("hi"),
+-- server.recv.text("hi"),
+-- })
+--
+-- In many other contexts, we use `upstream` instead of `server`, so having
+-- an alias for it helps when constructing parameterized tests:
+--
+-- for src, dst in pairs({ client = "upstream", upstream = "client"}) do
+-- session:assert({
+-- actions[src].send.text("hi"),
+-- actions[dst].recv.text("hi"),
+-- })
+-- end
+--
+actions.upstream = actions.server
+
+
+return actions
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/constants.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/constants.lua
new file mode 100644
index 00000000..0a68257c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/constants.lua
@@ -0,0 +1,32 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local const = require "kong.enterprise_edition.constants"
+
+return {
+ headers = {
+ id = "x-mock-websocket-request-id",
+ self = "x-mock-websocket",
+ multi = "x-mock-websocket-multi",
+ },
+
+ ports = {
+ ws = 3000,
+ wss = 3001,
+ },
+
+ -- token message values that the client sends to request
+ -- connection metadata from the mock upstream server
+ tokens = {
+ request = "$_REQUEST",
+ response = "$_RESPONSE",
+ },
+
+ opcode = const.WEBSOCKET.OPCODE_BY_TYPE,
+ type = const.WEBSOCKET.TYPE_BY_OPCODE,
+ status = const.WEBSOCKET.STATUS,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/rpc.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/rpc.lua
new file mode 100644
index 00000000..a7965fe7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/rpc.lua
@@ -0,0 +1,451 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local cjson = require "cjson.safe"
+local const = require "spec-ee.fixtures.websocket.constants"
+
+local unpack = require("kong.tools.table").unpack
+local log = ngx.log
+local concat = table.concat
+local insert = table.insert
+local fmt = string.format
+
+local NOTICE = ngx.NOTICE
+
+---
+-- RPC Plugin fixture
+--
+-- This fixture is designed around turning the pre-function/post-function
+-- plugins into somewhat of an RPC tool for WebSocket connections.
+--
+-- It consists of two main components:
+-- * Functions that are called during websocket handlers (for instance, by
+-- calling `require("spec-ee.fixtures.websocket.rpc").handler.ws_handshake()`
+-- during the ws_handshake phase
+-- * Helper functions for generating RPC call actions
+--
+-- The goal of all this is to enable easy testing of various PDK functions
+-- inside the WebSocket plugin handlers.
+--
+local RPC = {}
+
+
+---@alias ws.test.rpc.target
+---| '"client"'
+---| '"upstream"'
+---| '"close"'
+
+
+---@class ws.test.rpc.cmd : table
+---@field target ws.test.rpc.target
+---@field fn string
+---@field args any[]
+---@field eval string
+---@field postpone boolean
+
+
+---@param cmd ws.test.rpc.cmd
+local function handler(cmd)
+ if cmd.fn then
+ local ref = _G
+ cmd.fn:gsub("([^.]+)", function(k)
+ assert(type(ref) == "table")
+ ref = ref[k]
+ end)
+
+ assert(type(ref) == "function", tostring(cmd.fn) .. " is not a function")
+
+ local args = cmd.args or {}
+
+ log(NOTICE, "RPC ", cmd.target, " call: ", cmd.fn,
+ ", args: ", concat(args, ","))
+
+ return ref(unpack(args))
+
+ elseif cmd.eval then
+ log(NOTICE, "RPC ", cmd.target, " eval: ", cmd.eval)
+
+ local fn = assert(loadstring(cmd.eval))
+ return fn()
+ end
+end
+
+
+---@param role '"client"'|'"upstream"'
+local function ws_frame(role)
+ local ws = kong.websocket[role]
+ local ctx = kong.ctx.plugin
+
+ local data, typ, status = ws.get_frame()
+
+ local record = ctx.DATA[role]
+ record.frames = record.frames + 1
+
+ if typ == "close" then
+ record.close_status = status or const.status.NO_STATUS.CODE
+ record.close_reason = data ~= "" and data or const.status.NO_STATUS.REASON
+ end
+
+ ---@type ws.test.rpc.cmd
+ local cmd
+
+ -- check for any pending commands
+ if ctx.pending and ctx.pending.target == role then
+ ngx.log(NOTICE, "found a pending rpc command")
+ cmd = ctx.pending
+ ctx.pending = nil
+
+ -- check the in-flight text frame for a command
+ elseif typ == "text" then
+ local decoded = cjson.decode(data)
+ if type(decoded) == "table" and decoded.target then
+ cmd = decoded
+ end
+ end
+
+ if not cmd then
+ return
+ end
+
+ -- schedule command for ws_close
+ if cmd.target == "close" then
+ local t = ctx.rpc_close or {}
+ insert(t, cmd)
+ ctx.rpc_close = t
+ ws.drop_frame()
+ return
+
+ -- postpone command until next frame
+ -- frame will be dropped
+ elseif cmd.postpone then
+ ngx.log(NOTICE, "postponing ", cmd.target, " command")
+ cmd.postpone = nil
+ ctx.pending = cmd
+
+ ws.drop_frame()
+ return
+
+ elseif cmd.target ~= role then
+ return
+ end
+
+ local res = handler(cmd)
+
+ if res then
+ ws.set_frame_data(tostring(res))
+ end
+end
+
+
+---
+-- RPC handler functions
+--
+-- These are wired in via pre-function/post-function expressions
+RPC.handler = {
+ ---
+ -- Handshake handler for RPC plugin fixture
+ ws_handshake = function()
+ kong.response.set_header("ws-function-test", "hello")
+ kong.ctx.plugin.DATA = {
+ upstream = {
+ frames = 0,
+ close_status = const.status.NO_STATUS.CODE,
+ close_reason = const.status.NO_STATUS.REASON,
+ },
+ client = {
+ frames = 0,
+ close_status = const.status.NO_STATUS.CODE,
+ close_reason = const.status.NO_STATUS.REASON,
+ },
+ }
+ end,
+
+ ---
+ -- Client frame handler for RPC plugin fixture
+ ws_client_frame = function()
+ return ws_frame("client")
+ end,
+
+ ---
+ -- Upstream frame handler for RPC plugin fixture
+ ws_upstream_frame = function()
+ return ws_frame("upstream")
+ end,
+
+ ---
+ -- Close handler for RPC plugin fixture
+ ws_close = function()
+ kong.log.set_serialize_value("ws", kong.ctx.plugin.DATA)
+
+ local cmds = kong.ctx.plugin.rpc_close
+ if cmds then
+ for _, cmd in ipairs(cmds) do
+ handler(cmd)
+ end
+ end
+ end,
+}
+
+
+---
+-- Create an RPC call action
+--
+---@param target ws.test.rpc.target
+---@param postpone boolean
+---@param fn string
+---@vararg any
+---@return ws.session.action
+local function rpc_call(target, postpone, fn, ...)
+ local name = "rpc_call"
+ if postpone then
+ name = name .. "_postpone"
+ end
+
+ local args
+ if select("#", ...) > 0 then
+ args = {}
+ for i = 1, select("#", ...) do
+ args[i] = select(i, ...)
+ end
+ end
+
+ local payload = cjson.encode({
+ target = target,
+ postpone = postpone,
+ fn = fn,
+ args = args,
+ })
+
+ return {
+ name = name,
+ target = "client",
+ fn = function(ws)
+ return ws:send_text(payload)
+ end,
+ }
+end
+
+
+---
+-- Create an RPC eval action
+--
+---@param target ws.test.rpc.target
+---@param postpone boolean
+---@param code string
+---@return ws.session.action
+local function rpc_eval(target, postpone, code)
+ local name = "rpc_eval"
+ if postpone then
+ name = name .. "_postpone"
+ end
+
+ local payload = cjson.encode({
+ target = target,
+ postpone = postpone,
+ eval = code,
+ })
+
+ return {
+ name = name,
+ target = "client",
+ fn = function(ws)
+ return ws:send_text(payload)
+ end
+ }
+end
+
+
+local function make_rpc(target)
+ return {
+ ---
+ -- Call the given function upon receipt of this frame
+ --
+ -- If the function returns a truth-y value, the payload of the current
+ -- frame are replaced with this return value
+ --
+ --```lua
+ -- session:assert({
+ -- RPC.client.call("kong.request.get_scheme"),
+ -- WS.server.recv.text("http"),
+ -- })
+ --```
+ --
+ ---@param fn string
+ ---@vararg any
+ call = function(fn, ...)
+ return rpc_call(target, false, fn, ...)
+ end,
+
+ ---
+ -- Evaluate the given lua expression upon receipt of this frame
+ --
+ -- If the expression returns truth-y value, the payload of the current
+ -- frame are replaced with this return value
+ --
+ --```lua
+ -- session:assert({
+ -- RPC.client.eval("kong.ctx.plugin.foo = 1"),
+ -- WS.server.recv.any(),
+ --
+ -- RPC.client.eval("return kong.ctx.plugin.foo"),
+ -- WS.server.recv.text("1"),
+ -- })
+ --```
+ --
+ ---@param expr string
+ eval = function(expr)
+ return rpc_eval(target, false, expr)
+ end,
+
+ next = {
+ ---
+ -- Call the given function upon receipt of the _next_ frame.
+ --
+ -- The frame containing this RPC instruction will be dropped.
+ --
+ ---@param fn string
+ ---@vararg any
+ call = function(fn, ...)
+ return rpc_call(target, true, fn, ...)
+ end,
+
+ ---
+ -- Evaluate the given lua code upon receipt of the _next_ frame
+ --
+ -- The frame containing this RPC instruction will be dropped.
+ --
+ ---@param code string
+ eval = function(code)
+ return rpc_eval(target, true, code)
+ end,
+ }
+ }
+end
+
+
+---
+-- RPC client frame actions
+RPC.client = make_rpc("client")
+
+
+---
+-- RPC upstream frame actions
+RPC.upstream = make_rpc("upstream")
+
+
+---
+-- RPC close actions
+RPC.close = {
+ ---
+ -- Evaluate the given lua code during the ws_close phase
+ --
+ ---@param code string
+ eval = function(code)
+ return rpc_eval("close", false, code)
+ end,
+}
+
+
+---
+-- Generate a lua function string that writes to a temp file.
+--
+-- The body should be a lua expression.
+--
+-- The filename and function string are returned.
+--
+-- This was written with `RPC.close.eval()` in mind:
+--
+--```lua
+-- local fname, writer = RPC.file_writer("kong.ctx.plugin.foo")
+--
+-- session:assert({
+-- RPC.close.eval(writer)
+-- })
+--
+-- session:close()
+--
+-- assert_file_exists(fname)
+-- assert.equals("foo contents", read_file(fname))
+--```
+--
+---@param body string
+---@return string filename
+---@return string writer
+function RPC.file_writer(body)
+ local fname = os.tmpname()
+
+ -- in environments like gojira where busted and Kong run as different users,
+ -- this file will be unwritable, so remove it first
+ os.remove(fname)
+
+ return fname, fmt([[
+ local fname = %q
+ local fh = assert(io.open(fname, "w+"))
+ local content = %s
+ ngx.log(ngx.WARN, "Writing '", content, "' to ", fname)
+ assert(fh:write(content))
+ fh:close()
+ ]], fname, body)
+end
+
+
+-- Generate a lua function string that writes the output of
+-- `kong.log.serialize()` to a temp file.
+--
+-- The filename and function string are returned
+--
+---@return string filename
+---@return string writer
+function RPC.log_writer()
+ return RPC.file_writer([[require("cjson").encode(kong.log.serialize())]])
+end
+
+
+---
+-- Generate an RPC config for the pre-function/post-function plugins
+--
+-- The optional `extra` param allows one to extend the config table before
+-- returning it. If `extra` is a table, its contents are copied into the
+-- final config. If `extra` is a function, it is called with the config as its
+-- first argument, and the return value is used as the final config.
+--
+---@param extra? table|function
+---@return table
+function RPC.plugin_conf(extra)
+ local conf = {
+ ws_handshake = {[[
+ require("spec-ee.fixtures.websocket.rpc").handler.ws_handshake()
+ ]]},
+ ws_client_frame = {[[
+ require("spec-ee.fixtures.websocket.rpc").handler.ws_client_frame()
+ ]]},
+ ws_upstream_frame = {[[
+ require("spec-ee.fixtures.websocket.rpc").handler.ws_upstream_frame()
+ ]]},
+ ws_close = {[[
+ require("spec-ee.fixtures.websocket.rpc").handler.ws_close()
+ ]]},
+ }
+
+ if type(extra) == "table" then
+ for phase, items in pairs(extra) do
+ conf[phase] = conf[phase] or {}
+ for _, item in ipairs(items) do
+ table.insert(conf[phase], item)
+ end
+ end
+
+ elseif type(extra) == "function" then
+ conf = extra(conf)
+ end
+
+ return conf
+end
+
+
+return RPC
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/session.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/session.lua
new file mode 100644
index 00000000..3f22c72d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/session.lua
@@ -0,0 +1,163 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local helpers = require "spec.helpers"
+local ee_helpers = require "spec-ee.helpers"
+local const = require "spec-ee.fixtures.websocket.constants"
+local cjson = require "cjson"
+local assert = require "luassert"
+local pl_file = require "pl.file"
+
+---@class ws.session
+---@field client ws.test.client
+---@field server ws.test.client
+---@field request ws.request.info
+---@field id string
+---@field timeout integer
+---@field server_echo boolean
+local session = {}
+session.__index = session
+
+session.actions = require "spec-ee.fixtures.websocket.action"
+
+local fmt = string.format
+local rep = string.rep
+local concat = table.concat
+
+
+---@return string
+local function get_error_log()
+ local log = helpers.get_running_conf().nginx_err_logs
+ if not log then
+ return "NO ERROR LOG FILE FOUND!"
+ end
+
+ local content, err = pl_file.read(log)
+ if err then
+ return "FAILED READING ERROR LOG: " .. tostring(err or "unknown error")
+
+ elseif content == nil or content == "" then
+ return "ERROR LOG IS EMPTY!"
+ end
+
+ local lines = { "error.log contents:" ,
+ rep("-", 80),
+ content,
+ rep("-", 80) }
+
+ return concat(lines, "\n")
+end
+
+
+---
+-- Validate WebSocket session activity.
+--
+--
+-- @see `spec-ee.fixtures.websocket.action` for action examples
+--
+--
+---@param actions ws.session.action[]
+function session:assert(actions)
+ local len = #actions
+ for i, act in ipairs(actions) do
+ local target
+ if act.target == "session" then
+ target = self
+ else
+ target = self[act.target]
+ end
+
+ local ok, err = act.fn(target, self)
+
+ local extra = ""
+ if not ok then
+ extra = get_error_log()
+ end
+
+ assert(ok, fmt(
+ "\nsession: %s\nposition: %s/%s\naction: %s\ntarget: %s\nerror:\n\t%q\n%s",
+ self.id, i, len, act.name, act.target, err, extra
+ ))
+ end
+end
+
+---
+-- Close/teardown the WebSocket session.
+--
+-- This method is intended for post-test cleanup and does not perform
+-- error-checking or attempt to gracefully close the connection.
+function session:close()
+ self.client:close()
+ self.server:close()
+end
+
+---@class ws.test.session.opts : ws.test.client.opts
+---@field idle_timeout number
+
+---
+-- Initialize a mock WebSocket connection.
+--
+-- This accepts an optional table of options, which are passed to the client
+-- constructor (`spec-ee.helpers.ws_proxy_client`). It returns a table with
+-- two WS client objects ("client" and "server") as well as a table containing
+-- the details of the client handshake request.
+--
+---@param opts ws.test.session.opts
+---@return ws.session
+return function(opts)
+ opts = opts or {}
+
+ local idle_timeout = opts.idle_timeout or 5000
+
+ ngx.log(ngx.INFO, "connecting to session listen endpoint")
+
+ local server = ee_helpers.ws_client({
+ scheme = "ws",
+ path = "/session/listen",
+ port = const.ports.ws,
+ fail_on_error = true,
+ query = { idle_timeout = idle_timeout },
+ })
+
+ local id = server.id
+ ngx.log(ngx.INFO, "connected to session listen endpoint: ", id)
+
+ opts.query = opts.query or {}
+ opts.path = "/session/client"
+ opts.timeout = opts.timeout or 500
+ opts.query.session = id
+
+ ngx.log(ngx.INFO, "connecting to session client endpoint")
+
+ local client = ee_helpers.ws_proxy_client(opts)
+
+ ngx.log(ngx.INFO, "connected to session client endpoint")
+
+ ngx.log(ngx.INFO, "receiving handshake from upstream client")
+ server.client.sock:settimeouts(nil, nil, 500)
+ local data, typ, err = server:recv_frame()
+
+ assert.is_nil(err, "failed receiving initial connect frame: " .. tostring(err))
+ assert.equals("text", typ, "invalid initial frame type: " .. typ)
+ assert.equals("string", type(data), "invalid data returned from connection")
+ assert.truthy(#data > 0, "empty payload in initial connect frame")
+
+ local request = cjson.decode(data)
+
+ server.client.sock:settimeouts(nil, nil, opts.timeout)
+
+ ngx.log(ngx.INFO, "session initialized")
+
+ return setmetatable({
+ client = client,
+ server = server,
+ request = request,
+ id = server.id,
+ timeout = opts.timeout,
+ }, session)
+end
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/upstream.lua b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/upstream.lua
new file mode 100644
index 00000000..d60cc7f3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/fixtures/websocket/upstream.lua
@@ -0,0 +1,779 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cjson = require "cjson"
+local ws_server = require "resty.websocket.server"
+local utils = require "kong.tools.utils"
+local const = require "spec-ee.fixtures.websocket.constants"
+
+---
+-- WebSocket mock upstream fixture
+--
+-- # Features
+--
+-- ## echo server
+--
+-- You send it, it sends it back! Only exceptions to this are:
+--
+-- * ping: it responds with a pong
+-- * pong: it does nothing but emit a log entry
+--
+--
+-- ## session server
+--
+-- This allows you to use two websocket clients to emulate both sides of a
+-- client <-> upstream WS session:
+--
+-- 1. Connect a WS client to `/session/listen`
+-- * x-mock-websocket-request-id response header from step 1 contains a UUID
+-- * this is the "upstream" WS connection
+-- 2. Connect another WS client to `/session/client?session=$ID` using the UUID
+-- from step #1
+-- * this is the "client" WS connection
+-- 3. Call `recv_frame()` with the upstream WS client:
+-- * expect a text frame
+-- * text frame contains a json blob containing the client request details
+-- 4. Messages sent by each WS client are forwarded across shared memory
+--
+-- For convenience, `spec-ee.fixtures.websocket.session` takes care of all this
+-- setup work for you.
+
+local _M = {}
+
+local fmt = string.format
+local ngx = ngx
+local req = ngx.req
+local var = ngx.var
+local header = ngx.header
+local log = ngx.log
+local sleep = ngx.sleep
+local spawn = ngx.thread.spawn
+local wait = ngx.thread.wait
+local kill = ngx.thread.kill
+local on_abort = ngx.on_abort
+local encode = cjson.encode
+local decode = cjson.decode
+local find = string.find
+local ngx_now = ngx.now
+local update_time = ngx.update_time
+local min = math.min
+
+local INFO = ngx.INFO
+local NOTICE = ngx.NOTICE
+local WARN = ngx.WARN
+local ERR = ngx.ERR
+local DEBUG = ngx.DEBUG
+
+
+local HEADERS = const.headers
+local OPCODES = const.opcode
+
+local shm = assert(ngx.shared.kong_test_websocket_fixture,
+ "missing 'kong_test_websocket_fixture' shm declaration")
+
+
+local READ_TIMEOUT = 5
+local IDLE_TIMEOUT = 5
+local SESSION_TIMEOUT = 30
+local MAX_STEP = 0.5
+
+local function exit(status, body)
+ if ngx.get_phase() == "log" then
+ error(fmt("tried to call ngx.exit() in log phase: %s, status: %s",
+ body or "unknown", status))
+ end
+
+ if status == 444 then
+ return ngx.exit(444)
+ end
+
+ ngx.status = status
+ header["content-type"] = "application/json"
+ if type(body) == "table" then
+ body = cjson.encode(body)
+ end
+ ngx.say(body)
+ return ngx.exit(0)
+end
+
+local function substr(subj, s)
+ return type(subj) == "string"
+ and find(subj, s, nil, true)
+end
+
+local function is_timeout(err)
+ return substr(err, "timeout")
+end
+
+local function is_closed(err)
+ return substr(err, "closed")
+end
+
+local function is_fin(err)
+ return err ~= "again"
+end
+
+local function is_client_abort(err)
+ return substr(err, "client aborted")
+end
+
+local function is_reset(err)
+ return substr(err, "connection reset by peer")
+end
+
+local function now()
+ update_time()
+ return ngx_now()
+end
+
+---
+-- Some of our test cases cover things like "what happens when the NGINX worker
+-- is exiting?" Delaying the exit event here gives things a grace period so
+-- that our tests don't contend with the lifetime of the "upstream"
+local exiting
+do
+ local worker_exiting = ngx.worker.exiting
+ local exited
+ local timeout = 1
+
+ function exiting()
+ if not worker_exiting() then
+ return false
+ end
+
+ exited = exited or now()
+
+ local delay = now() - exited
+
+ if delay > timeout then
+ log(INFO, "delayed exit event by ", delay " seconds")
+ return true
+ end
+
+ return false
+ end
+end
+
+local function request_infos()
+ ---@class ws.request.info
+ local info = {
+ url = fmt("%s://%s:%s%s",
+ var.scheme, var.host,
+ var.server_port,
+ var.request_uri),
+ headers = req.get_headers(0),
+ headers_raw = req.get_headers(0, true),
+ query = req.get_uri_args(0),
+ method = req.get_method(),
+ uri = var.uri,
+ host = var.host,
+ hostname = var.hostname,
+ https = var.https,
+ scheme = var.scheme,
+ is_args = var.is_args,
+ server_addr = var.server_addr,
+ server_port = var.server_port,
+ server_name = var.server_name,
+ server_protocol = var.server_protocol,
+ remote_addr = var.remote_addr,
+ remote_port = var.remote_port,
+ realip_remote_addr = var.realip_remote_addr,
+ realip_remote_port = var.realip_remote_port,
+ binary_remote_addr = var.binary_remote_addr,
+ request = var.request,
+ request_uri = var.request_uri,
+ request_time = var.request_time,
+ request_length = var.request_length,
+ bytes_received = var.bytes_received,
+ ssl_client_s_dn = var.ssl_client_s_dn,
+ ssl_server_name = var.ssl_server_name,
+ }
+ return info
+end
+
+
+local function response_infos()
+ ---@class ws.response.info
+ local info = {
+ status = ngx.status,
+ headers = ngx.resp.get_headers(0),
+ }
+ return info
+end
+
+
+local NS = {
+ STATE = "state",
+ SESSION = "session",
+ HANDSHAKE = "handshake",
+ CLIENT = "client",
+ UPSTREAM = "upstream",
+}
+
+local PEER = setmetatable({
+ [NS.CLIENT] = NS.UPSTREAM,
+ [NS.UPSTREAM] = NS.CLIENT,
+}, {
+ __index = function(_, k)
+ error("unknown role: " .. tostring(k))
+ end
+})
+
+local function make_key(ns, id)
+ return ns .. "/" .. id
+end
+
+local function shm_push(ns, id, data)
+ local key = make_key(ns, id)
+ local ok, err = shm:rpush(key, data)
+ if not ok then
+ log(ERR, "failed writing data to shm: ", err)
+ return exit(444)
+ end
+end
+
+local function shm_pop(ns, id)
+ local key = make_key(ns, id)
+ local value, err = shm:lpop(key)
+
+ if err ~= nil then
+ log(ERR, "failed LPOP from ", key, ": ", err)
+ return exit(444)
+ end
+
+ return value
+end
+
+local function shm_get(ns, id)
+ local key = make_key(ns, id)
+ local value, err = shm:get(key)
+
+ if err then
+ log(ERR, "error while reading ", key, ": ", err)
+ return exit(444)
+ end
+
+ return value, err
+end
+
+local function shm_read(ns, id, method, timeout)
+ timeout = timeout or READ_TIMEOUT
+ local step = 0.01
+ local waited = 0
+ local start = now()
+
+ local get = method == "get" and shm_get or shm_pop
+
+ while true do
+ if exiting() then
+ return nil, "exiting"
+ end
+
+ local data = get(ns, id)
+
+ if data then
+ return data
+
+ elseif waited >= timeout then
+ break
+ end
+
+ sleep(step)
+ waited = now() - start
+ step = min(step * 1.25, MAX_STEP)
+ end
+
+ return nil, "timeout"
+end
+
+local function shm_add(ns, id, value, ttl)
+ ttl = ttl or READ_TIMEOUT
+ local key = make_key(ns, id)
+ local ok, err = shm:add(key, value, ttl)
+ if not ok then
+ log(ERR, "failed storing ", key, " to shm: ", err)
+ return exit(444)
+ end
+end
+
+local function shm_set(ns, id, state)
+ local key = make_key(ns, id)
+ local ok, err = shm:set(key, state, SESSION_TIMEOUT)
+ if not ok then
+ log(ERR, "failed shm:set ", key, ": ", err)
+ return exit(444)
+ end
+end
+
+local STATE = {
+ LISTEN = 1,
+ CONNECT = 2,
+ ACCEPT = 3,
+ PROXY = 4,
+ CLOSING = 5,
+ ABORT = 6,
+ CLOSED = 7,
+}
+
+local EOF = "eof"
+
+
+local function shm_transition(id, state, last)
+ local current = shm_get(NS.STATE, id)
+
+ if last then
+ assert(current == last, fmt("current state (%s) does not match expected (%s)",
+ current, last))
+ assert(state > current, fmt("invalid state change %s => %s", current, state))
+ else
+ current = current or 0
+ end
+
+ local diff = state - current
+ if diff == 0 then return end
+
+ local key = make_key(NS.STATE, id)
+ local new, err = shm:incr(key, diff)
+ assert(new ~= nil, fmt("state change shm operation failed: %s", err))
+ assert(new == state, fmt("state change %s => %s resulted in %s", current, state, new))
+end
+
+
+local function shm_await_state(id, state, timeout)
+ timeout = timeout or IDLE_TIMEOUT
+ local step = 0.01
+ local waited = 0
+ local start = now()
+
+ local init = shm_get(NS.STATE, id)
+
+ while true do
+ if exiting() then
+ return nil, "exiting"
+ end
+
+ local cur = shm_get(NS.STATE, id)
+
+ if cur == state then
+ log(DEBUG, fmt("waited %s seconds for state transition %s => %s",
+ waited, init, state))
+ return true
+
+ elseif cur and type(cur) ~= "number" then
+ log(ERR, "invalid ", NS.STATE, " value: ", cur)
+ break
+
+ elseif cur and cur > state then
+ log(ERR, NS.STATE, " is past expected: ", cur)
+ break
+
+ elseif waited >= timeout then
+ break
+ end
+
+ sleep(step)
+ waited = now() - start
+ step = min(step * 1.25, MAX_STEP)
+ end
+
+ log(ERR, "awaiting state change to ", state, " failed")
+ return exit(444)
+end
+
+
+local session = {
+ accept = function(id, timeout)
+ log(DEBUG, "session upstream LISTEN: ", id)
+ shm_add(NS.SESSION, id, encode({ idle_timeout = timeout }), SESSION_TIMEOUT)
+ shm_add(NS.STATE, id, STATE.LISTEN, SESSION_TIMEOUT)
+ shm_await_state(id, STATE.CONNECT)
+
+ log(DEBUG, "session upstream ACCEPT: ", id)
+ shm_transition(id, STATE.ACCEPT, STATE.CONNECT)
+ return shm_read(NS.HANDSHAKE, id, "get")
+ end,
+
+ connect = function(id, request)
+ log(DEBUG, "session client SESSION: ", id)
+ local data = shm_get(NS.SESSION, id)
+
+ if not data then
+ return exit(404, { error = fmt("session %s not found", id) })
+ end
+
+ local listen = decode(data)
+ if not listen then
+ return exit(500, { error = fmt("session %s data invalid: %s", id, data) })
+ end
+
+ log(DEBUG, "session client HANDSHAKE: ", id)
+ shm_add(NS.HANDSHAKE, id, encode(request))
+
+ log(DEBUG, "session client CONNECT: ", id)
+ shm_transition(id, STATE.CONNECT, STATE.LISTEN)
+ shm_await_state(id, STATE.ACCEPT)
+
+ return listen
+ end,
+
+ abort = function(id)
+ log(WARN, "session ABORT: ", id)
+ shm_set(NS.STATE, id, STATE.ABORT)
+ end,
+
+ aborted = function(id)
+ return shm_get(NS.STATE, id) == STATE.ABORT
+ end,
+
+ cleanup = function(role, id)
+ shm:delete(make_key(role, id))
+ end,
+
+ write = function(role, id, data)
+ shm_push(role, id, data)
+ end,
+
+ close = function(role, id)
+ shm_push(role, id, EOF)
+ end,
+}
+
+
+local function init_ws_server(ctx)
+ local ws, err = ws_server:new({
+ timeout = 5000,
+ max_payload_len = 2^31,
+ })
+
+ if not ws then
+ log(ERR, "failed creating websocket server: ", err)
+ return exit(444)
+ end
+
+ ctx.ws = ws
+end
+
+
+function _M.rewrite()
+ local ctx = ngx.ctx
+ ctx.request = request_infos()
+ local id = ctx.request.headers[HEADERS.id] or utils.uuid()
+
+ ctx.request_id = id
+ header[HEADERS.id] = id
+
+ -- for testing header forwarding
+ header[HEADERS.self] = "1"
+ header[HEADERS.multi] = { "one", "two" }
+
+ -- masquerade as mock_upstream
+ header["X-Powered-By"] = "mock_upstream"
+
+ -- allow the client to specify some response headers for us to send
+ for name, value in pairs(ctx.request.headers) do
+ name = ngx.re.gsub(name, "^x-mock-websocket-echo-(.+)", "$1", "oji")
+ if name then
+ header[name] = value
+ end
+ end
+end
+
+
+function _M.echo()
+ local ctx = ngx.ctx
+ init_ws_server(ctx)
+
+ log(INFO, "new echo server session")
+
+ ---@type resty.websocket.server
+ local ws = ctx.ws
+
+ local data, typ, sent, err
+
+ local closing = false
+
+ on_abort(function()
+ log(WARN, "handling client abort")
+ closing = true
+ end)
+
+ while not closing and not exiting() do
+ data, typ, err = ws:recv_frame()
+
+ if data then
+ if typ == "close" then
+ closing = true
+ sent, err = ws:send_close(err, data)
+
+ elseif typ == "binary" or typ == "text" then
+ if data == const.tokens.request then
+ data = encode(ctx.request)
+
+ elseif data == const.tokens.response then
+ data = encode(response_infos())
+ end
+
+ sent, err = ws:send_frame(is_fin(err), OPCODES[typ], data)
+
+ elseif typ == "ping" then
+ sent, err = ws:send_pong(data)
+
+ elseif typ == "pong" then
+ log(INFO, "client ponged: ", data)
+
+ else
+ log(ERR, "unhandled echo frame type: ", typ)
+ closing = true
+ end
+
+ elseif is_closed(err) or is_client_abort(err) then
+ log(ERR, "client aborted connection, exiting")
+ closing = true
+
+ elseif not is_timeout(err) then
+ log(ERR, "unexpected error while receiving frame: ", err)
+ closing = true
+ end
+
+ if not sent and not closing then
+ log(ERR, "failed sending echo frame: ", err)
+ closing = true
+ end
+ end
+
+ log(INFO, "echo server terminating...")
+end
+
+---
+-- Reads from shm and forwards downstream
+--
+---@param role string
+---@param id string
+---@param sock tcpsock
+---@param idle_timeout integer?
+local function shm_to_sock(role, id, sock, idle_timeout)
+ local msg, sent, err
+
+ local read_timeout = idle_timeout * 0.1
+ local last = now()
+
+ local peer = PEER[role]
+
+ while not exiting() do
+ msg, err = shm_read(role, id, "pop", read_timeout)
+
+ if msg == EOF then
+ log(INFO, role, " reached end of stream")
+ break
+
+ elseif msg then
+ log(DEBUG, "sock(", role, ") <- shm, len: ", #msg)
+ last = now()
+ sent, err = sock:send(msg)
+
+ if not sent then
+ log(ERR, "failed forwarding from shm: ", err)
+ break
+ end
+
+ elseif err == "timeout" then
+ local idle = now() - last
+
+ if idle > idle_timeout then
+ log(NOTICE, "reader session timed out")
+ break
+
+ elseif session.aborted(id) then
+ log(WARN, "peer (", peer, ") aborted connection")
+ break
+ end
+
+ elseif err == "exiting" then
+ break
+
+ else
+ log(ERR, "error while reading from shm: ", err or "unknown")
+ break
+ end
+ end
+
+ log(INFO, role, " shm_to_sock exiting")
+
+ session.cleanup(role, id)
+
+ return "reader"
+end
+
+---
+-- Reads from a socket and writes to shm
+--
+---@param role string
+---@param id string
+---@param sock tcpsock
+---@param timeout integer?
+local function sock_to_shm(role, id, sock, timeout)
+ local last = now()
+
+ local peer = PEER[role]
+
+ while not exiting() do
+ sock:settimeout(timeout * 1000)
+ local data, err = sock:receiveany(1024 * 128)
+
+ if data then
+ last = now()
+
+ log(DEBUG, "sock(", role, ") -> shm, len: ", #data)
+ session.write(peer, id, data)
+
+ elseif is_client_abort(err) then
+ log(WARN, "sock_to_shm ", role, " abort")
+ session.abort(id)
+ break
+
+ elseif is_timeout(err) then
+ local idle = now() - last
+ if idle > timeout then
+ log(ERR, role, " reached idle timeout")
+ break
+
+ elseif session.aborted(id) then
+ log(WARN, peer, " aborted connection")
+ break
+ end
+
+ elseif is_closed(err) or is_reset(err) then
+ break
+
+ else
+ log(ERR, "unexpected sock:receiveany() error: ", err)
+ break
+ end
+ end
+
+ log(INFO, role, " sock_to_shm exiting")
+
+ session.close(peer, id)
+
+ return "writer"
+end
+
+---@param role string
+---@param sock tcpsock
+---@param id string
+local function pipe(role, sock, id, idle_timeout)
+ local reader = spawn(shm_to_sock, role, id, sock, idle_timeout)
+ local writer = spawn(sock_to_shm, role, id, sock, idle_timeout)
+
+ local _, res = wait(reader, writer)
+
+ local abort = session.aborted(id)
+
+ local term = abort
+ and kill
+ or wait
+
+ if res == "reader" then
+ term(writer)
+
+ elseif res == "writer" then
+ term(reader)
+
+ else
+ log(ERR, "thread exited with error: ", res)
+ kill(reader)
+ kill(writer)
+ end
+
+ log(INFO, "closing ", role, " session...")
+
+ if abort then
+ exit(444)
+ end
+end
+
+
+---
+-- Upsream/Listener side of a WS session
+function _M.listen()
+ local t = var.arg_idle_timeout
+ if t and not tonumber(t) then
+ return exit(400, { error = "invalid idle_timeout: " .. t })
+ end
+
+ t = t and tonumber(t) and tonumber(t) / 1000
+ t = t or IDLE_TIMEOUT
+
+ local ctx = ngx.ctx
+ init_ws_server(ctx)
+
+ local id = ctx.request_id
+
+ local data, err = session.accept(id, t)
+
+ if err == "timeout" then
+ log(ERR, "timed out waiting for client connection")
+ return
+
+ elseif not data then
+ log(ERR, "error reading from shm while waiting for client: ", err)
+ return
+ end
+
+ assert(ctx.ws:send_text(encode(data)))
+
+ pipe(NS.UPSTREAM, ctx.ws.sock, id, t)
+end
+
+
+---
+-- Client side of a WS session
+function _M.client()
+ local id = var.arg_session
+ if not id or id == "" then
+ return exit(400, { error = "session query arg is required" })
+ end
+
+ local ctx = ngx.ctx
+ local listen = session.connect(id, ctx.request)
+
+ init_ws_server(ctx)
+
+ pipe(NS.CLIENT, ctx.ws.sock, id, listen.idle_timeout)
+end
+
+
+function _M.get_log()
+ local id = var.log_id
+ local timeout = tonumber(var.arg_timeout) or 1
+
+ local entry = shm_read("log", id, "get", timeout)
+
+ if not entry then
+ return exit(404, {
+ error = fmt("log for request %s not found", id),
+ })
+ end
+
+ return exit(200, entry)
+end
+
+
+function _M.log_to_shm()
+ local id = header[HEADERS.id] or ngx.req.get_headers()[HEADERS.id]
+
+ if not id then
+ log(ngx.WARN, "Request with no ", HEADERS.id, " request/response header")
+ return
+ end
+
+ local entry = cjson.encode(kong.log.serialize())
+ shm_set("log", id, entry)
+end
+
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/helpers.lua b/kong-versions/test9.9.9.3/kong/spec-ee/helpers.lua
new file mode 100644
index 00000000..17ca735a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/helpers.lua
@@ -0,0 +1,1185 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+------------------------------------------------------------------
+-- Collection of utilities to help testing Kong-Enterprise features and plugins.
+--
+-- @copyright Copyright 2016-2022 Kong Inc. All rights reserved.
+-- @module spec-ee.helpers
+-- @usage
+-- local helpers = require 'spec.helpers'
+-- local eehelpers = require 'spec-ee.helpers'
+
+local helpers = require "spec.helpers"
+local listeners = require "kong.conf_loader.listeners"
+local cjson = require "cjson.safe"
+local assert = require "luassert"
+local utils = require "kong.tools.utils"
+local admins_helpers = require "kong.enterprise_edition.admins_helpers"
+local pl_file = require "pl.file"
+
+
+local _M = {}
+
+--- Returns Redis Cluster nodes list.
+-- The list can be configured in environment variable `KONG_SPEC_TEST_REDIS_CLUSTER_ADDRESSES`.
+-- @function parsed_redis_cluster_addresses
+-- @treturn table nodes list
+-- @usage
+-- ~ $ export KONG_SPEC_TEST_REDIS_CLUSTER_ADDRESSES=node-1:6379,node-2:6379,node-3:6379
+--
+-- local redis_cluster_addresses = parsed_redis_cluster_addresses()
+function _M.parsed_redis_cluster_addresses()
+ local env_cluster_addresses = os.getenv("KONG_SPEC_TEST_REDIS_CLUSTER_ADDRESSES")
+
+ -- default
+ if not env_cluster_addresses then
+ return { "localhost:7000", "localhost:7001", "localhost:7002" }
+ end
+
+ local redis_cluster_addresses = {}
+ for node in string.gmatch(env_cluster_addresses, "[^,]+") do
+ table.insert(redis_cluster_addresses, node)
+ end
+
+ return redis_cluster_addresses
+end
+
+--- Registers RBAC resources.
+-- @param db db db object (see `spec.helpers.get_db_utils`)
+-- @param ws_name (optional)
+-- @param ws_table (optional)
+-- @return on success: `super_admin, super_user_role`
+-- @return on failure: `nil, nil, err`
+function _M.register_rbac_resources(db, ws_name, ws_table)
+ local bit = require "bit"
+ local rbac = require "kong.rbac"
+ local bxor = bit.bxor
+
+ local opts = ws_table and { workspace = ws_table.id }
+
+ -- action int for all
+ local action_bits_all = 0x0
+ for k, v in pairs(rbac.actions_bitfields) do
+ action_bits_all = bxor(action_bits_all, rbac.actions_bitfields[k])
+ end
+
+ local roles = {}
+ local err, _
+ -- now, create the roles and assign endpoint permissions to them
+
+ -- first, a read-only role across everything
+ roles.read_only, err = db.rbac_roles:insert({
+ id = utils.uuid(),
+ name = "read-only",
+ comment = "Read-only access across all initial RBAC resources",
+ }, opts)
+
+ if err then
+ return nil, nil, err
+ end
+
+ -- this role only has the 'read-only' permissions
+ _, err = db.rbac_role_endpoints:insert({
+ role = { id = roles.read_only.id, },
+ workspace = ws_name or "*",
+ endpoint = "*",
+ actions = rbac.actions_bitfields.read,
+ })
+
+ ws_name = ws_name or "default"
+
+ if err then
+ return nil, nil, err
+ end
+
+ -- admin role with CRUD access to all resources except RBAC resource
+ roles.admin, err = db.rbac_roles:insert({
+ id = utils.uuid(),
+ name = "admin",
+ comment = "CRUD access to most initial resources (no RBAC)",
+ }, opts)
+
+ if err then
+ return nil, nil, err
+ end
+
+ -- the 'admin' role has 'full-access' + 'no-rbac' permissions
+ _, err = db.rbac_role_endpoints:insert({
+ role = { id = roles.admin.id, },
+ workspace = "*",
+ endpoint = "*",
+ actions = action_bits_all, -- all actions
+ })
+
+ if err then
+ return nil, nil, err
+ end
+
+ local rbac_endpoints = { '/rbac/*', '/rbac/*/*', '/rbac/*/*/*', '/rbac/*/*/*/*', '/rbac/*/*/*/*/*', '/admins', '/admins/*', '/groups', '/groups/*' }
+ for _, endpoint in ipairs(rbac_endpoints) do
+ _, err = db.rbac_role_endpoints:insert({
+ role = { id = roles.admin.id, },
+ workspace = "*",
+ endpoint = endpoint,
+ negative = true,
+ actions = action_bits_all, -- all actions
+ })
+
+ if err then
+ return nil, nil, err
+ end
+ end
+
+ -- finally, a super user role who has access to all initial resources
+ roles.super_admin, err = db.rbac_roles:insert({
+ id = utils.uuid(),
+ name = "super-admin",
+ comment = "Full CRUD access to all initial resources, including RBAC entities",
+ }, opts)
+
+ if err then
+ return nil, nil, err
+ end
+
+ _, err = db.rbac_role_entities:insert({
+ role = { id = roles.super_admin.id, },
+ entity_id = "*",
+ entity_type = "wildcard",
+ actions = action_bits_all, -- all actions
+ })
+
+ if err then
+ return nil, nil, err
+ end
+
+ _, err = db.rbac_role_endpoints:insert({
+ role = { id = roles.super_admin.id, },
+ workspace = "*",
+ endpoint = "*",
+ actions = action_bits_all, -- all actions
+ })
+
+ if err then
+ return nil, nil, err
+ end
+
+ local super_admin, err = db.rbac_users:insert({
+ id = utils.uuid(),
+ name = "super_gruce-" .. ws_name,
+ user_token = "letmein-" .. ws_name,
+ enabled = true,
+ comment = "Test - Initial RBAC Super Admin User"
+ }, opts)
+
+ if err then
+ return nil, nil, err
+ end
+
+ local super_user_role, err = db.rbac_user_roles:insert({
+ user = super_admin,
+ role = roles.super_admin,
+ })
+
+ if err then
+ return nil, nil, err
+ end
+
+ return super_admin, super_user_role
+end
+
+
+--- Returns the Dev Portal port.
+-- Throws an error if not found in the configuration.
+-- @tparam[opt=false] boolean ssl if `true` returns the ssl port
+-- @treturn number the port
+function _M.get_portal_api_port(ssl)
+ if ssl == nil then ssl = false end
+ for _, entry in ipairs(_M.portal_api_listeners) do
+ if entry.ssl == ssl then
+ return entry.port
+ end
+ end
+ error("No portal port found for ssl=" .. tostring(ssl), 2)
+end
+
+
+--- Returns the Dev Portal ip.
+-- Throws an error if not found in the configuration.
+-- @tparam[opt=false] boolean ssl if `true` returns the ssl ip
+-- @treturn string the ip address
+function _M.get_portal_api_ip(ssl)
+ if ssl == nil then ssl = false end
+ for _, entry in ipairs(_M.portal_api_listeners) do
+ if entry.ssl == ssl then
+ return entry.ip
+ end
+ end
+ error("No portal ip found for ssl=" .. tostring(ssl), 2)
+end
+
+
+--- Returns the Dev Portal port.
+-- Throws an error if not found in the configuration.
+-- @tparam[opt=false] boolean ssl if `true` returns the ssl port
+-- @treturn number the port
+function _M.get_portal_gui_port(ssl)
+ if ssl == nil then ssl = false end
+ for _, entry in ipairs(_M.portal_gui_listeners) do
+ if entry.ssl == ssl then
+ return entry.port
+ end
+ end
+ error("No portal port found for ssl=" .. tostring(ssl), 2)
+end
+
+
+--- Returns the Dev Portal ip.
+-- Throws an error if not found in the configuration.
+-- @tparam[opt=false] boolean ssl if `true` returns the ssl ip
+-- @treturn string the ip address
+function _M.get_portal_gui_ip(ssl)
+ if ssl == nil then ssl = false end
+ for _, entry in ipairs(_M.portal_gui_listeners) do
+ if entry.ssl == ssl then
+ return entry.ip
+ end
+ end
+ error("No portal ip found for ssl=" .. tostring(ssl), 2)
+end
+
+
+--- returns a pre-configured `http_client` for the Dev Portal API.
+-- @tparam number timeout the timeout to use
+-- the Kong configuration with this port
+-- @return http-client, see `spec.helpers.http_client`.
+function _M.portal_api_client(timeout)
+ local portal_ip = _M.get_portal_api_ip()
+ local portal_port = _M.get_portal_api_port()
+ assert(portal_ip, "No portal_ip found in the configuration")
+ return helpers.http_client(portal_ip, portal_port, timeout)
+end
+
+
+--- returns a pre-configured `http_client` for the Dev Portal GUI.
+-- @tparam number timeout the timeout to use
+-- the Kong configuration with this port
+-- @return http-client, see `spec.helpers.http_client`.
+function _M.portal_gui_client(timeout)
+ local portal_ip = _M.get_portal_gui_ip()
+ local portal_port = _M.get_portal_gui_port()
+ assert(portal_ip, "No portal_ip found in the configuration")
+ return helpers.http_client(portal_ip, portal_port, timeout)
+end
+
+-- TODO: remove this, the clients already have a post helper method...
+function _M.post(client, path, body, headers, expected_status)
+ headers = headers or {}
+ headers["Content-Type"] = "application/json"
+ local res = assert(client:send{
+ method = "POST",
+ path = path,
+ body = body or {},
+ headers = headers
+ })
+ return cjson.decode(assert.res_status(expected_status or 201, res))
+end
+
+
+--- Creates a new Admin user.
+-- The returned admin will have the rbac token set in field `rbac_user.raw_user_token`. This
+-- is only for test purposes and should never be done outside the test environment.
+-- @param email email address
+-- @param custom_id custom id to use
+-- @param status admin status
+-- @param db db object (see `spec.helper.get_db_utils`)
+-- @param username username
+-- @param workspace workspace
+-- @return The admin object created, or `nil + err` on failure to get the token
+-- @usage
+-- local admin = eehelpers.create_admin(...)
+-- local admin_token = admin.rbac_user.raw_user_token
+function _M.create_admin(email, custom_id, status, db, username, workspace)
+ local opts = workspace and { workspace = workspace.id }
+
+ local admin = assert(db.admins:insert({
+ username = username or email,
+ custom_id = custom_id,
+ email = email,
+ status = status,
+ }, opts))
+
+ local token_res, err = admins_helpers.update_token(admin)
+ if err then
+ return nil, err
+ end
+
+ -- only used for tests so we can reference token
+ -- WARNING: do not do this outside test environment
+ admin.rbac_user.raw_user_token = token_res.body.token
+
+ return admin
+end
+
+-- add a retry logic for CI
+local function get_auth(client, username, password, retry)
+ if not client then
+ client = helpers.admin_client()
+ end
+ local res, err = assert(client:send {
+ method = "GET",
+ path = "/auth",
+ headers = {
+ ["Authorization"] = "Basic " .. ngx.encode_base64(username .. ":"
+ .. password),
+ ["Kong-Admin-User"] = username,
+ }
+ })
+
+ if err and err:find("closed", nil, true) and not retry then
+ client = nil
+ return get_auth(client, username, password, true)
+ end
+ assert.is_nil(err, "failed GET /auth: " .. tostring(err))
+ assert.res_status(200, res)
+ return res
+end
+
+--- returns the cookie for the admin.
+-- @param client the http-client to use to make the auth request
+-- @param username the admin user name to get the cookie for
+-- @param password the password for the admin user
+-- @return the cookie value, as returned in the `Set-Cookie` response header.
+function _M.get_admin_cookie_basic_auth(client, username, password)
+ local res = get_auth(client, username, password)
+ return res.headers["Set-Cookie"]
+end
+
+--- Sets up the oauth introspection fixture.
+-- This generates a fixture. The ip+port+path is used to generate the nginx directives
+-- `listen` and `location` in the fixture/mock.
+-- @tparam[opt] string ip the ip address, default `"127.0.0.1"`
+-- @tparam[opt] number port the port, default `10000`
+-- @tparam[opt] string path the path, default `"/introspect"`
+-- @return fixture + url, where url is build from the input ip/port/path, and fixture is a table with an `http_mock` that
+-- can be used when calling `spec.helpers.start_kong`.
+function _M.setup_oauth_introspection_fixture(ip, port, path)
+ path = path or "/introspect"
+ ip = ip or "127.0.0.1"
+ port = port or "10000"
+
+ local introspection_url = ("http://%s:%s%s"):format(
+ ip, port, path)
+ local fixtures = {
+ http_mock = {
+ mock_introspection = [=[
+ server {
+ server_name mock_introspection;
+ listen ]=] .. port .. [=[;
+ location ~ "]=] .. path .. [=[" {
+ content_by_lua_block {
+ local function x()
+
+ ngx.req.set_header("Content-Type", "application/json")
+
+ if ngx.req.get_method() == "POST" then
+ ngx.req.read_body()
+ local args = ngx.req.get_post_args()
+ if not args then
+ return ngx.exit(500)
+ end
+ if args.token == "valid" or
+ args.token == "valid_consumer_client_id" or
+ args.token == "valid_consumer_client_id_not_added_initially" or
+ args.token == "valid_consumer" or
+ args.token == "valid_consumer_limited" or
+ args.token == "valid_expired" or
+ args.token == "invalid_with_errors" or
+ args.token == "invalid_without_errors" or
+ args.token == "valid_complex" then
+
+ if args.token == "valid_consumer" then
+ ngx.say([[{"active":true,
+ "username":"bob"}]])
+ elseif args.token == "valid_consumer_client_id" then -- omit `username`, return `client_id`
+ ngx.say([[{"active":true,
+ "client_id": "kongsumer"}]])
+ elseif args.token == "valid_consumer_client_id_not_added_initially" then -- omit `username`, return `client_id`
+ ngx.say([[{"active":true,
+ "client_id": "kongsumer_not_added_initially"}]])
+ elseif args.token == "valid_consumer_limited" then
+ ngx.say([[{"active":true,
+ "username":"limited-bob"}]])
+ elseif args.token == "valid_complex" then
+ ngx.say([[{"active":true,
+ "username":"some_username",
+ "client_id":"some_client_id",
+ "scope":"some_scope",
+ "sub":"some_sub",
+ "aud":"some_aud",
+ "iss":"some_iss",
+ "exp":"99999999999",
+ "iat":"some_iat",
+ "foo":"bar",
+ "bar":"baz",
+ "baz":"baaz"}]])
+ elseif args.token == "valid_expired" then
+ ngx.say([[{"active":true,
+ "exp":"1"}]])
+ elseif args.token == "invalid_with_errors" then
+ ngx.say([[{"active":false, "error":"dummy error", "error_description": "dummy error desc"}]])
+ elseif args.token == "invalid_without_errors" then
+ ngx.say([[{"active":false}]])
+ else
+ ngx.say([[{"active":true}]])
+ end
+ return ngx.exit(200)
+ end
+ end
+
+ ngx.say([[{"active":false}]])
+ return ngx.exit(200)
+
+ end
+ local ok, err = pcall(x)
+ if not ok then
+ ngx.log(ngx.ERR, "Mock error: ", err)
+ end
+ }
+ }
+ }
+ ]=]
+ },
+ }
+ return fixtures, introspection_url
+end
+
+
+
+
+do
+ local resty_ws_client = require "resty.websocket.client"
+ local ws = require "spec-ee.fixtures.websocket"
+ local ws_const = require "spec-ee.fixtures.websocket.constants"
+ local inspect = require "inspect"
+
+ local function response_status(res)
+ if type(res) ~= "string" then
+ error("expected response data as a string", 2)
+ end
+
+ -- 123456789012345678901234567890
+ -- 000000000111111111122222222223
+ -- HTTP/1.1 301 Moved Permanently
+ local version = tonumber(res:sub(6, 8))
+ if not version then
+ return nil, "failed parsing HTTP response version"
+ end
+
+ local status = tonumber(res:sub(10, 12))
+ if not status then
+ return nil, "failed parsing HTTP response status"
+ end
+
+ local reason = res:match("[^\r\n]+", 14)
+
+ return status, version, reason
+ end
+
+ local headers_mt = {
+ __index = function(self, k)
+ return rawget(self, k:lower())
+ end,
+
+ __newindex = function(self, k, v)
+ return rawset(self, k:lower(), v)
+ end,
+ }
+
+
+ local function add_header(t, name, value)
+ if not name or not value then
+ return
+ end
+
+ if t[name] then
+ value = { t[name], value }
+ end
+ t[name] = value
+ end
+
+
+ local function response_headers(res)
+ if type(res) ~= "string" then
+ return nil, "expected response data as a string"
+ end
+
+ local seen_status_line = false
+
+ local headers = setmetatable({}, headers_mt)
+
+ for line in res:gmatch("([^\r\n]+)") do
+ if seen_status_line then
+ local name, value = line:match([[^([^:]+):%s*(.+)]])
+
+ add_header(headers, name, value)
+ else
+ seen_status_line = true
+ end
+ end
+
+ return headers
+ end
+
+ -- format WebSocket request headers
+ --
+ -- This function accepts headers in both forms:
+ --
+ -- * hash-like: { name = "value" }
+ -- * array-like: { "name: value" }
+ --
+ -- ...and formats them into { "name: value" } for lua-resty-websocket
+ --
+ local function format_request_headers(headers)
+ if not headers then return end
+
+ local t = {}
+
+ for i = 1, #headers do
+ t[i] = headers[i]
+ headers[i] = nil
+ end
+ for k, v in pairs(headers) do
+ if type(v) == "table" then
+ for _, val in ipairs(v) do
+ table.insert(t, k .. ": " .. val)
+ end
+ else
+ table.insert(t, k .. ": " .. v)
+ end
+ end
+
+ if #t == 0 then return end
+ return t
+ end
+
+ local fmt = string.format
+
+ local function handle_failure(params, uri, err, res, id)
+ local msg = {
+ "WebSocket handshake failed!",
+ "--- Request URI: " .. uri,
+ "--- Request Params:", inspect(params),
+ "--- Error: ", err or "unknown error",
+ "--- Response:", res or "",
+ }
+
+ -- attempt to retrieve the request ID from the request or response headers
+ local header = ws_const.headers.id
+ id = id or
+ params and
+ params.headers and
+ params.headers[header] or
+ (response_headers(res) or {})[header]
+
+ if id then
+ table.insert(msg, "--- Request ID: " .. id)
+ local log = ws.get_session_log(id)
+ if log then
+ table.insert(msg, "--- kong.log.serialize():")
+ table.insert(msg, inspect(log))
+ end
+ end
+
+ table.insert(msg, "---")
+ assert(nil, table.concat(msg, "\n\n"))
+ end
+
+
+ -- param client ws.test.client
+ local function body_reader(client)
+ -- param res ws.test.client.response
+ return function(res)
+ if res._cached_body then
+ return res._cached_body
+ end
+
+ local body = ""
+ local err
+
+ local status = res.original_status or res.status
+ local content_length = tonumber(res.headers["content-length"])
+
+ local sock = client.client.sock
+
+ if status == 101 then
+ -- simulate HTTP mock upstream
+ body = client:get_raw_request()
+
+ elseif content_length then
+ sock:settimeout(1000)
+ body, err = sock:receive(content_length)
+ sock:close()
+
+ else
+ sock:close()
+ end
+
+ -- cache the result so :read_body() can be called multiple times
+ res._cached_body = body or ""
+
+ return body, err
+ end
+ end
+
+ local OPCODES = ws_const.opcode
+
+ -- param client resty.websocket.client
+ -- param data string
+ -- return boolean ok
+ -- return string? error
+ local function init_fragment(client, opcode, data)
+ return client:send_frame(false, opcode, data)
+ end
+
+ -- param client resty.websocket.client
+ -- param data string
+ -- return boolean ok
+ -- return string? error
+ local function continue_fragment(client, data)
+ return client:send_frame(false, OPCODES.continuation, data)
+ end
+
+ -- param client resty.websocket.client
+ -- param data string
+ -- return boolean ok
+ -- return string? error
+ local function finish_fragment(client, data)
+ return client:send_frame(true, OPCODES.continuation, data)
+ end
+
+ -- param client resty.websocket.client
+ -- param typ '"text"'|'"binary"'
+ -- param data string[]
+ -- return boolean ok
+ -- return string? error
+ local function send_fragments(client, typ, data)
+ assert(typ == "text" or typ == "string",
+ "attempt to fragment non-data frame")
+
+ local opcode = OPCODES[typ]
+ local ok, err
+ local len = #data
+ for i = 1, len do
+ local first = i == 1
+ local last = i == len
+
+ local payload = data[i]
+
+ -- single length: just send a single frame
+ if first and last then
+ ok, err = client:send_frame(true, opcode, payload)
+
+ -- first frame: init fragment
+ elseif first then
+ ok, err = init_fragment(client, opcode, payload)
+
+ -- last frame: finish fragment
+ elseif last then
+ ok, err = finish_fragment(client, payload)
+
+ -- in the middle: continue
+ else
+ ok, err = continue_fragment(client, payload)
+ end
+
+ if not ok then
+ return nil, fmt("failed sending %s fragment %s/%s: %s",
+ typ, i, len, err)
+ end
+ end
+
+ return true
+ end
+
+ -- @class ws.test.client.response : table
+ -- @field status number
+ -- @field reason string
+ -- @field version number
+ -- @field headers table
+ -- @field read_body function
+
+ -- @class ws.test.client
+ -- @field client resty.websocket.client
+ -- @field id string
+ -- @field response ws.test.client.response
+ local ws_client = {}
+
+ -- param data string|string[]
+ -- return boolean ok
+ -- return string? error
+ function ws_client:send_text(data)
+ if type(data) == "table" then
+ return send_fragments(self.client, "text", data)
+ end
+
+ return self.client:send_text(data)
+ end
+
+ -- param data string|string[]
+ -- return boolean ok
+ -- return string? error
+ function ws_client:send_binary(data)
+ if type(data) == "table" then
+ return send_fragments(self.client, "binary", data)
+ end
+
+ return self.client:send_binary(data)
+ end
+
+ -- param data string
+ -- return boolean ok
+ -- return string? error
+ function ws_client:init_text_fragment(data)
+ return init_fragment(self.client, OPCODES.text, data)
+ end
+
+ -- param data string
+ -- return boolean ok
+ -- return string? error
+ function ws_client:init_binary_fragment(data)
+ return init_fragment(self.client, OPCODES.binary, data)
+ end
+
+ -- param data string
+ -- return boolean ok
+ -- return string? error
+ function ws_client:send_continue(data)
+ return continue_fragment(self.client, data)
+ end
+
+ -- param data string
+ -- return boolean ok
+ -- return string? error
+ function ws_client:send_final_fragment(data)
+ return finish_fragment(self.client, data)
+ end
+
+
+ -- param data? string
+ -- return boolean ok
+ -- return string? error
+ function ws_client:send_ping(data)
+ return self.client:send_ping(data)
+ end
+
+ -- param data? string
+ -- return boolean ok
+ -- return string? error
+ function ws_client:send_pong(data)
+ return self.client:send_pong(data)
+ end
+
+ -- param data? string
+ -- param status? integer
+ -- return boolean ok
+ -- return string? error
+ function ws_client:send_close(data, status)
+ return self.client:send_close(status, data)
+ end
+
+ function ws_client:send_frame(...)
+ return self.client:send_frame(...)
+ end
+
+ -- return string? data
+ -- return string? type
+ -- return string|number|nil err
+ function ws_client:recv_frame()
+ return self.client:recv_frame()
+ end
+
+ -- unlike resty.websocket.client, this does _not_ attempt to send
+ -- a close frame
+ -- return boolean ok
+ -- return string? error
+ function ws_client:close()
+ return self.client.sock:close()
+ end
+
+ -- fetch the raw handshake request data (as seen by the mock upstream)
+ -- return string
+ function ws_client:get_raw_request()
+ if self._request then
+ return self._request
+ end
+
+ local sent, err = self:send_text(ws_const.tokens.request)
+ assert.truthy(sent, "failed sending $_REQUEST text frame: " .. tostring(err))
+
+ local data, typ, status = self:recv_frame()
+ assert.truthy(data, "failed receiving request data: " .. tostring(status))
+ assert.equals("text", typ, "wrong message type for request: " .. typ)
+
+ self._request = data
+ return data
+ end
+
+ -- fetch and decode handshake request data (as seen by the mock upstream)
+ -- return table
+ function ws_client:get_request()
+ local data = self:get_raw_request()
+ local req = assert(cjson.decode(data))
+
+ local headers = setmetatable({}, headers_mt)
+ for k, v in pairs(req.headers) do
+ headers[k] = v
+ end
+ req.headers = headers
+
+ return req
+ end
+
+ ws_client.__index = ws_client
+
+
+ --- Instantiate a WebSocket client
+ -- @tparam table opts options table
+ -- @tparam string opts.path the path
+ -- @tparam table opts.query table with query args
+ -- @tparam string opts.scheme either '"ws"'|'"wss"'
+ -- @tparam number opts.port port
+ -- @tparam string opts.addr address
+ -- @tparam bool opts.fail_on_error boolean fail on error
+ -- @tparam number opts.connect_timeout connect timeout
+ -- @tparam number opts.write_timeout write timeout
+ -- @tparam number opts.read_timeout read timeout
+ -- @tparam number opts.timeout generic timeout if others not given
+ -- @return websocket client
+ function _M.ws_client(opts)
+ opts = opts or {}
+
+ local query = opts.query or {}
+ local scheme = opts.scheme or "ws"
+
+ local port = opts.port
+ if not port then
+ port = (scheme == "wss" and 443) or 80
+ end
+
+ local client, err = resty_ws_client:new({ max_payload_len = 2^31 })
+ assert(client, err)
+
+ local qs = ngx.encode_args(query)
+ if qs and qs ~= "" then qs = "?" .. qs end
+
+ local uri = fmt("%s://%s:%s%s%s",
+ scheme,
+ opts.addr or opts.host or "127.0.0.1",
+ port,
+ opts.path or "/",
+ qs
+ )
+
+ if opts.connect_timeout or opts.write_timeout or opts.read_timeout then
+ client.sock:settimeouts(opts.connect_timeout,
+ opts.write_timeout,
+ opts.read_timeout)
+ elseif opts.timeout then
+ client.sock:settimeout(opts.timeout)
+ end
+
+ local id = opts.headers and opts.headers[ws_const.headers.id]
+
+ local params = {
+ host = opts.host or opts.addr or "127.0.0.1",
+ origin = opts.origin,
+ key = opts.key,
+ server_name = opts.server_name or opts.host or opts.addr,
+ keep_response = true,
+ headers = format_request_headers(opts.headers),
+ client_cert = opts.client_cert,
+ client_priv_key = opts.client_priv_key,
+ }
+
+ local ok, res
+ ok, err, res = client:connect(uri, params)
+
+ if opts.fail_on_error and (not ok or err ~= nil) then
+ handle_failure(params, uri, err, res, id)
+ end
+
+ assert.is_not_nil(res, "resty.websocket.client:connect() returned no response data")
+
+ local status, version, reason = response_status(res)
+ assert.not_nil(status, version)
+
+ local self = setmetatable({
+ client = client,
+ response = {
+ status = status,
+ reason = reason,
+ version = version,
+ headers = response_headers(res),
+ }
+ }, ws_client)
+
+
+ -- without this function the response modifier won't think this is
+ -- a valid response object
+ self.response.read_body = body_reader(self)
+
+ self.id = id or self.response.headers[ws_const.headers.id]
+
+ return self
+ end
+
+ --- Establish a WebSocket connection to Kong.
+ -- The defaults take the `opts.scheme` into account and will automatically
+ -- pick either the plain or ssl based details.
+ -- @tparam table opts same table as `ws_client`, but has defaults for the following fields;
+ -- @tparam number opts.port port, defaults to Kong proxy port
+ -- @tparam string opts.addr address, defaults to Kong proxy ip
+ -- @tparam bool opts.fail_on_error boolean fail on error, defaults to `true`
+ -- @return websocket client
+ function _M.ws_proxy_client(opts)
+ opts = opts or {}
+ local ssl = opts.scheme == "wss"
+
+ if not opts.addr then
+ opts.addr = helpers.get_proxy_ip(ssl)
+ end
+
+ if not opts.port then
+ opts.port = helpers.get_proxy_port(ssl)
+ end
+
+ if opts.fail_on_error ~= false then
+ opts.fail_on_error = true
+ end
+
+ return assert(_M.ws_client(opts))
+ end
+
+
+ -- A client object that is loosely compatible with `helpers.proxy_client`
+ -- but is WebSocket-aware.
+ --
+ -- This is mostly useful for tests that need to validate request/response
+ -- data (i.e. auth plugins) and is not intended for WebSocket-centric tests
+ local ws_compat_client = {}
+
+ function ws_compat_client:send(params)
+ if params.method then
+ assert.equals("GET", params.method, "only GET is supported")
+ params.method = nil
+ end
+
+ do
+ local host, host_key
+ for k, v in pairs(params.headers or {}) do
+ if k:lower() == "host" then
+ host = v
+ host_key = k
+ break
+ end
+ end
+
+ if host_key then
+ params.headers[host_key] = nil
+ end
+
+ params.host = host
+ end
+
+ if not params.force_path then
+ -- this saves me from having to update lots and lots of tests
+ local path = params.path or "/"
+
+ local qs = path:find("?", 1, true)
+ if qs then
+ params.query = ngx.decode_args( (path:sub(qs + 1)) )
+ path = path:sub(1, qs - 1)
+ end
+
+ params.path = path
+ end
+
+ params.fail_on_error = false
+
+ if self.ssl then
+ params.ssl = true
+ end
+
+ local client = _M.ws_proxy_client(params)
+ assert.not_nil(client)
+
+ local response = client.response
+
+ if response.status == 101 then
+ assert.not_nil(response.headers[ws_const.headers.self],
+ ws.const.headers.self .. " header is missing. " ..
+ "The request was not routed to the proper route/service")
+ client:get_request()
+ client:send_close()
+ client:close()
+
+ -- many existing tests check for a 200 status code
+ --
+ -- monkey-patch it so that we don't have to update everything
+ response.status = 200
+ response.original_status = 101
+ else
+
+ -- read the body once (this ensures that the underlying socket is closed)
+ local body, err = response:read_body()
+ assert.not_nil(body, "failed reading non-101 websocket response body: ", err)
+ end
+
+ return client.response
+ end
+
+ function ws_compat_client:get(path, params)
+ params.path = path
+ params.method = "GET"
+ return self:send(params)
+ end
+
+ function ws_compat_client:close()
+ return true
+ end
+
+ setmetatable(ws_compat_client, {
+ __index = function(_, k)
+ error("method " .. tostring(k) .. " is NYI")
+ end,
+ })
+
+
+
+ --- A client object that is loosely compatible with `spec.helpers.proxy_client`
+ -- but is WebSocket-aware.
+ --
+ -- This is mostly useful for tests that need to validate request/response
+ -- data (i.e. auth plugins) and is not intended for WebSocket-centric tests
+ --
+ -- See `spec-ee.helpers.each_protocol`
+ function _M.ws_proxy_client_compat()
+ return setmetatable({ ssl = false }, { __index = ws_compat_client })
+ end
+
+ --- A client for wss. Same as the WS one, but for WSS.
+ -- See `spec-ee.helpers.ws_proxy_client_compat` and `spec-ee.helpers.each_protocol`.
+ function _M.wss_proxy_client_compat()
+ return setmetatable({ ssl = true }, { __index = ws_compat_client })
+ end
+
+end
+
+
+do
+ local protos = {
+ http = {
+ proxy_client = helpers.proxy_client,
+ proxy_ssl_client = helpers.proxy_ssl_client,
+ OK = 200,
+ route_protos = { "http" },
+ service_proto = "http",
+ service_proto_tls = "https",
+ },
+
+ websocket = {
+ proxy_client = _M.ws_proxy_client_compat,
+ proxy_ssl_client = _M.wss_proxy_client_compat,
+ OK = 101,
+ route_protos = { "ws" },
+ service_proto = "ws",
+ service_proto_tls = "wss",
+ },
+ }
+
+ --- Iterator over http and websocket protocols.
+ -- This is useful to run the same tests over multiple protocols. The returned
+ -- table has entries for each protocol specific element.
+ --
+ -- @usage
+ -- -- check the 'proto' table for other fields supported
+ -- for proto in eehelpers.each_protocol() do
+ --
+ -- describe("running tests for protocol '"..proto.service_proto.."'", function()
+ --
+ -- local client = proto.proxy_client() -- returns either an `http` or `ws` client
+ -- local sslclient = proto.proxy_ssl_client() -- returns either an `https` or `wss` client
+ -- local ok_status = proto.OK -- returns either 200 (for http) or 101 (for ws)
+ --
+ -- it("do a test", function()
+ -- -- test here
+ -- end)
+ -- end)
+ -- end
+ function _M.each_protocol()
+ return pairs(protos)
+ end
+end
+
+
+-- This function clears the license envs, avoiding to break the tests
+-- that use license data.
+-- It returns a function to set the envs back.
+function _M.clear_license_env()
+ local kld = os.getenv("KONG_LICENSE_DATA")
+ helpers.unsetenv("KONG_LICENSE_DATA")
+
+ local klp = os.getenv("KONG_LICENSE_PATH")
+ helpers.unsetenv("KONG_LICENSE_PATH")
+
+ return function()
+ if kld then
+ helpers.setenv("KONG_LICENSE_DATA", kld)
+ else
+ helpers.unsetenv("KONG_LICENSE_DATA")
+ end
+
+ if klp then
+ helpers.setenv("KONG_LICENSE_PATH", klp)
+ else
+ helpers.unsetenv("KONG_LICENSE_PATH")
+ end
+ end
+end
+
+
+function _M.get_portal_and_vitals_key()
+ local key, err = pl_file.read("spec-ee/fixtures/mock_portal_and_vitals_key.txt")
+
+ if err then
+ return nil, err
+ end
+
+ return key
+end
+
+
+----------------
+-- Variables/constants
+-- @section exported-fields
+
+
+--- A list of fields/constants exported on the `spec-ee.helpers` module table:
+-- @table helpers
+-- @field portal_api_listeners the listener configuration for the Portal API
+-- @field portal_gui_listeners the listener configuration for the Portal GUI
+-- @field admin_gui_listeners the listener configuration for the Admin GUI
+-- @field redis_cluster_addresses the contact points for the Redis Cluster
+
+local http_flags = { "ssl", "http2", "proxy_protocol", "transparent" }
+_M.portal_api_listeners = listeners._parse_listeners(helpers.test_conf.portal_api_listen, http_flags)
+_M.portal_gui_listeners = listeners._parse_listeners(helpers.test_conf.portal_gui_listen, http_flags)
+_M.admin_gui_listeners = listeners._parse_listeners(helpers.test_conf.admin_gui_listen, http_flags)
+_M.redis_cluster_addresses = _M.parsed_redis_cluster_addresses()
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.env.example b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.env.example
new file mode 100644
index 00000000..6f2e656a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.env.example
@@ -0,0 +1,39 @@
+# response logs
+VERBOSE_RESPONSE_LOGS=true
+
+# gateway mode, one of 'classic | hybrid'
+GW_MODE=classic
+
+# gateway host
+GW_HOST=localhost
+
+# for aws-lambda-secret-reference tests
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+
+# for azure-functions-secret-reference tests to be able to trigger the Azure function
+AZURE_FUNCTION_KEY=
+
+# to imitate CI environment (used in licenses tests), on of 'true | false'
+CI=true
+
+# kong version used in release package tests and smoke service tests
+KONG_VERSION=3.3.0.0
+
+# kong package name, used for checking kong version and updating container env variables
+KONG_PACKAGE=
+
+# either 'koko' or 'gateway' - to run target tests
+TEST_APP=koko
+
+# konnect/koko test environment - currently only 'dev' is supported
+TEST_ENV=dev
+
+# password for konnect gateway user - the main static user which creates Orgs and other resources in Konnect
+KONNECT_USER_PASSWORD=
+
+# Appdynamics password for running the appd test
+APPD_PASSWORD=
+
+# Konnect data plane docker image, default is kong/kong-gateway-dev:nightly-ubuntu
+KONNECT_DP_IMAGE='kong/kong-gateway-dev:nightly-ubuntu'
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.eslintrc.js b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.eslintrc.js
new file mode 100644
index 00000000..ce7d8200
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.eslintrc.js
@@ -0,0 +1,27 @@
+module.exports = {
+ root: true,
+ env: {
+ node: true,
+ mocha: true,
+ },
+ parser: '@typescript-eslint/parser',
+ plugins: ['@typescript-eslint'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:prettier/recommended',
+ ],
+ ignorePatterns: ['packages/**'],
+ rules: {
+ 'prettier/prettier': 0,
+ 'no-restricted-syntax': [
+ 'error',
+ {
+ selector: "AwaitExpression[argument.type='CallExpression'][argument.callee.name='wait']",
+ message: "Don't use `await wait()` due to it's flakiness, prefer `eventually`",
+ },
+ ],
+ '@typescript-eslint/no-var-requires': 'warn',
+ '@typescript-eslint/no-explicit-any': 'off',
+ },
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.gitignore b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.gitignore
new file mode 100644
index 00000000..bca4b0fb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.gitignore
@@ -0,0 +1,15 @@
+# kong-api-test node_modules
+node_modules
+
+# test results
+results
+
+# local reflection of .env.example
+.env
+.npmrc
+
+# Konnect DP certificates
+certificate.crt
+certificate.csr
+private.pem
+public.pem
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.mocharc.js b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.mocharc.js
new file mode 100644
index 00000000..5c851cc0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.mocharc.js
@@ -0,0 +1,34 @@
+require('dotenv').config();
+const { v4: uuidv4 } = require('uuid');
+
+const addRootHooks = () => {
+ const testApp = process.env.TEST_APP;
+ if (!testApp) {
+ throw new Error('No value provided for environment variable: TEST_APP');
+ }
+ let hooks = [];
+ switch (testApp) {
+ case 'gateway':
+ hooks.push('test/gateway/_hooks.ts');
+ break;
+ case 'koko':
+ hooks.push('test/koko/_hooks.ts');
+ break;
+ default:
+ throw new Error(`TEST_APP: ${testApp} is not currently supported`);
+ }
+ return hooks.join(',');
+};
+
+module.exports = {
+ extension: ['.spec.ts'],
+ reporter: 'mocha-multi-reporters',
+ reporterOptions: `configFile=.mocharc.js,cmrOutput=xunit+output+${uuidv4()}`,
+ reporterEnabled: 'spec-failed-reporter.js,xunit',
+ xunitReporterOptions: {
+ output: 'results/test-results-{id}.xml',
+ },
+ require: `ts-node/register,tsconfig-paths/register,test/_fixtures.ts,${addRootHooks()}`,
+ timeout: '180000',
+ ui: 'bdd',
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.npmrc.ci b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.npmrc.ci
new file mode 100644
index 00000000..bd3327ab
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.npmrc.ci
@@ -0,0 +1 @@
+//registry.npmjs.org/:_authToken=${NPM_TOKEN}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.prettierignore b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.prettierignore
new file mode 100644
index 00000000..e797d001
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.prettierignore
@@ -0,0 +1,2 @@
+openapi
+packages
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.prettierrc b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.prettierrc
new file mode 100644
index 00000000..14c111e5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/.prettierrc
@@ -0,0 +1,7 @@
+{
+ "singleQuote": true,
+ "semi": true,
+ "trailingComma": "all",
+ "arrowParens": "avoid",
+ "printWidth": 120
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/README.md b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/README.md
new file mode 100644
index 00000000..d9dc7cdf
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/README.md
@@ -0,0 +1,234 @@
+# KONG API TESTS
+
+[![Gateway API Tests](https://github.com/Kong/kong-ee/actions/workflows/gateway-api-tests.yml/badge.svg)](https://github.com/Kong/kong-ee/actions/workflows/gateway-api-tests.yml)
+
+> Note: This repo is in active development.
+
+The `spec-ee/kong-api-tests` is aimed to be used to create and execute Kong Gateway API tests locally as well as in CI.
+
+### How to Build
+
+1. Navigate to `spec-ee/kong-api-tests`
+2. Install `node` & `npm` (you can also use [nvm](https://github.com/nvm-sh/nvm)) (minimum node.js version `v16.x.x`)
+3. Access private NPM packages
+ - Copy `.npmrc.ci` file contents into `.npmrc`
+ - `cp .npmrc.ci .npmrc`
+ - Export the `NPM_TOKEN` in your terminal
+ - `export NPM_TOKEN=`[NPM Read Token](https://start.1password.com/open/i?a=KJVYOL2OTVGRPAAAHEVOL6MXZE&h=team-kong.1password.com&i=ss3ux3i3brfsruiarhhugzlqqm&v=q7r4hh4465zentymwtoonxxp3m)
+4. For formatting/linting, run `npm run format` and then `npm run lint`
+5. Install the dependency packages with the following command `npm install --legacy-peer-deps`
+
+## Gateway
+
+**Deploying Gateway**
+
+We use [gateway-docker-compose-generator](https://github.com/Kong/gateway-docker-compose-generator) to deploy gateway for API tests.\
+In CI, the gateway starts `without enterprise license`. The license is being posted via API at the runtime before all tests to allow us to have more control over the license.
+
+### Env File
+
+Create a `.env` file in the root directory.
+
+Copy from the [.env.example](https://github.com/Kong/kong-api-tests/blob/contrib/readme-update/.env.example.gateway) file.
+
+Add the following gateway specific environment variable to your `.env` file.
+
+TEST_APP=gateway
+
+**Environment Secrets**
+
+Retrieve the necessary credentials from _1Password_ and add as environment variables in your .env file:
+
+- [AWS Secret Credentials](https://start.1password.com/open/i?a=KJVYOL2OTVGRPAAAHEVOL6MXZE&v=q7r4hh4465zentymwtoonxxp3m&i=3o5zhzexnfhyldid53j6fquvwm&h=team-kong.1password.com) - required only for `aws-lambda-secret-reference` test
+
+ `AWS_ACCESS_KEY_ID=""`
+
+ `AWS_SECRET_ACCESS_KEY=""`
+
+**Test specific environment variable requirements for Gateway**
+
+There are tests which rely on specific gateway environment variables, make sure to include these in your gateway/kong/docker.
+
+- `aws-lambda-secret-reference` test
+
+ `AWS_REGION="us-east-2"`
+
+ `AWS_ACCESS_KEY_ID=""`
+
+ `AWS_SECRET_ACCESS_KEY=""`
+
+ [GCP_SERVICE_ACCOUNT](https://start.1password.com/open/i?a=KJVYOL2OTVGRPAAAHEVOL6MXZE&v=q7r4hh4465zentymwtoonxxp3m&i=w2gvxcep5ffevmiykbfq4ffb64&h=team-kong.1password.com)`=""`
+- `azure-functions-secret-reference` test
+
+ [AZURE_FUNCTION_KEY](https://start.1password.com/open/i?a=KJVYOL2OTVGRPAAAHEVOL6MXZE&v=q7r4hh4465zentymwtoonxxp3m&i=e7vip43g3nucwsrb44ijs6qsfa&h=team-kong.1password.com)`=""`
+
+- `app-dynamics test`
+ [APPD_PASSWORD](https://start.1password.com/open/i?a=KJVYOL2OTVGRPAAAHEVOL6MXZE&v=q7r4hh4465zentymwtoonxxp3m&i=syw6avr7bddbconfzep6o6jokq&h=team-kong.1password.com) `=""`
+
+- `rla-secret-referene` test
+
+ `RLA_REDISU=redisuser`
+
+ `RLA_REDISP=redispassword`
+
+ `AWS_REGION="us-east-2"`
+
+ `AWS_ACCESS_KEY_ID=""`
+
+ `AWS_SECRET_ACCESS_KEY=""`
+
+ [GCP_SERVICE_ACCOUNT](https://start.1password.com/open/i?a=KJVYOL2OTVGRPAAAHEVOL6MXZE&v=q7r4hh4465zentymwtoonxxp3m&i=w2gvxcep5ffevmiykbfq4ffb64&h=team-kong.1password.com)`=""`
+
+## Test specific 3rd party service requirements for Gateway
+
+There are specific tests which rely on particular 3rd party services to run alongside the gateway.\
+Make sure to enable these services using [gateway-docker-compose-generator](https://eu.api.konghq.com/konnect-api)
+
+- All tests relying in upstream service or sending requests to upstream use [httpbin-service](https://github.com/Kong/gateway-docker-compose-generator/blob/ce44aa5d508b7210336a58975285ea8e2e6b6bee/docker-compose.yml.sh#L211) which needs to run in the same docker network as kong.
+- `1_vitals-influxdb` test requires [INFLUXDB](https://github.com/Kong/gateway-docker-compose-generator/blob/d9ee692675d4efdb14d0e1b8376b20a290f72b34/docker-compose.yml.sh#L32)
+- `aws-lambda-secret-reference` and `rla-secret-reference` tests require [HCV](https://github.com/Kong/gateway-docker-compose-generator/blob/d9ee692675d4efdb14d0e1b8376b20a290f72b34/docker-compose.yml.sh#L40)
+- `opentelemtry` test requires [JAEGER](https://github.com/Kong/gateway-docker-compose-generator/blob/d9ee692675d4efdb14d0e1b8376b20a290f72b34/docker-compose.yml.sh#L54)
+- `rate-limiting-advanced` test requires [REDIS (standalone)](https://github.com/Kong/gateway-docker-compose-generator/blob/d9ee692675d4efdb14d0e1b8376b20a290f72b34/docker-compose.yml.sh#L29)
+- `oas-validation` test requires [SWAGGER](https://github.com/Kong/gateway-docker-compose-generator/blob/main/docker-compose.yml.sh#L36)
+- `acme` test requires [Pebble](https://github.com/Kong/gateway-docker-compose-generator/blob/main/docker-compose.yml.sh#L1022) which will be automatically enabled when [ACME](https://github.com/Kong/gateway-docker-compose-generator/blob/main/docker-compose.yml.sh#L126) is set to `true`
+
+**Test specific configuration requirements for Gateway**
+
+- `licenses` test requires the gateway to NOT have enterprise license. We `post` the enterprise license at the runtime via API before tests start to run.\
+ There will be no harm having the license in kong, the tests will still pass but in order to fully imitate the CI environment you need to set `CI=true` and exclude license from kong locally.
+
+- `ACME` plugin tests require `127.0.0.1 domain.test` mappping to exist in your `/etc/hosts` file.
+
+**Gateway Mode**
+
+The default Gateway mode is `classic`. If you want to run tests against `hybrid` mode specify that in your `.env` file:
+
+```bash
+# .env file
+GW_MODE=hybrid
+```
+
+### Verbose Response Logging
+
+`export VERBOSE_RESPONSE_LOGS=false` to disable response logging (default is `true`).
+
+**Execute Gateway API Test Suites**
+
+- All existing gateway test
+
+```bash
+npm run test-gateway
+```
+
+- A single gateway test
+
+```bash
+# for example if you want to run 'service.spec.ts' tests
+npm run test-spec --spec=service
+```
+
+- Smoke tests
+
+```bash
+npm run test-smoke
+```
+
+- Release package tests
+
+Make sure to have `KONG_VERSION` and `KONG_PACKAGE` variables set in your environment.\
+For example, `export KONG_PACKAGE=ubuntu-22.04 KONG_VERSION=3.3.0.0` or in your `.env` file
+
+**When `KONG_PACKAGE` environment variable is set in your environment the framework will automatically\
+understand that api tests should run against natively installed kong (download kong from pulp and install).**
+After this, you can run the tests as mentioned above.
+
+Refer to [How to run API smoke tests](https://konghq.atlassian.net/wiki/spaces/FTT/pages/3072917606/Running+smoke+tests+on+released+artifacts) to learn about running the tests in GH Actions.
+
+### Mocking/Recording
+
+The framework uses [POLLY.JS](https://netflix.github.io/pollyjs/#/quick-start) JavaScript library to record the target request/response interactions.
+
+Example usage:
+
+```bash
+# import the 'createPolly' function at the top level of the test file
+import { createPolly } from '@support'
+
+# instantiate a new Polly instance for mocking/recording
+const polly = createPolly('yourRecordingName')
+
+# send a request
+await axios('http://localhost:8000/someRequest')
+
+# stop the polly mock instance to stop recording further requests
+await polly.stop()
+```
+
+In the above example polly will record the request/response interraction and replay it using the response recording when the test is run again thereafter.\
+The recorded files/mocks will be stored for the specified amount of time (e.g. 30 days).
+
+## Koko & Konnect
+
+### Env File
+
+Add the following konnect specific environment variables to your `.env` file.
+
+1. TEST_APP=koko
+2. TEST_ENV=dev
+3. KONNECT_USER_PASSWORD=[KONNECT_USER_PASSWORD](https://start.1password.com/open/i?a=KJVYOL2OTVGRPAAAHEVOL6MXZE&v=q7r4hh4465zentymwtoonxxp3m&i=vag6ska5nafl3u7rlxy26wobge&h=team-kong.1password.com)
+4. KONNECT_DP_IMAGE=`yourTargetDockerImage` - optional, default is kong/kong-gateway-dev:nightly-ubuntu
+
+**Execute Konnect Tests**
+
+- All existing tests
+
+```bash
+npm run test-koko
+```
+- A single test
+
+```bash
+# for example if you want to run 'service.spec.ts' tests
+npm run test-spec --spec=service
+```
+
+## Run tests in GKE Cluster
+
+### Run from local setup
+Follow the [instructions](https://github.com/Kong/gateway-docker-compose-generator/tree/main/infrastructure/gateway-terraform-gke) to deploy the Kong gateway hybrid mode in GKE cluster.
+
+After deployment, try portforward `kong-cp`, `kong-dp`, and `redis` pod to your localhost. Before we run any api tests, aside from regular env variables you set before running the tests (e.g. `TEST_APP`,`AWS_ACCESS_KEY_ID`), there are some extra env variables you need to set.
+```
+ export GKE=true
+ export GW_MODE=hybrid
+ export HCV=false
+ export GW_HOST=localhost
+ ```
+
+ We added `tag` like `@gke` to filter through tests that can run against kong deployed in GKE cluster, you can run command below to trigger the e2e api tests.
+
+- All existing tests with `@gke` tag
+
+```bash
+npm run test-gke
+```
+- A single test
+
+```bash
+export TEST_APP=gateway
+# for example if you want to run 'service.spec.ts' tests
+npm run test-spec --spec=service
+```
+
+### Run from github action workflow
+
+You can also trigger the test run from [github Actions workflow](https://github.com/Kong/kong-ee/actions/workflows/gateway-cluster-api-tests.yml). This workflow will conduct all the actions required for running kong e2e api tests against kong deployed in GKE cluster including:
+
+1. Create/provision the GKE Cluster using `terraform`
+2. Deploy Kong gateway hybrid mode to the GKE Cluster using `terraform`
+3. Portforwarding `kong cp`, `kong dp`, and `redis` pod to github runner localhost
+4. Run the e2e api tests
+5. Send results to slack
+6. Destroy the GKE cluster using `terraform`
+
+There are a couple workflow input variables you need to pay attention when you run the workflow. Usually we bring up kong with images like `kong/kong-gateway-dev:nightly-ubuntu` (⚠️ `kong/kong-gateway-internal` seems not compatible with `k8s`/`terraform`) or `kong/kong-gateway:3.6.0.0`. So we breakdown the `image` to `kong/kong-gateway-dev` as the input for `Kong repository to test`, and `nightly-ubuntu` for `Kong version to test`. We also need to specify the [`Kong effective Semver`](https://github.com/Kong/charts/blob/main/charts/kong/values.yaml#L139) to value like `3.7.0.0` if you are using a non-released version of kong image like `nightly-ubuntu`.
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/credentials.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/credentials.json
new file mode 100644
index 00000000..6717344f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/credentials.json
@@ -0,0 +1,107 @@
+{
+ "cert": {
+ "certificate": "-----BEGIN CERTIFICATE-----\nMIICvjCCAaYCCQCZ6y3idn+UNDANBgkqhkiG9w0BAQsFADAhMQswCQYDVQQGEwJVUzESMBAGA1UECAwJTWlubmVzb3RhMB4XDTIyMDMwNzE5NTk0N1oXDTI0MTIwMTE5NTk0N1owITELMAkGA1UEBhMCVVMxEjAQBgNVBAgMCU1pbm5lc290YTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK14T7aoWp8QsjDSoNSB81PibxPtKT+3sQBeMQ2MO0NvFdU/fO1qH1PUNR9rpkoo9Yt0agyI5xPFib5hU+BtDi/iTYgTdnwoptuBBMFAChrVy2mig0lCXAWerMWZlROObtpYiS26ImOQ5gezbr6L4TO9qzyHcMkxTI9taVzyJxbjNSz69XMkfLmHGnYRkZ5RWYVpX29vHQPDzPNY4unq+sJUfqp9BlZW7zHwxi48J4/Q/TsO4AQSiwUFiIfLdFMd22FioCAdzi56R2uWatOpLRXD+TbSHo0P9MSvqd5AAZSbTKWU5bZH6ZD1NhsGDP+IQtEmQUH8SeJ86BEv6BJ898kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAM0EXb6tb1ge6M+Ogl9SYsUkudNPs2o3y3dPnEnMoLZOSSJDRtae8ooVWyBX8YBZ2NNgg26lz8p3Gu/jDKQK8PSRsH2oKWE+GSxiOW8nMMsu/4Esxe3uqWAsx0CEwmFZ+Qe4M3m16Mc8jvgTOkcmry4p5pz9BILqh1sv73SeziwxiIbC/8hxTmhswoyjnHPd0wIo1RZSkZMILFrp9qju8K7vRaI6k+n4EJueO1ESQ+eodGG+L71yw8brEh/YwPpjYG3aKozKBCFjUhpWyXBk3TQwfj3JLpkDGS1NVQUJxYEdBJm+KlxcpqM7YD4toMP4DBzj4NUR5mGh3INp1spf36A==\n-----END CERTIFICATE-----",
+ "key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEArXhPtqhanxCyMNKg1IHzU+JvE+0pP7exAF4xDYw7Q28V1T987WofU9Q1H2umSij1i3RqDIjnE8WJvmFT4G0OL+JNiBN2fCim24EEwUAKGtXLaaKDSUJcBZ6sxZmVE45u2liJLboiY5DmB7NuvovhM72rPIdwyTFMj21pXPInFuM1LPr1cyR8uYcadhGRnlFZhWlfb28dA8PM81ji6er6wlR+qn0GVlbvMfDGLjwnj9D9Ow7gBBKLBQWIh8t0Ux3bYWKgIB3OLnpHa5Zq06ktFcP5NtIejQ/0xK+p3kABlJtMpZTltkfpkPU2GwYM/4hC0SZBQfxJ4nzoES/oEnz3yQIDAQABAoIBAHFy5scx0ZqIA9G0XuS8XFP3GegxSbHXQ9ZWw8P1e2i9xMyjaeOnbPeZKGj1MVr88KGUsCqkemPO5CfzZTS4fffJDCW7fNj0iTOzbFWquTEVs6PRWgVWdfJi9OGCcSHKHCuGjaivPABb6mUM75eEyfLZz5yVg6jBKi3/Q1I7OF9Zgz3kA89UZ1HuljLy7/JjzewHOLCQpuR+6U2DVL8SpoKWEyVdVUOp0h370AxhWTDGynJzA6ntLbq9xZV9rr558PrtqWzb6GJpM8klstMULJY1EJLEwP/+McxJxyKJgxVVLRHQoRnHwy1laKt9UDIVEhrVfyoxusu7o+7TW/iU4tECgYEA2tjrzDJyiUA0ZwO4gfT4+QM4r8e8tkSJu6RCLt5Y5wPvRO55jYLkECB6YZEeWgJ/jQufE3lmKfPuH5LruQ+Xw1iDdBr+/DQcTsxqjUEo93WOfL/lsXWlV76IvJfKPvVFnySvDc/OVwE6tskHxbs66ddA/cdlHH1WLTLOwUVUxo0CgYEAyutLe3E6kaS74AlPOwcsYSeU/rZXExdTpPKsflXdJU1mCJBR197/5Xdb2YeyEv2yRhl74yMbJAf/c384YNpEYOK4PTl51pTELS4mtg69rvkLXgpFPK1wopyXZeZiO0GBqLUYjANzIlNgOV+cgW755ogwFz1+1Q+I3W+5ygCelS0CgYBpd+Erp6RLQQYAH2rsNoNGfqbzD++CZ8V5CNBchguSSfMl5UfvSwVeQEi6fJWEC3AtKUygtgDmdwhrbwLrTPh2zFGNwvTf1r1NvHYdXSnBGIZy2Ih53ZixkMGVIhH2BX5oE+XJYjyu8Yi6TmfQg0sBJxvLMi0obb76SWIbAq64gQKBgQC4s2WYbd8W1LBh43dnzka6BDJdipFFglUTbgROVk+d9Y5rOxoT+RpJvtQu85HGMYAEyIt4murIaDM/6MvjdKkK2zNCHz/kbrMGQNzhmBdV3cBYD9cnvOMqUIiKKyK8twqTCovWXgl9dBDBCGNHeUEkck/7x3Db9HDqxhiBYwD13QKBgQDDU6FAEy/zI2lP7/wL6fGo4Jsv3loO7k2wL4Al7ZhIa6IrTueADS1HzVCZuyGrr7fkWFAXBCho2arvjeFe+herCnqh944oOjZCPkr9YygEOs5AtHisin3K/Da83bMf5NjPn5wXKCEULAjYjoRox8xVyq01dyiwfSuWa4Jo1H67GQ==\n-----END RSA PRIVATE KEY-----"
+ },
+ "valid_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlRqdHpqamZubWZlbWlmT3hwNTZEOFdPRW5pbmFxdEdzUTR5VHpCNUhxZzAifQ.eyJpYXQiOjE2NjU2MTAxMjksImlzcyI6ImRlbW8iLCJ1c2VybmFtZSI6ImRlbW8ifQ.H_1Zb8XXIfX3rJbHgJ8G8iLIi0CXOdpj0_wOrCPAPZLd7E6IBJ9FtDevHP41IH48An67Bpgl7nRSCDRx4NuawbWUWFqiYArGlWzpqLDLKn3y0XTlSSPnx-SnNXYXIX8JufBKd1If-e7FWP6Llm2qEvUmQdbt_07R3dIlVMRAj3PLwEvuAvV6SqJuq-oGx5qLAVijShhpqY3-HbI-4Zihb_iuYJqEva5xbnhrrgQVvHP4yXDgY0Q7Swq6MjIzrFzQbWzBnpLn9nImjHKKujzh_d2nyav9UdEbi7jJc_0cUzr7L0tYxRhg_JXfcQt9bZwe4yj37i2e5zU2S_DSH446GQ",
+ "expired_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlRqdHpqamZubWZlbWlmT3hwNTZEOFdPRW5pbmFxdEdzUTR5VHpCNUhxZzAifQ.eyJleHAiOjE2NjU1MzE3MzYsImlhdCI6MTY2NTUzMTQzNiwiaXNzIjoiZGVtbyIsInVzZXJuYW1lIjoiZGVtbyJ9.bBOfKDHsT5k43EGZXatY9pKJfPsG_1EPlOkFF4F7rxwRr1JuHNvi9PPYPYaGNnDe-kInWpRYntiCrWbVyx1LrT9n1nWbS_Ar6pyyHxGahq_AzKbW_9yCZCS7Rfed3RTrJG59BaVJTd1Xy5grQTPmtPlnYqryh316tDl7a1CoSz6_Pjjj8wyRcgpUSzzZ8coEQkIm_8l97udk651PzWpBNXLsuWwN4CqtGru4iu8bltTkFvxvzvsC1lhf1te4dKnAB19AxbdxvpZsVa1FiC4uINqYpDD2tKBbbvLOydwGWLZskq19DAVukxQBbsiS1OHTkEoy1Df-lpN32dGeU3FFmQ",
+ "jwe": {
+ "valid-token": "eyJhbGciOiJSU0EtT0FFUCIsInR5cCI6IkpXVCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOjQyfQ.R1IuilWdV4kkwwpvu70RKAx-HTmB6cCkn3gPdmLx7SoKqEyTmtatI11uGmOZ4Ot5veYzwxCJMvLpETvCXaO4-S9WYAO9bmm3IZD9MoBszY-JKZY4vOI3Pe8GFNcb0IdKtvppHH6D2p80GgkGjT2prtYPprNKVkxM0OmnjyztBQWPxbZndAduZC6SepGscFRcfkBVSJqKiQo-hA5u-WOHrhlf9EcGgRe0W92S1pVR9Dxf5dzp4emexoJ0T3hqfHBAlXCSkcYf78sOwd_DClh2VNHEjjJvHyT-869eGHj5Wp-58LfLKz47wVk7LHK07LTnLxTW3UZPi6d_J8Ko5-hgVg.joVpEG77VGj5bV_a.J0EfePBhYfXkTfb48wA.PtdYZ-B4uOyjtCoGcKDCIQ",
+ "invalid-token": "eyJhbGciOiJSU0EtT0FFUCIsImN0eSI6IkpXVCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJLWkdqWDJQa1FmX0dGODROQWpVRV8tNkV1WExXV1Rwb0VpQ2EtcW9oTjc0IiwidHlwIjoiSldFIn0.aaq5Ys2-WfeERDTFuIWss2QONR_Yri0Nb7rr8meLxpzqUrP9HjaugijZvVIkEsYNjXdJ8FB5Br4JcXpgL_dMvG1UGI0itBEnn6Ti_QO1lim5BtEQR0Sv-iDW73SNLGMA_SquIS8vSW7nGvZsKGptI9JnSxRNIBtsJDHv-OZKI2oEHXmAjE0spyCJ1DLMyTaZaXFJzkEH7PxOo3paC6bqk66Aiool3fRG-CQor3GcVnx9tag5tQlvQd5neMXi58fa_B8J4iB9QXE2p8cpMaBiRhnMQdBsWm0a7s3qZ34nCHaDp6DIrkq0yVo0kNpg_YWd9VLdhtEjACvdIRFWVHY7Xw._aJWXCpfprWz5Eui.PivGQN95IO6VkmldqTJzPF_FoGw7V31ZCZDB5X02g-WDR56U-0oO_dLuSW1ZI5di8hh67xSwd8JuKuxgQnPeB0dhbChfB9_lwhcrtLT_-EqPfGfPKHFBjYu8VTvR9fswkFUrNR70Uu0Cp65IhLsToKJKA6t7d3lF1PwnzlE5mLIkE_TlddrfPHwyvfmnsKEu5s2jtGj5Ei-J0dVcdyn7DLq-So28QdlAx9JebvnB8cHEuK-m8ZDluO5pCgY8I6zFLTsQmUcfJdCVvEnCsG3RpMYpirlHI630URVFD64ioNrAXRnXnzaQGVTixnkNTBSOfAAOTs7VyDhaiJhwqsL2IOTj1_es_V5zEaKQ4_fY5Hpk76YMfb5HtpUCDekvdc9H0_jHikChKiGsiwOd4Q.LQzrLHw_9hrLT4Fc6tJ5zg",
+ "jwk": "{\n \"kty\": \"RSA\",\n \"e\": \"AQAB\",\n \"use\": \"enc\",\n \"kid\": \"42\",\n \"alg\": \"RSA-OAEP\",\n \"n\": \"6ykLsv6HyvdrFPm1f9ItIt8mlwjuSGhtCxr4fr_c0gsxfyownOHVCSzeYMezKtir4LFcn8IB47F8WmnXeyIB7pd7K71elpxoQqgCfwVI76_m5gC1HuiStXz2ltC0uFZkh5FmHJcVfjzh3hAYaHfMw-xv1NTHL6biAO1QNNV0NrbSvgKG2p22arq3NYJUDXvit0L7B83IsyBFPoaL1UcwKgO7nAPdQDkeTlzLa-dSZOFAu6vBuW9UIIZ3xy1zYUrEKzywO4Wg-CtliSycmCqM2L2XVz8IPy0ZwyGjLrNL573VPohtAa0Wwk7vT6ZzjNlwhxUmwt5AUeyLTzPGHKn-mQ\"\n}",
+ "private": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDrKQuy/ofK92sU\n+bV/0i0i3yaXCO5IaG0LGvh+v9zSCzF/KjCc4dUJLN5gx7Mq2KvgsVyfwgHjsXxa\nadd7IgHul3srvV6WnGhCqAJ/BUjvr+bmALUe6JK1fPaW0LS4VmSHkWYclxV+POHe\nEBhod8zD7G/U1McvpuIA7VA01XQ2ttK+AobanbZqurc1glQNe+K3QvsHzcizIEU+\nhovVRzAqA7ucA91AOR5OXMtr51Jk4UC7q8G5b1QghnfHLXNhSsQrPLA7haD4K2WJ\nLJyYKozYvZdXPwg/LRnDIaMus0vnvdU+iG0BrRbCTu9PpnOM2XCHFSbC3kBR7ItP\nM8Ycqf6ZAgMBAAECggEAOCqMjcr3WgXwQIlHzWjAuRR9XhOW/hpa+Q0PDw76Sfy8\nbLgB9maGAHQEBS+NXo1zt+4Exm+LDfwlTF79RX6g4YCyvYa0YdqnmEwSlI4Jlp33\n0zWWDPTVnZ1kNC9TCrgvuKWEsw1u58DY3bYQbls2wBDnwPi1Pm9oiXok7wmQgUSC\nIMgMjXpJZR/643IaozdQJRLbVLZQf/0Mn4+Pi37+YNeXbJlRCI8NVekk5Da71Tuw\ndtIeDVcmjQmvBxt8M+9cfe/wrzYZW7FV9RPzulDd5FPpGvHzQ2RN3jaqsPUHeznk\neNfpCwZhgL+J11dd2N7XQqPE1wM5FTTYvb10WuoogQKBgQD7r0fmjcc4CqD1J6jf\naARqXRKZ7QfilUkQQeHYWKtlkOJ54DIvZjk7eB7RQQkA10aGk/xWQonSsIlApZqq\nKFnQFujBeII1G1d9ILQPNpqPzLQhrxjRYzK6FywqUxhuRzbgBQDfnsdQzhpojC77\nfPei8GH2elSDdVz3depNiDqtyQKBgQDvMTwJD0gL3pTonItT8rXwbBCL5T/7syZn\nGqKGMx49DQ1uNx8PJc1aEC3OP87+UxyKEFqbPtAdjmeu7sRvDVjEGzCUezx1Cn69\nAyeypikhYkt59eHZKFyL86YrdQya/m4Bbf01tboli4Sjb27QdIze+3HDEf5+CUJm\njZPy38nyUQKBgQCtMhqteoVMeGv5KHiafG8FbtT9HdMeRpMH3/51rdcMxp5DNlfy\nrErYNLooT7PNY1dCBxZPmexUE23wEcYUMD5V5132vWIE/7K+BcJC8dV8/WY8A811\nUek3i/i1i4c1ZEZL9MV8HTyVy676oPU31PBorTkMr3P3Hs2B8R/yiLuwmQKBgQDN\nVmHokSLTdbg13LXLUnAYPl0SC7Ma29fa6UWemWGbHYprFIzoTxZz+7Gm/qYvNKAq\nchGmdozo4qhlx3oAr9+FMqZIwWbMPz8/+eZpejWTADQ1Nf01lG2mg3sikXBJ2qQL\nPCYEpsZvls8+Etp5v6RzhD5MLTVhc4d6TI43ESDnkQKBgGG28aV8yYhbRi/6/qL6\n6rUBT1hZz5U1RE8S0THTtGt6XjSnq74EomvSCI01rFalKgJKrgn9QcA98ntVyenQ\nM5BYCAgoQV0Mv7y7XSB5vU2wczhGF4QYP6OlUyDeDuBQ7cNEA2/vzcnGXcqJlR3S\n4nxtWJzCJV/jM5XoTUe3VOUw\n-----END PRIVATE KEY-----",
+ "public": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6ykLsv6HyvdrFPm1f9It\nIt8mlwjuSGhtCxr4fr/c0gsxfyownOHVCSzeYMezKtir4LFcn8IB47F8WmnXeyIB\n7pd7K71elpxoQqgCfwVI76/m5gC1HuiStXz2ltC0uFZkh5FmHJcVfjzh3hAYaHfM\nw+xv1NTHL6biAO1QNNV0NrbSvgKG2p22arq3NYJUDXvit0L7B83IsyBFPoaL1Ucw\nKgO7nAPdQDkeTlzLa+dSZOFAu6vBuW9UIIZ3xy1zYUrEKzywO4Wg+CtliSycmCqM\n2L2XVz8IPy0ZwyGjLrNL573VPohtAa0Wwk7vT6ZzjNlwhxUmwt5AUeyLTzPGHKn+\nmQIDAQAB\n-----END PUBLIC KEY-----"
+ },
+ "jwt": {
+ "ES512": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZPDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib476MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwMAl8G7CqwoJOsW7Kddns=\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBiyAa7aRHFDCh2qga\n9sTUGINE5jHAFnmM8xWeT/uni5I4tNqhV5Xx0pDrmCV9mbroFtfEa0XVfKuMAxxf\nZ6LM/yKhgYkDgYYABAGBzgdnP798FsLuWYTDDQA7c0r3BVk8NnRUSexpQUsRilPN\nv3SchO0lRw9Ru86x1khnVDx+duq4BiDFcvlSAcyjLACJvjvoyTLJiA+TQFdmrear\njMiZNE25pT2yWP1NUndJxPcvVtfBW48kPOmvkY4WlqP5bAwCXwbsKrCgk6xbsp12\new==\n-----END PRIVATE KEY-----"
+ },
+ "PS256": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\nMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\nNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\nqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\np2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\nZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\nVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\nlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\nsJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\nmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\ndgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw\nta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ\nDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T\nN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t\n0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv\nt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU\nAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk\n48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL\nDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK\nxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA\nmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh\n2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz\net6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr\nVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD\nTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\ndn/RsYEONbwQSjIfMPkvxF+8HQ==\n-----END PRIVATE KEY-----"
+ },
+ "PS384": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\nMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\nNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\nqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\np2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\nZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\nVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\nlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\nsJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\nmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\ndgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw\nta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ\nDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T\nN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t\n0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv\nt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU\nAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk\n48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL\nDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK\nxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA\nmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh\n2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz\net6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr\nVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD\nTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\ndn/RsYEONbwQSjIfMPkvxF+8HQ==\n-----END PRIVATE KEY-----"
+ },
+ "PS512": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\nMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\nNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\nqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\np2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\nZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\nVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\nlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\nsJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\nmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\ndgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw\nta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ\nDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T\nN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t\n0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv\nt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU\nAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk\n48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL\nDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK\nxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA\nmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh\n2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz\net6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr\nVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD\nTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\ndn/RsYEONbwQSjIfMPkvxF+8HQ==\n-----END PRIVATE KEY-----"
+ },
+ "RS256": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\nMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\nNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\nqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\np2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\nZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\nVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\nlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\nsJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\nmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\ndgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw\nta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ\nDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T\nN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t\n0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv\nt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU\nAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk\n48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL\nDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK\nxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA\nmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh\n2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz\net6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr\nVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD\nTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\ndn/RsYEONbwQSjIfMPkvxF+8HQ==\n-----END PRIVATE KEY-----"
+ },
+ "RS384": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\nMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\nNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\nqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\np2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\nZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\nVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\nlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\nsJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\nmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\ndgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw\nta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ\nDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T\nN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t\n0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv\nt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU\nAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk\n48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL\nDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK\nxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA\nmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh\n2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz\net6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr\nVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD\nTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\ndn/RsYEONbwQSjIfMPkvxF+8HQ==\n-----END PRIVATE KEY-----"
+ },
+ "RS512": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\nMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\nNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\nqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\np2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\nZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\nVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\nlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\nsJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\nmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\ndgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw\nta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ\nDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T\nN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t\n0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv\nt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU\nAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk\n48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL\nDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK\nxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA\nmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh\n2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz\net6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr\nVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD\nTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\ndn/RsYEONbwQSjIfMPkvxF+8HQ==\n-----END PRIVATE KEY-----"
+ },
+ "ES256": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2\nOF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r\n1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G\n-----END PRIVATE KEY-----"
+ },
+ "ES384": {
+ "public_key": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC1uWSXj2czCDwMTLWV5BFmwxdM6PX9p+Pk9Yf9rIf374m5XP1U8q79dBhLSIuaojsvOT39UUcPJROSD1FqYLued0rXiooIii1D3jaW6pmGVJFhodzC31cy5sfOYotrzF\n-----END PUBLIC KEY-----",
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCAHpFQ62QnGCEvYh/p\nE9QmR1C9aLcDItRbslbmhen/h1tt8AyMhskeenT+rAyyPhGhZANiAAQLW5ZJePZz\nMIPAxMtZXkEWbDF0zo9f2n4+T1h/2sh/fviblc/VTyrv10GEtIi5qiOy85Pf1RRw\n8lE5IPUWpgu553SteKigiKLUPeNpbqmYZUkWGh3MLfVzLmx85ii2vMU=\n-----END PRIVATE KEY-----"
+ }
+ },
+ "license": {
+ "valid": "{\"license\":{\"payload\":{\"admin_seats\":\"1\",\"customer\":\"hayk-dev\",\"dataplanes\":\"100\",\"license_creation_date\":\"2021-09-20\",\"license_expiration_date\":\"2025-02-03\",\"license_key\":\"ASDASDASDASDASDASDASDASDASD_a1VASASD\",\"product_subscription\":\"Kong Only\",\"support_plan\":\"None\"},\"signature\":\"affb886d81db61d3eb813c81da00f1b365a68281a2fa1f921245b27b95e4aaf583b40e745526a8b828f61e97bcc657b699ace84c3a9b8353c5e48dacb579218e\",\"version\":\"1\"}}",
+ "invalid": "{\"license\":{\"payload\":{\"admin_seats\":\"1\",\"customer\":\"hayk-dev\",\"dataplanes\":\"100\",\"license_creation_date\":\"2021-09-20\",\"license_expiration_date\":\"2025-02-03\",\"license_key\":\"ASDASDASDASDASDASDASDASDASD_a1VASASD\",\"product_subscription\":\"Kong\",\"support_plan\":\"None\"},\"signature\":\"affb886d81db61d3eb813c81da00f1b365a68281a2fa1f921245b27b95e4aaf583b40e745526a8b828f61e97bcc657b699ace84c3a9b8353c5e48dacb579218e\",\"version\":\"1\"}}"
+ },
+ "keycloak": {
+ "certificate": "-----BEGIN CERTIFICATE-----\nMIIEQzCCAisCFCZCZERei1aHqgRH8rp4OV5kQi2VMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMTEwODA4MTk1M1oXDTI1MDMyMjA4MTk1M1owXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1OMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcO0FmV94ZwSWoE5DSXCE15u8qG4Mk4ir8rSq6ILt3aF4HYn89af08Y1LvObCntGZuRwJdvYr24too3Hp8m1ZoZe/1Q4wP8dCMQFnGQoXRfeYX8vQGewA09ux16amFS7rO7PBffFnz3V3ZHvar8/eDwAUduipWVFy+0dk2B3pJJJwcX5ZJ5Vu5BbkZ61zBj8Fo5luoX+8qgV3A5EXeSMpM0tzCTAdB0jV1FCHdvdZEvT8+R45JkK1aRxoqw1GTY1ddNz+GfuMq8Lhfp8KKwUhuSwS2aH7/zPa+YgpJ2ego5zGoOCBp/95fYxSVewyF9M/uz+F3Z+KsoYCQfrtDKLwDAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAHyuJEJHxq3fvcpS1OyFpCM57N2H6N/2l1eMtOOB0C9yoCbpcvZMBZhGt2lqPXbAAuLvaeUSsHBOm5w6HeICzFF+75lqBrTYCbSnNSxt5kOqnMcW6Lsf4Oh066/XOSCF8mBAkr/npBoXx3dqykkJx7ckjp/Ny0qeShn4Cg6NWm031KgK6Ana/2GowtHJY54gWeK+kO01qj1zwkLhECGPkD26JLBhbzkwL/0nhP1TovZirFw6AOzbGJAIXedbflI3P3HD7hUudbRdrQ7i5sTGo8qWpiM2Cb/fhFEkj+jh3/WIp9HVJ0sEsIqX1blGm84PZxB2903N0LXToWcPHMuMpfMrvSguXZHjEI4r400dQ9OlWohgbPEl6+Ka+i2jsKXluqzGcWZpOKl5fPdWQNYhvxXZ95wUrg3aJosed5pol7+c5576KY2ptKa/WqZRfICcUQkMu6imUggfznapPivEg8adz7VH6NWb+1rtsJcfgKSEJi1FJZYd7v6rDgxIOyKXJvDhTc7B9UCjJ5HL9AqAn4H7yW7Oo/8pivjy5QJrDtqEUSNRWD6DxBPKXj0bssAqqyDO7VpRIr33vXMOq6T2BKXaWJByltOC/kCerA+SsggRTzjS14Od5VvvsiIWnfmqo5oCYK2mcFhpmWb+T3a+wd15OYF4CeIJhEDELyEQz11Q\n-----END CERTIFICATE-----",
+ "key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcO0FmV94ZwSWoE5DSXCE15u8qG4Mk4ir8rSq6ILt3aF4HYn89af08Y1LvObCntGZuRwJdvYr24too3Hp8m1ZoZe/1Q4wP8dCMQFnGQoXRfeYX8vQGewA09ux16amFS7rO7PBffFnz3V3ZHvar8/eDwAUduipWVFy+0dk2B3pJJJwcX5ZJ5Vu5BbkZ61zBj8Fo5luoX+8qgV3A5EXeSMpM0tzCTAdB0jV1FCHdvdZEvT8+R45JkK1aRxoqw1GTY1ddNz+GfuMq8Lhfp8KKwUhuSwS2aH7/zPa+YgpJ2ego5zGoOCBp/95fYxSVewyF9M/uz+F3Z+KsoYCQfrtDKLwDAgMBAAECggEAMul17IFqpFyM5oajZ6YJvWrGJl9W2Xt5gZFMu9ueSvPzLNTd4mYytzek8A3Po21mcp7y0pXjXsDBu0mA/XZxqs/KAdKcOgFme03TEyzEv9HRk/7D4A2mVhgxQO8EE/8WUAjhZsmaB/DIZc2tK8JKGYiWBjHH4M4+5JQhmUZwRCEG+Zts6oAwaWlxgvACVDKwLXbFCMHOPHHmTHp1SG+t5T26U6MAzzmcSCBSB2i04W+ZK4oeKEl+ZKYcxLhQtm5IL9iRGAyuZUPY/2etwn+rGRZ14UDnRLr15WjZDqtDyljtyiROBhVu7Pr09KXS0T2yrL66f0wP9XqPTdMPSv8ytQKBgQD9tTsB8xouhryydugzoyEDTgqt3NqnHK72U5dFFP5FFALTTp0goGxBhDsJ96qLNldejiM5LqhnROFcE3XhhPA7zpeR/7Wh7qUNN9c5uiaSxYPtwnklFK8IA7LIepl/372MNo4S3YuoblYDZ43jZoR5fORxJSMocbEGuQVg1tbWPwKBgQDeOJn2o6yAVB/MV/rz2wnAI3FYY1PBjpnhBVHoQwtE0eG1zg914Wfr2f4wOgqrtsC9f1i6JKM0JRYZbEBcSFswEW249JiRESLdsvN3apBTHoy6Pj5nc2WZfmzgkyJ1N/l+3d0LsRztrI1TbD8r5JGm3AKMyL3gJY5D2U9HAeKRPQKBgQDHjbDoC6rgRaJOTTC+zOS+kLfdoQVqxCjqHz2wXJ7S/rZqNj9o1kXQEo3NfTxrW0bFXM9fvs8+yvLnVXc29zVuFUUq1RKsV+UCzJVBFqYYc7sdXAWloOs7Ro1tGuF5ryaMDq0ZMaETzhVhgCYeYxh35kBYrnlE6ofvXcBZNyKsSQKBgGZ4+V6HmUtP/53OUFXVAskMTv8wRnYyGPlv55ZQHEku84NPs0drmu1ih9mbWkJWsshvie3EaVHpbChrB77GSu5+ivdIK3ZxzX8oPycpB8achs53V3WC/C3s464nizM8jZuYGY+Dq9BZyo3Wk8bczuGAANkE1LIg/1qAQhN/ZggNAoGBAKhR0KtjgwkGazVJUsGVyRBBCNrX6IJkXPauydIpdVmUye/YLRASJjweavdKxK6YQolaHFJvUAagluxXT53hn6zg/7FU4VsBB8EoIEt0l5AFCC53ShBBLyiIbIn90Y4vbojCJJeZkE5shTYWuLI/sBlLLWITSdxjdz6YWipSYf3e\n-----END PRIVATE KEY-----",
+ "invalid_cert": "-----BEGIN CERTIFICATE-----\nMIIEJzCCAg8CFCZCZERei1aHqgRH8rp4OV5kQi2XMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMTEwODA5MTUwNFoXDTIzMTIwODA5MTUwNFowQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJuBwadjXsVMICBCr/18fFINHQB9xlgr8HHnetYQxEswpZ2wVsDFlMYkkDjKC3cdvw0OC6Zq3TMX37fOtWqoZzz91BdWdw+Fau8IwXjjLwfC8/u6KGtvS46NDJMmINVIvUdlOjv9hQbHcW9UPnEnCxPq1dwgRvrfGMQ/SO4a8kPEzfpc+PQFuZhdzwnvq7IpfiG3sW96dbBCrMfaIsJGqxXF7gfp0cEdVtsUPbMO12lPfwYVCBjiuBvGM6VTUroP/Jg6cwk36dCe+WFqIjqt7TQRP9w58vSR9auQ3g/ILneIQ0hIsKiX5ybK12p6tlue8EIkynCxaaOOwq657aC/EdECAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAZeUW1PrE+3upADWi4U89uRyDt2EXlmqfRBy6J7OJnprHugOCtKGmIPPtaWRLzzj0lCgs2ENp39xtvstlszrdN40Fq66NiYBb3OLnyOSbnTlPMg48xiA3fXGfa63rz6ofMGSxxTY7ryn72S4nUWNDCpOD70N5xDg9AhCdbQErfz63LqhBcXYT8ycQVgvD30A2bRPUct7Lg5ZfOUvd3hj+ZndVBxYqFMEm/y6WOA0/02N3j6R817P6fgGvbCLtLuSofxG0qe+b1a2+ZY+aAtcZgU3Ij/YRou38kiMEZwi5xF8+G02Z02oQR/acEdBLI/+kkqKL6CsZjdR46mEBft0UXtEpCgZq1XVTxgE34mrmwY3o2xufc8rVySt1x+5nDMAvrcFfJzuxeEPDyq5kEKhNR0CKjyCKvAxxFvZp512M6LBFSg8SH+yXUDeqxO+rsI75id76bxMgP71zw6Sb9WzVtWCC7iIg/+IxYOuQZffeuVsG0rW7gkZPDLQrrhOdc/k+TJG1zC5SPCJk4w/mIgKCfjcahlrIFaB6K12PBh74hwuN9wjU3cM4AjG5haS7oDRKK2OmzTr+PF7dzHEKlhGMIiLM0SiTwRp/VwoF/iKj2GGrl5CrPAJ8meEmRzWCyhR2v/eEEhNLQi0mKQOEMgcjHO4+Q0rjIWbvFK7pwAXOhAs=\n-----END CERTIFICATE-----",
+ "invalid_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbgcGnY17FTCAgQq/9fHxSDR0AfcZYK/Bx53rWEMRLMKWdsFbAxZTGJJA4ygt3Hb8NDgumat0zF9+3zrVqqGc8/dQXVncPhWrvCMF44y8HwvP7uihrb0uOjQyTJiDVSL1HZTo7/YUGx3FvVD5xJwsT6tXcIEb63xjEP0juGvJDxM36XPj0BbmYXc8J76uyKX4ht7FvenWwQqzH2iLCRqsVxe4H6dHBHVbbFD2zDtdpT38GFQgY4rgbxjOlU1K6D/yYOnMJN+nQnvlhaiI6re00ET/cOfL0kfWrkN4PyC53iENISLCol+cmytdqerZbnvBCJMpwsWmjjsKuue2gvxHRAgMBAAECggEABMztBJzykWJj2P8aBfgccd9+AlR/pZvooqmHzxkqKByOtkcZ7H5E9RdunNQu5azVR6LD/Q2OOcwuP7M5wjrD4hv/Wr+UchYXKp0hdNkFjAaqh74zzno7ccF8roO2LoNd9XjkYvL08vQ/9hT6jv1qsAEXPEICUu8FaZR/DLun+CvMLKmGXDzADuXD78MFlVs3OkJdBBfIa+joj4Cj5R14QJITpMqAluatMqSlfq5AmgfPi8Vb1gN8sS3Iay2uvjKFVrBSHbtCyzBhXpREZMkqQJXTLeaGzx0ydxr2cpb83WlK+c+5LcOKbs7pF6JzOgirNmzvEG/LLWqDD8TzzILwyQKBgQC7W6DbjrXNw4y6v0mURB6ZCZ+wLArNYCTZLsXvWyX2EHlW3NQ/PY18DMpUSsyTf/VWZ46QfgGTrJVDhSaYz+FUiQMNRwP2LaHCAEJab0wiTB7xrd9UyciG6M2iI5HXa7/eqKUSA2AidwBfyAhSPepSIxAp/SNrG5ziNbgNAsEraQKBgQDUes4EVwPuFxzdPp4DQzw3xfnCaWgoiKMCqkdBbpQ0ZAytlQJCSTHbXc0g/qHsT+vjTF4OiEDjkCxKjpjUkOSUk+72x3Q2pPG5x+rveQ/zNUAz7YJnK0bB55wclRV+XUS2y86UijTBBTke4Qeglj7DySzUCBRcrfoSrBhIGOhuKQKBgA2pidpY1sMRbnKzvj7UlNKeFAn5vDPrveQkeASTRfpiiyadZdDSwT15hUuORMrAuvg5BvlcZusI272XuQ/NoOXUM+fE9PH/s9r8v9nuYcPifyMqha2eJURJN62KIHlv/wbSj2731gvNV7akVI8CutKQgf492lZWJiOMvUYdnE55AoGAO83s2o/bTVI4D5WmR+jOtCwG5k9gv7flmcRvld0X3b8jSmAcaKfNXp8CM303+hlsnWdLOe/jw6k6rimAnqPBkogoxFDTRuYaG15Ho+uBL8UXMwZwo7UsjzsvV2QmzpBV3NV0aZ6Uxpc/wo835F0lgVnIQk2b3ZIiRsslcnWARjECgYEAlv8R0b1LWpmY0k2E4L9tqI0jEgTcWy/GP/xBrRVJlrRAeUSzGPuaE/La8vi6YWbdXnQ4J8jTemEXUa34UJ+lI87pqq8q+hhlViSvpcXNAoEqqVbjQgSe03CqjmVZVfsHuLO5mYS7DRIT406zQ88EjlKgDGF9TLp2iHVw/Q+8c+c=\n-----END PRIVATE KEY-----",
+ "client_cert": "-----BEGIN CERTIFICATE-----\nMIICxzCCAa8CFFgjbShfHTwuqdIxYV/s6pBFTTvKMA0GCSqGSIb3DQEBCwUAMB8x\nCzAJBgNVBAsMAklUMRAwDgYDVQQDDAdKb2huRG9lMCAXDTIzMTIxNDEwMDIwM1oY\nDzIxMjMxMTIwMTAwMjAzWjAfMQswCQYDVQQLDAJJVDEQMA4GA1UEAwwHSm9obkRv\nZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANgpaB6pasorZTXEZlmt\nqsitAQnOw/C3pbxICe15GS5iVBvbFI2ejF62hZnmCXgFlrEgDehWZ2gGiUg9Xiek\n2LJh0dKaHvfFkjwUCWC/GtbYGAbwZCOfL98DiDO1Ih8RfvbzRQiqX/hB6p/C8YHQ\ntFfs/AsfI0oCnynRJK2byRqHqP9FjXhY/V7NqNrVhwxRq1jYM82iJeMi3rpnJVDF\naxG2B9Con0S4uu3/CVzv0rOAi0kR0tbljHzFstG31jGk9aBxg3wfVqhDXN2/8iuU\nXgUKBBbR2JAKDR/VABC21NuonaHDHWOEb16a+rC+PVt/CK/zVAe3cPwsIYDc2Q24\n3WUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAApliUXxCXJBMsY25rotNj2a0TtI3\nddcm4NhL1Y4nKKNgejfE54aeRXgReahExhFXKJEODqHhawyCJc9KGku7xNuznaNw\nYFc6e0h8pIVCwERVRxTUIk7pDxEoLq7jRjazLKI4mLypopfKjXkkuSqdXZFvH1jH\nLqxMFTzMIDuJDLRFC5v+R4M+FgITi73um2Jj51FbMoPXV6Tpw0im+sUriL3Leppz\n0Qgu1iN7G0T2VILcMNnjxcUj4BoYY15qz9vHyGrVqW7tOutUEjqBqwbG9MWrx00a\nZfVieeNnDHZioEVtAhHXo2SL9SdSHmV8E+5rJD25jFyWcUTsRLAEE03mmQ==\n-----END CERTIFICATE-----\n",
+ "client_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDYKWgeqWrKK2U1\nxGZZrarIrQEJzsPwt6W8SAnteRkuYlQb2xSNnoxetoWZ5gl4BZaxIA3oVmdoBolI\nPV4npNiyYdHSmh73xZI8FAlgvxrW2BgG8GQjny/fA4gztSIfEX7280UIql/4Qeqf\nwvGB0LRX7PwLHyNKAp8p0SStm8kah6j/RY14WP1ezaja1YcMUatY2DPNoiXjIt66\nZyVQxWsRtgfQqJ9EuLrt/wlc79KzgItJEdLW5Yx8xbLRt9YxpPWgcYN8H1aoQ1zd\nv/IrlF4FCgQW0diQCg0f1QAQttTbqJ2hwx1jhG9emvqwvj1bfwiv81QHt3D8LCGA\n3NkNuN1lAgMBAAECggEACCbJlncWP/D9KaxSHW7N5QWXJ4RHVygfGnekqN1Qod3o\nj5Nbq0/aZHD2yVFc3oyRZ0pkPonRmkvoIz6t3V9bTPYg4ukCeB6tz3uu8y8fEeaH\nbhg5zS5shaOyYBwkIQP4eSi+hp+MK8ZqlLiV1xV5044BiYwp+RlvtfSShkR8w+Cz\nedaax5qlsN4qzMW2KPadlwEwCUNy3dLciTZk8+N4lVEbwQEF+3FknqSqMSpPd1as\n/NwcF/l67pEmFd7rqOS9aMDf92Xc0y7M8u8Lm/zujForL+yKTjHT+Rw1eF2FtJyA\nc5vG9P95PX9+EgEDX5jfZHzSU1Zjx41MT9n038+Z2QKBgQD/+pr6dPjsVojx6JVj\na2dT1laAPoaDaHZaAp7M9h/QfvE4fIZr7oysJO9IjHea+3s2k2iNAD4Ly8nLxR38\nE/gzKkzx/HNqEAt6rRDj0d2Qhd+ChIFPeGabYHXaLIBjZt0i1Sts1iBeUOVwtzb9\nwdxRuf7luG6MyDgj7IUQYVSw2QKBgQDYLfZTSYbUC6QW+92NGjoshvDPAVq4qUNi\n4Z7ghVlKfXZdAv+UMzd4sB66w3ydcMHjvRVTH4J0gbTn1SyTdvvJ+w4lPft4nCik\nnGUTO5NN18KL652AM/JBLnkPySkiRzAJSQaANdogyLcTt2GOc25AE+T24uv/qt5U\nYojBgct5bQKBgBTveh1h07oAmdKvIwiukBTbQtfaRUYaFRPngvVga9mxiCkGhjft\nmrO5kZhtZJrYVotwIuzlSC3OzQJNbNZhC7ujigeAji/ugAHYhbkcQPLBADa8f5bw\nUfQDP6BIY3e9wfhHVvXfzWXpV9qQIX8/i7VjcOV44BHDjlPh1EAytaqBAoGAZgq0\nBjZjvT3Nc8fpmUwy/4emrZ/PCfFjzI4BZ6uqLT4xm8/pgjP20DIaH0BdVxe9fdln\n8neiJYqAMRpv0x/L1Fh8IdozFIhpquHj2AM8EfdxboiGLNjYViownOHwV6zQoOFm\nfyhsUSNNP7JFC2+zL+30GxGcIm+uSJjGQskYmbUCgYBcDW+msjtTFY2agOv5cHZ+\noFVsXNZcdR1ZBj490WALWsAMfAIKGr2UBjUvTWEmCaORm08bRJ+WZOdeWJqD8ODV\nioOYLLhroLFS8WWssHJS81DemuqR4VOoeeohYkz8w40ik3+o8a6Wb9JhdP4HrsEr\nCDr4avQv+ovVFn5van5XmQ==\n-----END PRIVATE KEY-----\n",
+ "expired_cert": "-----BEGIN CERTIFICATE-----\nMIICxTCCAa0CFBjWB9aWwggvdg0BpHOkfdYoUvQJMA0GCSqGSIb3DQEBCwUAMB8x\nCzAJBgNVBAsMAklUMRAwDgYDVQQDDAdKb2huRG9lMB4XDTIzMTIwMTE2MDAwMFoX\nDTIzMTIwMjE2MDAwMFowHzELMAkGA1UECwwCSVQxEDAOBgNVBAMMB0pvaG5Eb2Uw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCM+accTh9210Zwti9auPaE\nHIjwgnZ9oxWKGssPWZN3wUWvFpTXsbEBBYLk7aZfd1/MgjAj8uKpGSA+b+yNBgJ5\nNAS+08CQQQ+XPSWiGM97EhHpOBxk2SfGUhOEWnnsygeUu9Uftn+59Jf9wHHPrIrD\nxBCRjYj5azUkni7Rwlxr8MKNnDVxf6bSE3xD1kG/TsNzA4uDJK57UYTjsCcmqRHo\nitYPcYqCX7zK4cDJzvfvgMD0ywWJJpW/EGEyOHyI0TTNvjSAg6IZ++XMNq7pxN34\n///3bkY3nb+LvTAJpofaNTfJAMDcdYAozHwPs4A2NlyAl52aaTLm2PUH/EL7Co6R\nAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAMna7QfXOa6MHqVxiJI6k2Emh5iPHTj\n4Rl391XF2gQgFelIDY3y6DYDs7kcfiOsPb03H74prggFcwVqRksb/hX8Oiaid9IF\nIiXjejXI/lwXAnmUR+iS+K17ViLM6uO6sSbdyP8veD9TSSEpFHnNU9uR2INx46tn\nLp/0BxNWsXjST6qj8b/RS7qP9JPGp0QDMJxIP+z1DAKnflbcC2LC7GWRp4E8foEd\nKdgg/XAbI7prrk9+ZbVPKJCY7LZ4/+YTHxATi6wzWE/CpUrcbxJedlW28vFsXKnA\nGH8vh+2V/OwBYPahQydjLmTQYHcakhTsVb6fI4MlAFD2htAIRaclbVg=\n-----END CERTIFICATE-----",
+ "expired_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCM+accTh9210Zw\nti9auPaEHIjwgnZ9oxWKGssPWZN3wUWvFpTXsbEBBYLk7aZfd1/MgjAj8uKpGSA+\nb+yNBgJ5NAS+08CQQQ+XPSWiGM97EhHpOBxk2SfGUhOEWnnsygeUu9Uftn+59Jf9\nwHHPrIrDxBCRjYj5azUkni7Rwlxr8MKNnDVxf6bSE3xD1kG/TsNzA4uDJK57UYTj\nsCcmqRHoitYPcYqCX7zK4cDJzvfvgMD0ywWJJpW/EGEyOHyI0TTNvjSAg6IZ++XM\nNq7pxN34///3bkY3nb+LvTAJpofaNTfJAMDcdYAozHwPs4A2NlyAl52aaTLm2PUH\n/EL7Co6RAgMBAAECggEAMMiBbfeQxsiW/RDmUSAZWj0fnA6hXzeMkBZWd1DZiYLN\nkNmhFwTLyh00nvCn9ngyI+xXKVF24uopcMG9W02dlICONdTD2YdSFFVaDP5F6gIm\nRYyfsrw8mdG0JERE1c5RkkPva8J07432/J3YJHPCLx1NWHjgUeMp6Cp+7CI0ZjtO\nLBn4Nk+6umPL/PNKNRSBjjL1KSSOCfZ41Tk4oa814aM6chdAi4nCo4GJr2YraSuD\nErzz7MSK9OENNtm/zLt8bhKsqKmz9iMWFOdA18a6Vo5tBNhlrf4X+GC4uNokju06\nQI30060yiZFyiSmMQ9ll7BujIwgEuGwWc00lGJQEqwKBgQDCb3x1kbyNd4+nzv2b\nNWleTBTL3kLsFofeYlNmg62/B0C6K4nmaDwr9DFaavytHk4PgW9ARqO2COpGIq2A\nz6o8+6utfTj6dzHGeG/JxBtqpvmp+O3KL3rhlZd4FIu8BTR1m5Ug2/D/HdNelV9u\nA29dPjHHynPz4mhreaI7T2cwzwKBgQC5nMsRPff4/pmt/TAfB9ePVAX1csELRTEb\n+st2oGA9Xz4EsHk08CM8hhyZ21C2EvVcix0/tkRFwgkGtBjvyUHdtdP7fJF25ppe\n5xQuy/tYYizbK2hLJj0imZIImvbOAofh1nQddqd73q8V9tvxT69EJUvL0JDYibGQ\niI9La3xinwKBgBi6cxrEyTU4woV2Ftz5gwgiJvbyspIyH1+SI8dC8gL8mifTR7uN\nADIa4Xf9aVtfIlBohsym70VEhv8ag/SA9MbCv0fWSDvakvMJ+DWyAkajK9eJODzh\nowAztUrpWgg0pUJb4jCGqHTZxzsdRkDIoQ47zbr6LZsT7XHVy2M9qwMTAoGAZOHX\nbROlR2v50xsdoH/+pSQh6Pnp+lotCuwQ0fTw2ep+JsZT2nX6cNbjjKwq/grn2Niz\nmQFTsiZ4CXCxbQKSDhLZmIJn/Zvwo0wtKBIuFiemAaliEhZCfB/Dw3GWWvn9KL4K\ndkkbVrP9rY5ylIGe3Qb4X1qIn2iXXdsbJv7QqDkCgYEAlIUtLS3hfj5sy8R14CMu\nZmxiWa6tYPc3s0WhuQaA1O8qV0hSoKizPbLxc8CWkxmj3sKfVgjPYtxWt5kNsx0e\n5mC3SrdFLXiZN3zUCm1fjwzkF/BHa0TwccsMnuW79gY3fjADvkcuUoNSytEtiv+I\nlho1YrlWKdRjxNy1SMItKFM=\n-----END PRIVATE KEY-----\n"
+ },
+ "mtls_certs": {
+ "root1": "-----BEGIN CERTIFICATE-----\nMIID4zCCAsugAwIBAgIUO14yS1MRHQCj131hFsPKj5T2W74wDQYJKoZIhvcNAQELBQAwgYAxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEQMA4GA1UEBwwHVG9yb250bzENMAsGA1UECgwES29uZzEQMA4GA1UECwwHR2F0ZXdheTERMA8GA1UEAwwIS29uZ1NERVQxHjAcBgkqhkiG9w0BCQEWD2tvbmdAa29uZ2hxLmNvbTAeFw0yNDAzMTQxMzAzMjVaFw0zNDAzMTIxMzAzMjVaMIGAMQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xEDAOBgNVBAcMB1Rvcm9udG8xDTALBgNVBAoMBEtvbmcxEDAOBgNVBAsMB0dhdGV3YXkxETAPBgNVBAMMCEtvbmdTREVUMR4wHAYJKoZIhvcNAQkBFg9rb25nQGtvbmdocS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCntmHKj1AsW/kS+QSDT3txhrbk2hfMqVqbWmEyCBX+gkCC2TEhynFAIu8C6WtpTLkJ7w9O21dbOFvbB73cQVWs79ZJRLNxj4HTEAfdDDXT2EuaJUzYVGjulWFts0Vv4X39uBZW3OTnpw+N813xIorSOfhUr7PNtB2adkesPRoPlIgeXaBT9eAJFMAZygxbTIi91tju5LnYC8gcsVD1U+EQMpYr1dAqL21bIHYzb9/gDOL3VRZdvUj3jmrBk+29yJYxwWT7Y7JJB2QDg1D6NIYrM9zdZCxd3jUHZCSktwQz7ArCjREYt7qI/Hs/+hxgC09nHBRQ7c3+u4pf+Av7KzmXAgMBAAGjUzBRMB0GA1UdDgQWBBTdZ+Tg1VRg7tuc4R71uPAnGyy5BDAfBgNVHSMEGDAWgBTdZ+Tg1VRg7tuc4R71uPAnGyy5BDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCdseV+SBycjoGZAuZNwScdwbUQgXcLSBpZBgRrU3sS0AO5kFbIWTBum57MsotWrINHdwqbYEEaL2PCP131vKCkNi4V55GWlBm1cZvW/ECB5HAnABw5b9lw7xbm8NXQHPXPgBHp2oEyLi60+BPSnWtOrYo7NlIO4xD/rmANfCQPkI66Gu9+Dd5LCxNkNra02whges8/ZL6dSoCVI11+5jmRMcQgBxcT8G+LCzoysgrI+KlQqZ08P/kMR3YydvMR6boi0f5SFeQf1zAZO6SfV1xkXLS+O0QkHllqIWMy+IoHJmgKYrXE5mwXC9/WK2MZvRYiPgeoGlo5fISUDuRFZovd\n-----END CERTIFICATE-----",
+ "entity1_cert": "-----BEGIN CERTIFICATE-----\nMIID0jCCArqgAwIBAgIUe0+5ddCAy7XdRoRtJGeBsO6fSwYwDQYJKoZIhvcNAQELBQAwgYAxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEQMA4GA1UEBwwHVG9yb250bzENMAsGA1UECgwES29uZzEQMA4GA1UECwwHR2F0ZXdheTERMA8GA1UEAwwIS29uZ1NERVQxHjAcBgkqhkiG9w0BCQEWD2tvbmdAa29uZ2hxLmNvbTAeFw0yNDAzMTQxMzQxNTJaFw0zNDAzMTIxMzQxNTJaMIGAMQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xEDAOBgNVBAcMB1Rvcm9udG8xDTALBgNVBAoMBEtvbmcxEDAOBgNVBAsMB0dhdGV3YXkxETAPBgNVBAMMCEtvbmdTREVUMR4wHAYJKoZIhvcNAQkBFg9rb25nQGtvbmdocS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDABdiq4DZafCeecGWKqU+uH2S114YhM0FtIqIIoAnMU7FpHntsk/XCKROVp+uy7CiQK+b+7gC7h7xCW3TNgzOxGW7TJ4pOTUiaDTq65UpcqL1rMrKQBLfktEYXry2myQ+z9FEbYUPtkB0l/hN1YGl+qx4NvtpTi+75g8vPMWiCwDTWL1blHbgbsWx9ULSeLWvf9vCQVQ5wMf/IiCarHwM1Svo3cu4d4Wssq9cWMohhX0HtQWJbfqOVi3GAL0hcEe7yknwXtiw0iZkDGAVdGvnVgLXZVOWF7UA865gWFv+Jw+Q3u3KRg8bTfzIP0mOv8QCvqCtVdXwy/W0//gLSGIkHAgMBAAGjQjBAMB0GA1UdDgQWBBR6fE8tJEdBw6OcJ61vkYx/AAIV5jAfBgNVHSMEGDAWgBTdZ+Tg1VRg7tuc4R71uPAnGyy5BDANBgkqhkiG9w0BAQsFAAOCAQEAePwkhxon+M6WsQjfpYi4qc2yQffvlfD1wrW/hntf4tj7Xh8c99yxie5iO0iVCc04GWNdfNXpztTEBhT06x9csB0N97JhBHZlIVSsdIYP4Pli8zgU80Tc++xfKwo8qXZIXaCh92OVztplosQWEvAFVlzhkTE2uZKX+5OFH3PYOowcJXOwnvT60GCMPRjGnWAVx6Btrzsrd6u/EwfaNQbEVcAY66HlmqT66LSJ1mW1b31oinja3IME77TdzQUK3nsUr7n1BTYjbNPFXxa7u29kTev5kXINZXuAG4rUDTAq1kQx+vH/YNNFIVJL5ChEKBnnBDYRluWHSe2nV8YZh4vgew==\n-----END CERTIFICATE-----",
+ "entity1_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDABdiq4DZafCeecGWKqU+uH2S114YhM0FtIqIIoAnMU7FpHntsk/XCKROVp+uy7CiQK+b+7gC7h7xCW3TNgzOxGW7TJ4pOTUiaDTq65UpcqL1rMrKQBLfktEYXry2myQ+z9FEbYUPtkB0l/hN1YGl+qx4NvtpTi+75g8vPMWiCwDTWL1blHbgbsWx9ULSeLWvf9vCQVQ5wMf/IiCarHwM1Svo3cu4d4Wssq9cWMohhX0HtQWJbfqOVi3GAL0hcEe7yknwXtiw0iZkDGAVdGvnVgLXZVOWF7UA865gWFv+Jw+Q3u3KRg8bTfzIP0mOv8QCvqCtVdXwy/W0//gLSGIkHAgMBAAECggEABwnWOK7O1Z9wmFPuwbFip7Nw4cimAV67ZDOwlF5a9y/BWss9cB82rK8MXIaMkvQpdhD8S5XciWmSEsb/AtVvT9HqodGONgzxJbk2BBaJewuYV272F2W4E9yW9FR9Ra+S/fnTmAxUDOFYBpKuLE9w+0dsasnD5bh4W8VjJnoQjNJMZIe4yhXaGg1CvbK2mZa82N+opaPZG1Cp7FoihuneBK1JIBQAZuVFL0VfwrRZlxOXdyhNK38Gi79XypJt9otqutePBwisZpY4vk0RmXgYqSl/iCJOUFRCrNl50ZCBxPBmPuJKJ7agC5Rl+B6rEph4oLf8rrJ1v1QlRgvp0+qncQKBgQD6LhpCy5d0xnPZ4XW8lYrPsQKwqYoHWGozerri0I0L6lFNmnzb5mC2BZ2j3NkkXm3zjnl8AjISLnbQO+vpGQAYUQ1YMKxTjFY6He3LkaQ8VWq9abp2ZqoY+f6wl1NS8Ajs9gxnsuoKf+7DwXPcOg34C1ls9IQIoHp+FS+pvAbwPwKBgQDEfWZgvkwI/G4P9ap4NDqCek7ywaIMBTKuwuTEB6DOwo5RAqLEsCx8/zlxQpZsXRoV6ljc19buwozIR6vRDyFN5YtVzINqoPkQRt24Rwfv8aD4Efe9o26RsNiK1v12hcM8uw0o9tuUEGvMRB0C5zfhqPPRoyvYtR169xmkRzA1OQKBgQDTETAwqEkoSQGWkWNebh/FZ4ZoezCrT8mRkvq2vtIwekiLm0m50FxJVOWRSJKd31kOAWa227dYpYCx38wBRYlR+peyjqXkeuKId5WWiFIyYLIECX45xBW/qrJYU+MFduNg35jJqCVSJGrRFpXj98qvOQwCIyx9hr6xPriOj8aeywKBgQC1wD0L7dqNUVpJfvdhWwZoHNScLkpDjidUikhrn5a6yjI0cxi2kTOFp9utjWdw8ymm7e+m0CwrDGCG/ezLY5Bt6o9ZHOTOo/neeX6r72BDLaG09YHQs7UaJJgYzUcE9DQlH+zLYJS5v/rGppajUddZ8BsgZiuvt9B+JsVcNNE92QKBgF78U6tVmJJpInHQyjLl5eRPQTiUeEOSTCEAraA87kv1KB9n0iwTo++blM/I3cVMkW7i7g1ZGA+c4d9zKCOy0uRfoDmpGFwI1vnGakdl3XT8ezgeE+pD7zxYrXKTPQx0eV4+qgWLUmVNUA+kXJOpb2yBNYxfcLecoCbpR4x7HD6f\n-----END PRIVATE KEY-----",
+ "root2": "-----BEGIN CERTIFICATE-----\nMIIDnzCCAoegAwIBAgIUMnAzPZktX2v5nFtmSiAnfkfDjaYwDQYJKoZIhvcNAQELBQAwXzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMRAwDgYDVQQHDAdUb3JvbnRvMQ0wCwYDVQQKDARLb25nMRAwDgYDVQQLDAdHYXRld2F5MRAwDgYDVQQDDAdhcGl0ZXN0MB4XDTI0MDMxNDEzNTA0MloXDTM0MDMxMjEzNTA0MlowXzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMRAwDgYDVQQHDAdUb3JvbnRvMQ0wCwYDVQQKDARLb25nMRAwDgYDVQQLDAdHYXRld2F5MRAwDgYDVQQDDAdhcGl0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuCcsGtP1Kfpeq5xFElXHxIrhMOVKRYtJ9GtKj7dvyn3u1h1EeQC9pRCaSY6/th2fqTlOexq/Fsa5dvVLoHSRlCRzAR7KdG9AcYvF5IvmNgggY2TTWI1YHn9+ssQPsYrhFBMJzMNsxXzKcJBfqTczTHG9GX3J9nESzXMoGHyRjDH3kddt4xCAwPQTklDJbRZD8uJf8vSvqWPL/c2ZWiO/JLU2V7xttU9x/klv7jqqW/E2R8iLNlXfQauBNsdVH4o/cE1Xdq8k3TDmt2iHIiWqLnGuwATqWKw0MeIUX5woe3HDMW9ftBRblp4hS4iYAbNhK3HC21YNzCdgHnD0ZPuLLwIDAQABo1MwUTAdBgNVHQ4EFgQUqaR79LNHO7yTbhAYSS0aUJ64NJwwHwYDVR0jBBgwFoAUqaR79LNHO7yTbhAYSS0aUJ64NJwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAIMo9xKfC9987V5sAGL/jayfH95vxOCyfXubk8pq5xgbb9mokDkzdN7Wgs+mg1xxchCNpJXQcrCkt98xZvC5s6lV34r2j5SmNxbDsDa4/P9t53eeSvYAvXyhk0M4Bhj+lUbt98YOuKcaem82csn8R9sLb6iafqVlJLicuZ5O3+FDG+pFbKnGnFU30qiQzl2o7l/y9aViXBrSI46qLYSQ849QX9i9p5mRVHZ/PfxLtEPLcvQHOzRNgqjXYkei3wGlMMnADSebDTDqnho9niSUjM/C0/KHCEiVDI8YN39itmtY5NvEd5nJxPorRPY4tGJQBobB7vlqlcWeSztOGC6mhbw==\n-----END CERTIFICATE-----",
+ "entity2_cert": "-----BEGIN CERTIFICATE-----\nMIIDjjCCAnagAwIBAgIUaaEh0AgWx9gGSvvErngDzwSZo5MwDQYJKoZIhvcNAQELBQAwXzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMRAwDgYDVQQHDAdUb3JvbnRvMQ0wCwYDVQQKDARLb25nMRAwDgYDVQQLDAdHYXRld2F5MRAwDgYDVQQDDAdhcGl0ZXN0MB4XDTI0MDMxNDEzNTIxN1oXDTM0MDMxMjEzNTIxN1owXzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMRAwDgYDVQQHDAdUb3JvbnRvMQ0wCwYDVQQKDARLb25nMRAwDgYDVQQLDAdHYXRld2F5MRAwDgYDVQQDDAdhcGl0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsIlBP4VMJg2wRbpHtUUBzmroNEZwW9HStzI+sZMGe0uA9zcHS2hblaC6nhri0UoCd0CvjxSQyWMX+eVVPnfjitar2Lo/NUzoqbt1WsFL8KFVIes+cs7Z6+e6c1QFowumsNSQN9xRd6cIfIMTZkcvg3JXuxk31KJKq5ffJrQxPYkzDA21r3mocKsBSI5lB8J6oPHtqP35J8pKJLYirSbO7soUYk3/ZnNxX+1hDud0gx1iuebuZHvRypqDETxufLHYWyrEyKm8zMgCFir9pl928FHmevwn/vwu0EhoOnNiw0k1scztybKziU/gfqHzATFIDnGoVtU5bXJ6YKd0+h7zSwIDAQABo0IwQDAdBgNVHQ4EFgQUleLjRahHM83YKdKy9/fR3z7HBjcwHwYDVR0jBBgwFoAUqaR79LNHO7yTbhAYSS0aUJ64NJwwDQYJKoZIhvcNAQELBQADggEBAAHQWJxh/IKLStw36/w7wJAiiFewsmpFBfXHzL9Tx0JrRdLjT1rDgDZCgyzLZB6m055bOnAxFoG+I2HUfrvxliD54I+X762yp8e/KN10tb3WnzBkbi/t9D2N/AZTnk5jB5i42N3sLpU1P/jVqYzKfAXSu7DW1V63us7o2zJwpvSabmbNnkbBhvkwcX1rfJsaniaCQreftq0Aicq5DE5VYkPP87TvjYh8zVF+rO6hO72zria/eORbXWlZX/JGi8lsfdKZ+s/o0RPe2ta9EPQy4H/Ky5CDJPUQ8UUn3mtDBiSOPKzDQRXZZqaq7owdRmrB7DlRQUhFB2mCvOw4eT5K6Ok=\n-----END CERTIFICATE-----",
+ "entity2_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwiUE/hUwmDbBFuke1RQHOaug0RnBb0dK3Mj6xkwZ7S4D3NwdLaFuVoLqeGuLRSgJ3QK+PFJDJYxf55VU+d+OK1qvYuj81TOipu3VawUvwoVUh6z5yztnr57pzVAWjC6aw1JA33FF3pwh8gxNmRy+Dcle7GTfUokqrl98mtDE9iTMMDbWveahwqwFIjmUHwnqg8e2o/fknykoktiKtJs7uyhRiTf9mc3Ff7WEO53SDHWK55u5ke9HKmoMRPG58sdhbKsTIqbzMyAIWKv2mX3bwUeZ6/Cf+/C7QSGg6c2LDSTWxzO3JsrOJT+B+ofMBMUgOcahW1Tltcnpgp3T6HvNLAgMBAAECggEADy/fox1Ojb+DuJvr61ZCb4WW8FhW9i8413XFGvWxCw5Vy+65++zAZEDtjQh3FtYtHCE+eSqlwg16VI88JCGgO6N4KS7snSM+qMttrx8N72eRsVdRigLtkHPhAGbsDTjFYFUJCyUzBP4jpsKH+raJCd/eWEQ1H1/O2LA6DFRYIBj25iXSpMJB3+H22Yb9XYp5CoarvOjfeZsx8ya0jzvX/DZ2KQWyPyHvAwrlASgO/f/I+HODZw6hAO6h7sn314qpY/rRsphammR+XUs3CXO56Yx1isgksU6ZIwn8Hc0c3CssW2vbKzzLKbichJdRjkKUVDppLCZSlY05I5WaeTclMQKBgQDdKd8rLS3zDZmKRFC2esVZMfigooOkormSawOzEqtXM3uVJSkpv7expfWGJZ/TRpVemVrosJsJVZTf+NFS0WtBrFZ5E25ktsIlHRAWz5eLYeeRM0o+GOH56kG1l6Zo8PpksjMUXoCZyzmok0m9GcpKN7mE3kB/x3E1zkxbKr79jQKBgQDMV9eBw7sEBiys5DEEtSKcE1t1AHTdK6zvYKvR3XvGjeFS2uQ1mjFvZ2yL/jWNKg+lTu4bMwOPuCYbz66k0WTgqnX0074POHtXCNWwDIHXhQt5uvM7SqnxQoNUp6YUAhLbO7WFL5/f3ycZQs3ljdruouwzhPRROE/4hmz8/EDiNwKBgB3FYys2u7a6H+8C3zKRODuXhHZrKflhhkV1blwOqU3luj8Lb40VJ68cPf6+1dLk73p6fkhQba5F/PJhVhwl4KBIiKNnwDaCe4Pvc5j+fNHgbgGXu+i8BL9Vj17YeSgrOCmR61pUlk8dkjdlDEoOTLLM6YY/PMN2n4C0soabLHaJAoGBAJxNIJGodpdIZcnFLd1s89EwWUdwlvNJ4hij+Ckp4fBpPA3VUrPRgri1Amk+hBW7M4Z9jlGNvs6iy7uAKxT0+dWDbYBcqkcYK/fR0Sm9F2AT60gsgLo8jSr2dmA9cCJjx6qbzGfYmtBx74U5/SX2UJ0yT7vEmeC6UylFIX6NMNAXAoGAbON6v3NdTJFWmsNFZboTeckzsgk0tVxhtIYGPIao7fVIcyTwNzOSVADc6TJ37Jge4ttSCbKwe1t/k3yCTkUSh/ThFI+LmfqJlqCBq9K7cEl7wZmgigqRL/AZ8ObV9B0EtReRINdmo//yTZyc316IvSFib6IoD9NBfGeWC+8UEuk=\n-----END PRIVATE KEY-----"
+ },
+ "tls": {
+ "cert": "-----BEGIN CERTIFICATE-----\nMIIDfTCCAmWgAwIBAgIUJCdVEgDy5Wmy6M7GG68L65WrF9UwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgMAk9OMRAwDgYDVQQHDAdUb3JvbnRvMQ0wCwYDVQQKDARLb25nMRAwDgYDVQQLDAdHYXRld2F5MRgwFgYDVQQDDA9mb29AZXhhbXBsZS5jb20wHhcNMjQwNDI2MTIwODUyWhcNMjUwNDI2MTIwODUyWjBnMQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xEDAOBgNVBAcMB1Rvcm9udG8xDTALBgNVBAoMBEtvbmcxEDAOBgNVBAsMB0dhdGV3YXkxGDAWBgNVBAMMD2Zvb0BleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMI734h+6TfIhNVSDFEmt8DeXrOCugDUTwh2oA8qwwf3+evp5mJbaZPycCdDSz3hbCbHk1eJY/G7nOupVVjMJCW1lc31B5SJBuZ9gOuYPYFFhGHsfqgd9XXwQp0Nor4Iib51eVkxLtQYiHlw5wX1i6xkni+8rqzWN6bgAGzSYn414HMCI8yqev1O+lR5Q91CjEJqZtQHgGDaYjenkCffY3yLd56IiXmy/C9X7ragvuGG9Yw7aUD7UexsrDT3MuwzAe6EmzIN2HqIhXi0Z+uocnCuH3AW1vuGvtFsa6O1F6zcKEBBdUd/5EhflqJ33CRti8ESWgykOwbIizrQUzMIwGUCAwEAAaMhMB8wHQYDVR0OBBYEFAZDHQzWTizKtysPl0wxSrkH+Y3kMA0GCSqGSIb3DQEBCwUAA4IBAQBrExtBQ3sN/c8XJsGffXxbYUhWxXJHpX3zAhAetVUO+qE/I/8r6z1WXjXJQI0WxV5Bo8fjF7UiDtXqh4A6EkSeTRLL/6iy/dvgpRfh5fod8bjxQD6XE4AjW9X20Gez+uSzvonNLMHibV9u65CBViGhVb+pWfisG96TX2dfSVi7Jp38Kh07ow/4AUNrUm56cUg7Cn4yeRENg3QOCjEs7SChurCoY11sfMiUFK8o7jID9mWRSgV+Nfa0uf9ERG3gbbqW3Hpen6aHGN4eq2vLmCtgNaGwMASSxMZA3hOpOOj14pUdMtx0xNGxlMdYz/bXVQKt9JRdk2M9m0Z2n7dQcQ9d\n-----END CERTIFICATE-----",
+ "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCO9+Ifuk3yITVUgxRJrfA3l6zgroA1E8IdqAPKsMH9/nr6eZiW2mT8nAnQ0s94Wwmx5NXiWPxu5zrqVVYzCQltZXN9QeUiQbmfYDrmD2BRYRh7H6oHfV18EKdDaK+CIm+dXlZMS7UGIh5cOcF9YusZJ4vvK6s1jem4ABs0mJ+NeBzAiPMqnr9TvpUeUPdQoxCambUB4Bg2mI3p5An32N8i3eeiIl5svwvV+62oL7hhvWMO2lA+1HsbKw09zLsMwHuhJsyDdh6iIV4tGfrqHJwrh9wFtb7hr7RbGujtRes3ChAQXVHf+RIX5aid9wkbYvBEloMpDsGyIs60FMzCMBlAgMBAAECggEAVG5CVMsWBbGcTD5GLGBwL955u/5vbDzdvggSu4UTEjuw3+PkyYyC8FTKe8a8xkFfCdxnJ/4I++NZ6vbIqrjSXbH5VHXWvbJy1W5oxACBe9gEG6z5wYqEUKgHzM1ImOKNPcVC4oqGmJuxd1ikk+qQtHdoa8fRBkB4IInupV8ugT2XJQvTp7L3zhTjf1VgF/zqizcQnt+OhGf8jHhRgxXHfzeRK3EGIT01NF0kgPOhqU+H+cnGFpIDZFhGgu5QQq9arIC7PrJsngWYRZuro1jyAbbz+O+wq73BPP3kLS6U8YpnDnl6QNWY3EwSqg1Ys1pQ9BfAFUHzZkgHViy6Eqv3YQKBgQD5f+O5VvrIy28nMLR1Ujzd/0H9SOh5EPP/B25XtvpwZ/wikG5qnJ8mn5OHhrASJdPzUQYOgeaFCeFAFdZcnKJJEaSidXSPhyNu802cm0MjtBknM+m4iH/MIM/jF57Yuw4roRLvj/xW4H1yXpnMYLI14HFcevbuyOJfk96SFV2JSwKBgQDHS194TYMg9769wB8bDsBbKAp4uxH30fZWo7T2DlbxmP1SAjhxqRIx2qfIeLEHV4u+yWQVg2llqnABshmMLu5GTrfJlQIkf6HZrGb/qKKnR/fEZ0b8IIL8OgA79PMSll3VSkZiHE4NELVdEzKmuiSv+EY6V8mLblJ1SJaYgOT/DwKBgQDAyGfeWu32XvKpFoIpsPylEEod+sreWRIfec9x6UnDtyVkU7Ntn01+G4l0C8dajm4iMPX8GbVUENlCml0DPGhNC1vpx9Xj3Sis30SWAIY1PUC+QOeUFayhvqzDLQopLwf5AEHQZ7I0MsvbpmsyN3kcFiuhqMXKpd/8uBYfTHD01wKBgD9VcaqOn3xrnv84PepgXrrZV7KUaJpj7DAOaGTFm9W9c8uLDxr3yuNzBF0OFdwqcyuXXqoDkAwSBPRVAA9vnqNTQs7Z/stQkEPXqQfYNJCKq2+MDp/zSQ0U9ZwYNOMUTA+LQiDNs/Qs+iXxAui/QQd3FAJYgaFA1w2Umt8uHxf7AoGAWKOted+WqZPApdoG/frae4bn2UfI9QupK+UckoRUPiG8bpEjyXHZvY1XhB+6wKigtmujkMnHmGKrXP/Nj5GuGmhauKH7Tr6lpXnbFoUsRaEDy5D/3Z+jVlltGneyyNjtqvVL99GPr13PvdBNYbUmCHfmSRLCWGjpW0aCmPN5Szo=\n-----END PRIVATE KEY-----"
+ },
+ "dpop": {
+ "private_jwk": {
+ "p": "v5U_BFvl2rQuUVe0XJxJsvmEXfnsMPnFr_6Q1F_N32s7-3htTtN39Wsx2zrP0G6NL-UM6VdgBY8kcrz968gS_eNYlnWnF-YXh3vgqPeRvjo8Tv-xdKPoBZMtSH-esIhqZSn51Gmd9Yc0KQFZ6h9W6hyuV-45QMhAEYBaSGndtlU",
+ "kty": "RSA",
+ "q": "sXi_MIWLFOmLc2RopHDabl69m2fC-90UZzsGH3TjuaC5uS951REQXTakZIMrOX1uFIAlZcVwP1vnU-rlIACn-g5-B_T0cBZhZdskHipGN_bEIRk8CBZZ5Q44Ud4TbJO8JaQXtTgIoGdSyI9Pmq3C5mQf73EMOpcpSloYslItuj8",
+ "d": "HJhCROX3VK1v3QVkmQAyuXXCRA_fjBnB-h1C51o2D25GiWQfoia-pj0TfuGChQ1xEAZnLpwwNdzxwvR97K7IQI81Ulu3QjTDwYVzRpowoTJTlTgK9m8Ec-CzgFgUTAiwFskIJDbSvRZLamtgy__86uXYuuDdsrONpmN99socBb6Um3oGUO7fejyPO7-0sG8ddBa4OYbGr4JQKlFpnhWNFtWckpD8r2ObpahQfON8XWzDfqvGRnOSyV3NgGGzulufzbfGn9K5I_IslrGmLeh6oBo3B4GM2gXC-E3HabMeluFw0Piuj1rPdGI0uu9BhaLnswa6xdIQNy15Dl2YURmSUQ",
+ "e": "AQAB",
+ "use": "sig",
+ "kid": "vVwcGu06pNZAxZ3hOi7K0j9tDzv1D2X7CndgXnamCP8",
+ "qi": "LEy4NqHVSjQ7qXBHMJrOFeEv5gt3_rBy-EsvjNtW23B-nTVGIay2zoChLutaLWJUETwVNmZpp5iN7dbRVEytD1kOz5_6ONWYsxJ12wrN_b0qgfaTU4UvmL0__JAAmsw6jsy9FKZ9Azex82NPLOAfF9eOFq0yQCPCsC0GdEgPNe0",
+ "dp": "ex0qoN95Z0Dwnt8wjW71wWitEkMf8B0iV8XMRl3Gi7N6mVA7VjN43CJlqI8zqqrQWh3kXTMEBq6E6qLevh0Vb0ggzpeOqEH5jtwhdBOb-dWsmcuguub1nFeG-xbf4GvZi2cdH847buPStiMjJ9u527pQozr4WdQVr-l-mxhFHP0",
+ "alg": "RS256",
+ "dq": "K8hGIopPjZEwPf9_cVIGEUR-MmCe0dPwvKbrE8eahqkIKI911wKmrthUJhfWChDPn28dysWys27Q2vsQG50N07rPjNb5ls1uqdouRuLW_-d461F2ZehdtQyVCF1pHVg1CnQ5BRQVzLywx5nTYa2Mb5oz5rZU9f9kyFsBzLxJW7E",
+ "n": "hNCNnDeFsMO8_MsWbp16R1kBYClJaCEO6U6mo08FXScwSoH_BndAWn48PAkraJzeK-WRtT0e_Uyqizw3CAg3uU3w3FlwV04o-PIwer83CQQv48Mv-qphX1hYjr4m5qda0Kqr3-dzkf-lcTpMQzdtBSqhMm4_kNdm6C48NEDuU3BWyM5zM51uM_OitC8Ck_D26V6urCl2wnokzrgCFQzzqSDpdKia9HevqC-yiZ51dGb7Tex5m6Gp64dGK1Z5jSLuiNq5skwnegwBWXRD6a8vf4EX6qMIv6zezWO2Abg3Df_bAVT8QrbbS-wFbzS4q6cQp_dsgMdjOoCx9TQ3avqg6w"
+ },
+ "public_jwk": {
+ "kty": "RSA",
+ "e": "AQAB",
+ "use": "sig",
+ "kid": "vVwcGu06pNZAxZ3hOi7K0j9tDzv1D2X7CndgXnamCP8",
+ "alg": "RS256",
+ "n": "hNCNnDeFsMO8_MsWbp16R1kBYClJaCEO6U6mo08FXScwSoH_BndAWn48PAkraJzeK-WRtT0e_Uyqizw3CAg3uU3w3FlwV04o-PIwer83CQQv48Mv-qphX1hYjr4m5qda0Kqr3-dzkf-lcTpMQzdtBSqhMm4_kNdm6C48NEDuU3BWyM5zM51uM_OitC8Ck_D26V6urCl2wnokzrgCFQzzqSDpdKia9HevqC-yiZ51dGb7Tex5m6Gp64dGK1Z5jSLuiNq5skwnegwBWXRD6a8vf4EX6qMIv6zezWO2Abg3Df_bAVT8QrbbS-wFbzS4q6cQp_dsgMdjOoCx9TQ3avqg6w"
+ },
+ "private_pem": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCE0I2cN4Www7z8\nyxZunXpHWQFgKUloIQ7pTqajTwVdJzBKgf8Gd0Bafjw8CStonN4r5ZG1PR79TKqL\nPDcICDe5TfDcWXBXTij48jB6vzcJBC/jwy/6qmFfWFiOvibmp1rQqqvf53OR/6Vx\nOkxDN20FKqEybj+Q12boLjw0QO5TcFbIznMznW4z86K0LwKT8PbpXq6sKXbCeiTO\nuAIVDPOpIOl0qJr0d6+oL7KJnnV0ZvtN7Hmboanrh0YrVnmNIu6I2rmyTCd6DAFZ\ndEPpry9/gRfqowi/rN7NY7YBuDcN/9sBVPxCtttL7AVvNLirpxCn92yAx2M6gLH1\nNDdq+qDrAgMBAAECggEAHJhCROX3VK1v3QVkmQAyuXXCRA/fjBnB+h1C51o2D25G\niWQfoia+pj0TfuGChQ1xEAZnLpwwNdzxwvR97K7IQI81Ulu3QjTDwYVzRpowoTJT\nlTgK9m8Ec+CzgFgUTAiwFskIJDbSvRZLamtgy//86uXYuuDdsrONpmN99socBb6U\nm3oGUO7fejyPO7+0sG8ddBa4OYbGr4JQKlFpnhWNFtWckpD8r2ObpahQfON8XWzD\nfqvGRnOSyV3NgGGzulufzbfGn9K5I/IslrGmLeh6oBo3B4GM2gXC+E3HabMeluFw\n0Piuj1rPdGI0uu9BhaLnswa6xdIQNy15Dl2YURmSUQKBgQC/lT8EW+XatC5RV7Rc\nnEmy+YRd+eww+cWv/pDUX83fazv7eG1O03f1azHbOs/Qbo0v5QzpV2AFjyRyvP3r\nyBL941iWdacX5heHe+Co95G+OjxO/7F0o+gFky1If56wiGplKfnUaZ31hzQpAVnq\nH1bqHK5X7jlAyEARgFpIad22VQKBgQCxeL8whYsU6YtzZGikcNpuXr2bZ8L73RRn\nOwYfdOO5oLm5L3nVERBdNqRkgys5fW4UgCVlxXA/W+dT6uUgAKf6Dn4H9PRwFmFl\n2yQeKkY39sQhGTwIFlnlDjhR3hNsk7wlpBe1OAigZ1LIj0+arcLmZB/vcQw6lylK\nWhiyUi26PwKBgHsdKqDfeWdA8J7fMI1u9cForRJDH/AdIlfFzEZdxouzeplQO1Yz\neNwiZaiPM6qq0Fod5F0zBAauhOqi3r4dFW9IIM6XjqhB+Y7cIXQTm/nVrJnLoLrm\n9ZxXhvsW3+Br2YtnHR/OO27j0rYjIyfbudu6UKM6+FnUFa/pfpsYRRz9AoGAK8hG\nIopPjZEwPf9/cVIGEUR+MmCe0dPwvKbrE8eahqkIKI911wKmrthUJhfWChDPn28d\nysWys27Q2vsQG50N07rPjNb5ls1uqdouRuLW/+d461F2ZehdtQyVCF1pHVg1CnQ5\nBRQVzLywx5nTYa2Mb5oz5rZU9f9kyFsBzLxJW7ECgYAsTLg2odVKNDupcEcwms4V\n4S/mC3f+sHL4Sy+M21bbcH6dNUYhrLbOgKEu61otYlQRPBU2ZmmnmI3t1tFUTK0P\nWQ7Pn/o41ZizEnXbCs39vSqB9pNThS+YvT/8kACazDqOzL0Upn0DN7HzY08s4B8X\n144WrTJAI8KwLQZ0SA817Q==\n-----END PRIVATE KEY-----\n",
+ "public_pem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhNCNnDeFsMO8/MsWbp16\nR1kBYClJaCEO6U6mo08FXScwSoH/BndAWn48PAkraJzeK+WRtT0e/Uyqizw3CAg3\nuU3w3FlwV04o+PIwer83CQQv48Mv+qphX1hYjr4m5qda0Kqr3+dzkf+lcTpMQzdt\nBSqhMm4/kNdm6C48NEDuU3BWyM5zM51uM/OitC8Ck/D26V6urCl2wnokzrgCFQzz\nqSDpdKia9HevqC+yiZ51dGb7Tex5m6Gp64dGK1Z5jSLuiNq5skwnegwBWXRD6a8v\nf4EX6qMIv6zezWO2Abg3Df/bAVT8QrbbS+wFbzS4q6cQp/dsgMdjOoCx9TQ3avqg\n6wIDAQAB\n-----END PUBLIC KEY-----\n"
+ }
+}
+
+
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/index.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/index.ts
new file mode 100644
index 00000000..abe22036
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/index.ts
@@ -0,0 +1,8 @@
+import authDetails from './credentials.json';
+import petstore from './petstore.json';
+import jsonSchemas from './request-validator-schemas.json';
+import openApiSchemas from './oas-validation-schema.json';
+import spaceApi from './space-api.json';
+import * as teams from './kauth_teams';
+
+export { authDetails, petstore, jsonSchemas, openApiSchemas, spaceApi, teams };
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/kauth_teams.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/kauth_teams.ts
new file mode 100644
index 00000000..89737299
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/kauth_teams.ts
@@ -0,0 +1,10 @@
+export enum DefaultTeamNames {
+ ORGANIZATION_ADMIN = 'organization-admin',
+ ORGANIZATION_ADMIN_READONLY = 'organization-admin-readonly',
+ PORTAL_ADMIN = 'portal-admin',
+ RUNTIME_ADMIN = 'runtime-admin',
+ SERVICE_ADMIN = 'service-admin',
+ SERVICE_DEVELOPER = 'service-developer',
+ ANALYTICS_ADMIN = 'analytics-admin',
+ ANALYTICS_VIEWER = 'analytics-viewer',
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/oas-validation-schema.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/oas-validation-schema.json
new file mode 100644
index 00000000..f57deb5b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/oas-validation-schema.json
@@ -0,0 +1,1162 @@
+{
+ "openapi": "3.0.2",
+ "servers": [
+ {
+ "url": "/v3"
+ }
+ ],
+ "info": {
+ "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)",
+ "version": "1.0.17",
+ "title": "Swagger Petstore - OpenAPI 3.0",
+ "termsOfService": "http://swagger.io/terms/",
+ "contact": {
+ "email": "apiteam@swagger.io"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "tags": [
+ {
+ "name": "pet",
+ "description": "Everything about your Pets",
+ "externalDocs": {
+ "description": "Find out more",
+ "url": "http://swagger.io"
+ }
+ },
+ {
+ "name": "store",
+ "description": "Access to Petstore orders",
+ "externalDocs": {
+ "description": "Find out more about our store",
+ "url": "http://swagger.io"
+ }
+ },
+ {
+ "name": "user",
+ "description": "Operations about user"
+ }
+ ],
+ "paths": {
+ "/oas-validation/pet": {
+ "post": {
+ "tags": ["pet"],
+ "summary": "Add a new pet to the store",
+ "description": "Add a new pet to the store",
+ "operationId": "addPet",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ],
+ "requestBody": {
+ "description": "Create a new pet in the store",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "tags": ["pet"],
+ "summary": "Update an existing pet",
+ "description": "Update an existing pet by Id",
+ "operationId": "updatePet",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ },
+ "405": {
+ "description": "Validation exception"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ],
+ "requestBody": {
+ "description": "Update an existent pet in the store",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/oas-validation/pet/findByStatus": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings",
+ "operationId": "findPetsByStatus",
+ "parameters": [
+ {
+ "name": "status",
+ "in": "query",
+ "description": "Status values that need to be considered for filter",
+ "required": false,
+ "explode": true,
+ "schema": {
+ "type": "string",
+ "enum": ["available", "pending", "sold"],
+ "default": "available"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ]
+ }
+ },
+ "/oas-validation/pet/findByTags": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Finds Pets by tags",
+ "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
+ "operationId": "findPetsByTags",
+ "parameters": [
+ {
+ "name": "tags",
+ "in": "query",
+ "description": "Tags to filter by",
+ "required": false,
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid tag value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ]
+ }
+ },
+ "/oas-validation/pet/{petId}": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Find pet by ID",
+ "description": "Returns a single pet",
+ "operationId": "getPetById",
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet to return",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ },
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ]
+ },
+ "post": {
+ "tags": ["pet"],
+ "summary": "Updates a pet in the store with form data",
+ "description": "",
+ "operationId": "updatePetWithForm",
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet that needs to be updated",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "name",
+ "in": "query",
+ "description": "Name of pet that needs to be updated",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "status",
+ "in": "query",
+ "description": "Status of pet that needs to be updated",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ]
+ },
+ "delete": {
+ "tags": ["pet"],
+ "summary": "Deletes a pet",
+ "description": "",
+ "operationId": "deletePet",
+ "parameters": [
+ {
+ "name": "api_key",
+ "in": "header",
+ "description": "",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "Pet id to delete",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid pet value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ]
+ }
+ },
+ "/oas-validation/pet/{petId}/uploadImage": {
+ "post": {
+ "tags": ["pet"],
+ "summary": "uploads an image",
+ "description": "",
+ "operationId": "uploadFile",
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet to update",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "additionalMetadata",
+ "in": "query",
+ "description": "Additional Metadata",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ApiResponse"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": ["write:pets", "read:pets"]
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/octet-stream": {
+ "schema": {
+ "type": "string",
+ "format": "binary"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/oas-validation/store/inventory": {
+ "get": {
+ "tags": ["store"],
+ "summary": "Returns pet inventories by status",
+ "description": "Returns a map of status codes to quantities",
+ "operationId": "getInventory",
+ "x-swagger-router-controller": "OrderController",
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ }
+ ]
+ }
+ },
+ "/oas-validation/store/order": {
+ "post": {
+ "tags": ["store"],
+ "summary": "Place an order for a pet",
+ "description": "Place a new order in the store",
+ "operationId": "placeOrder",
+ "x-swagger-router-controller": "OrderController",
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ }
+ }
+ },
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ },
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/oas-validation/store/order/{orderId}": {
+ "get": {
+ "tags": ["store"],
+ "summary": "Find purchase order by ID",
+ "x-swagger-router-controller": "OrderController",
+ "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.",
+ "operationId": "getOrderById",
+ "parameters": [
+ {
+ "name": "orderId",
+ "in": "path",
+ "description": "ID of order that needs to be fetched",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ }
+ },
+ "delete": {
+ "tags": ["store"],
+ "summary": "Delete purchase order by ID",
+ "x-swagger-router-controller": "OrderController",
+ "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
+ "operationId": "deleteOrder",
+ "parameters": [
+ {
+ "name": "orderId",
+ "in": "path",
+ "description": "ID of the order that needs to be deleted",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ }
+ }
+ },
+ "/oas-validation/user": {
+ "post": {
+ "tags": ["user"],
+ "summary": "Create user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "createUser",
+ "responses": {
+ "default": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ },
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ },
+ "description": "Created user object"
+ }
+ }
+ },
+ "/oas-validation/user/createWithList": {
+ "post": {
+ "tags": ["user"],
+ "summary": "Creates list of users with given input array",
+ "description": "Creates list of users with given input array",
+ "x-swagger-router-controller": "UserController",
+ "operationId": "createUsersWithListInput",
+ "responses": {
+ "200": {
+ "description": "Successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "successful operation"
+ }
+ },
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/oas-validation/user/login": {
+ "get": {
+ "tags": ["user"],
+ "summary": "Logs user into the system",
+ "description": "",
+ "operationId": "loginUser",
+ "parameters": [
+ {
+ "name": "username",
+ "in": "query",
+ "description": "The user name for login",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "password",
+ "in": "query",
+ "description": "The password for login in clear text",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "headers": {
+ "X-Rate-Limit": {
+ "description": "calls per hour allowed by the user",
+ "schema": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "X-Expires-After": {
+ "description": "date in UTC when token expires",
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "content": {
+ "application/xml": {
+ "schema": {
+ "type": "string"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid username/password supplied"
+ }
+ }
+ }
+ },
+ "/oas-validation/user/logout": {
+ "get": {
+ "tags": ["user"],
+ "summary": "Logs out current logged in user session",
+ "description": "",
+ "operationId": "logoutUser",
+ "parameters": [],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/oas-validation/user/{username}": {
+ "get": {
+ "tags": ["user"],
+ "summary": "Get user by user name",
+ "description": "",
+ "operationId": "getUserByName",
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "The name that needs to be fetched. Use user1 for testing. ",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ },
+ "put": {
+ "tags": ["user"],
+ "summary": "Update user",
+ "x-swagger-router-controller": "UserController",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "updateUser",
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "name that need to be deleted",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ },
+ "requestBody": {
+ "description": "Update an existent user in the store",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": ["user"],
+ "summary": "Delete user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "deleteUser",
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "The name that needs to be deleted",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ }
+ }
+ },
+ "externalDocs": {
+ "description": "Find out more about Swagger",
+ "url": "http://swagger.io"
+ },
+ "components": {
+ "schemas": {
+ "Order": {
+ "x-swagger-router-model": "io.swagger.petstore.model.Order",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "example": 10
+ },
+ "petId": {
+ "type": "integer",
+ "format": "int64",
+ "example": 198772
+ },
+ "quantity": {
+ "type": "integer",
+ "format": "int32",
+ "example": 7
+ },
+ "shipDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "status": {
+ "type": "string",
+ "description": "Order Status",
+ "enum": ["placed", "approved", "delivered"],
+ "example": "approved"
+ },
+ "complete": {
+ "type": "boolean"
+ }
+ },
+ "xml": {
+ "name": "order"
+ },
+ "type": "object"
+ },
+ "Customer": {
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "example": 100000
+ },
+ "username": {
+ "type": "string",
+ "example": "fehguy"
+ },
+ "address": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Address"
+ },
+ "xml": {
+ "wrapped": true,
+ "name": "addresses"
+ }
+ }
+ },
+ "xml": {
+ "name": "customer"
+ },
+ "type": "object"
+ },
+ "Address": {
+ "properties": {
+ "street": {
+ "type": "string",
+ "example": "437 Lytton"
+ },
+ "city": {
+ "type": "string",
+ "example": "Palo Alto"
+ },
+ "state": {
+ "type": "string",
+ "example": "CA"
+ },
+ "zip": {
+ "type": "string",
+ "example": 94301
+ }
+ },
+ "xml": {
+ "name": "address"
+ },
+ "type": "object"
+ },
+ "Category": {
+ "x-swagger-router-model": "io.swagger.petstore.model.Category",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "example": 1
+ },
+ "name": {
+ "type": "string",
+ "example": "Dogs"
+ }
+ },
+ "xml": {
+ "name": "category"
+ },
+ "type": "object"
+ },
+ "User": {
+ "x-swagger-router-model": "io.swagger.petstore.model.User",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "example": 10
+ },
+ "username": {
+ "type": "string",
+ "example": "theUser"
+ },
+ "firstName": {
+ "type": "string",
+ "example": "John"
+ },
+ "lastName": {
+ "type": "string",
+ "example": "James"
+ },
+ "email": {
+ "type": "string",
+ "example": "john@email.com"
+ },
+ "password": {
+ "type": "string",
+ "example": 12345
+ },
+ "phone": {
+ "type": "string",
+ "example": 12345
+ },
+ "userStatus": {
+ "type": "integer",
+ "format": "int32",
+ "example": 1,
+ "description": "User Status"
+ }
+ },
+ "xml": {
+ "name": "user"
+ },
+ "type": "object"
+ },
+ "Tag": {
+ "x-swagger-router-model": "io.swagger.petstore.model.Tag",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "tag"
+ },
+ "type": "object"
+ },
+ "Pet": {
+ "x-swagger-router-model": "io.swagger.petstore.model.Pet",
+ "required": ["name", "photoUrls"],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "example": 10
+ },
+ "name": {
+ "type": "string",
+ "example": "doggie"
+ },
+ "category": {
+ "$ref": "#/components/schemas/Category"
+ },
+ "photoUrls": {
+ "type": "array",
+ "xml": {
+ "wrapped": true
+ },
+ "items": {
+ "type": "string",
+ "xml": {
+ "name": "photoUrl"
+ }
+ }
+ },
+ "tags": {
+ "type": "array",
+ "xml": {
+ "wrapped": true
+ },
+ "items": {
+ "$ref": "#/components/schemas/Tag",
+ "xml": {
+ "name": "tag"
+ }
+ }
+ },
+ "status": {
+ "type": "string",
+ "description": "pet status in the store",
+ "enum": ["available", "pending", "sold"]
+ }
+ },
+ "xml": {
+ "name": "pet"
+ },
+ "type": "object"
+ },
+ "ApiResponse": {
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "type": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "##default"
+ },
+ "type": "object"
+ }
+ },
+ "requestBodies": {
+ "Pet": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "description": "Pet object that needs to be added to the store"
+ },
+ "UserArray": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ },
+ "description": "List of user object"
+ }
+ },
+ "securitySchemes": {
+ "petstore_auth": {
+ "type": "oauth2",
+ "flows": {
+ "implicit": {
+ "authorizationUrl": "https://petstore.swagger.io/oauth/authorize",
+ "scopes": {
+ "write:pets": "modify pets in your account",
+ "read:pets": "read your pets"
+ }
+ }
+ }
+ },
+ "api_key": {
+ "type": "apiKey",
+ "name": "api_key",
+ "in": "header"
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/petstore.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/petstore.json
new file mode 100644
index 00000000..bf773c7a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/petstore.json
@@ -0,0 +1,177 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "license": {
+ "name": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "http://petstore.swagger.io/v1"
+ }
+ ],
+ "paths": {
+ "/pets": {
+ "get": {
+ "summary": "List all pets",
+ "operationId": "listPets",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "How many items to return at one time (max 100)",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A paged array of pets",
+ "headers": {
+ "x-next": {
+ "description": "A link to the next page of responses",
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pets"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a pet",
+ "operationId": "createPets",
+ "tags": [
+ "pets"
+ ],
+ "responses": {
+ "201": {
+ "description": "Null response"
+ },
+ "default": {
+ "description": "unexpected error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/pets/{petId}": {
+ "get": {
+ "summary": "Info for a specific pet",
+ "operationId": "showPetById",
+ "tags": [
+ "pets"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "required": true,
+ "description": "The id of the pet to retrieve",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Expected response to a valid request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "unexpected error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Pet": {
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ },
+ "Pets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "Error": {
+ "type": "object",
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/request-validator-schemas.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/request-validator-schemas.json
new file mode 100644
index 00000000..cd914cd0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/request-validator-schemas.json
@@ -0,0 +1,9 @@
+{
+ "maxItems": "{ \"type\": \"object\", \"properties\": { \"where\": { \"type\": \"object\", \"properties\": { \"kpiId\": { \"maxItems\": 2700, \"type\": \"array\", \"items\": { \"maxLength\": 5000, \"minLength\": 0, \"pattern\": \"^[\\\\w\\\\.\\\\-]*$\", \"type\": \"string\", \"description\": \"Marquee unique KPI identifier.\" } } }, \"additionalProperties\": false } }, \"additionalProperties\": false }",
+ "paramSchema": "{\"maxItems\":10000, \"minItems\":1, \"type\":\"array\",\"items\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee unique KPI identifier.\"}}",
+ "dateTime": "{\"$schema\":\"http://json-schema.org/draft-04/schema\",\"type\":\"object\",\"properties\":{\"startTime\":{\"type\":\"string\",\"format\": \"date-time\"}},\"required\":[\"startTime\"]}",
+ "unicode": "{\"type\": \"object\",\"properties\": {\"context\": {\"type\": \"object\", \"properties\": {\"ip\": {\"maxLength\": 2048, \"pattern\": \"^[\\\\t\\\\w&\\\\/()\\\\[\\\\]+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r\\\\-`@~#^={}|?\\\\\\\\??\\\\x{2013}\\\\x{339C}\\\\x{2026}\\\\x{3041}-\\\\x{3096}\\\\x{30A0}-\\\\x{30FF}\\\\x{3400}-\\\\x{4DB5}\\\\x{4E00}-\\\\x{9FCB}\\\\x{F900}-\\\\x{FA6A}\\\\x{2E80}-\\\\x{2FD5}\\\\x{FF5F}-\\\\x{FF9F}\\\\x{3000}-\\\\x{303F}\\\\x{31F0}-\\\\x{31FF}\\\\x{3220}-\\\\x{3243}\\\\x{3280}-\\\\x{337F}\\\\x{FF01}-\\\\x{FF5E}]{0,2048}$\", \"type\": \"string\"}}, \"additionalProperties\": false}}, \"additionalProperties\": false}",
+ "withHeader": "{\"type\":\"object\",\"items\":{\"type\": \"object\",\"properties\":{\"DC\":{\"type\": \"string\",\"pattern\": \"anz|ANZ|anzgcis|ANZGCIS\"}}}}",
+ "bodySchema": "{\"required\":[\"event\"],\"type\":\"object\",\"properties\":{\"event\":{\"type\":\"string\",\"enum\":[\"Compose_Email\",\"Copy_Email_Address\",\"Dial\",\"Export_ContactList\",\"Open_Client\",\"Open_Contact\",\"Open_Tag\",\"View_FA_GIR\",\"View_FA_GIR_Details\",\"Share_Interaction\",\"Failed_Service_Call\",\"Print_Client_Dashboard\",\"View_Client_Dashboard\",\"Edit_Client_Dashboard\",\"View_Opportunity\",\"Open_Call_Log\",\"Open_ClientHub\",\"OutlookAddin_Log_Email\",\"OutlookAddin_Log_Meeting\",\"OutlookAddin_Use_ClientHub_Directory\",\"View_Contacts\",\"View_Interactions\",\"View_Opportunities\",\"View_Fast_Analytics\",\"View_Reporting\",\"Call_Log_Update_Contact_Phone\",\"Call_Log_Create_New_Contact\",\"Call_Log_Select_Suggested_Contact\",\"View_Campaigns\",\"Open_Div_Head_Dashboard_PDF\",\"Open_Div_Head_Memo_PDF\",\"MOBILE_ANALYTICS.CONTACT_ACTIONS.SELECT_CONTACT_LIST\",\"MOBILE_ANALYTICS.CONTACT_ACTIONS.SELECT_SPECIAL_CONTACT_LIST\",\"MOBILE_ANALYTICS.CONTACT_ACTIONS.SELECT_CONTACT\",\"MOBILE_ANALYTICS.CLIENT.SELECT_CLIENT\",\"MOBILE_ANALYTICS.INTERACTION_ACTIONS.SCROLL_ACTIVITIES\",\"MOBILE_ANALYTICS.INTERACTION_ACTIONS.SET_FEED_OFFSET\",\"MOBILE_ANALYTICS.INTERACTION_ACTIONS.SET_FEED_FILTERS\",\"MOBILE_ANALYTICS.INTERACTION_ACTIONS.SET_UPCOMING\",\"MOBILE_ANALYTICS.APP_OPENED\",\"MOBILE_ANALYTICS.HOME_SELECTED\",\"MOBILE_ANALYTICS.CONTACT_LIST_PAGE_SELECTED\",\"MOBILE_ANALYTICS.INTERACTIONS_SELECTED\",\"MOBILE_ANALYTICS.CONTACT_LIST_SELECTED\",\"MOBILE_ANALYTICS.RECENT_MEETINGS_SELECTED\",\"MOBILE_ANALYTICS.GLOBAL_SEARCH_OPENED\",\"MOBILE_ANALYTICS.CONTACT_SELECTED\",\"MOBILE_ANALYTICS.CLIENT_SELECTED\",\"MOBILE_ANALYTICS.INTERACTION_SELECTED\",\"MOBILE_ANALYTICS.NOTES_OPENED\",\"MOBILE_ANALYTICS.DIALED_NUMBER\",\"MOBILE_ANALYTICS.EMAIL_SEND\"]},\"properties\":{\"type\":\"object\",\"properties\":{\"asOf\":{\"type\":\"string\",\"format\":\"date-time\"},\"method\":{\"maxLength\":10,\"pattern\":\"[^\\\\x00-\\\\x08\\\\x0e-\\\\x1f\\\\x7f]$\",\"type\":\"string\"},\"recipientCount\":{\"type\":\"integer\"},\"messages\":{\"maxItems\":100,\"type\":\"array\",\"items\":{\"maxLength\":2000,\"pattern\":\"[^\\\\x00-\\\\x08\\\\x0e-\\\\x1f\\\\x7f]$\",\"type\":\"string\"}},\"id\":{\"maxLength\":50,\"pattern\":\"[0-9a-zA-Z_.\\\\-]+$\",\"type\":\"string\"},\"title\":{\"maxLength\":400,\"pattern\":\"[^\\\\x00-\\\\x08\\\\x0e-\\\\x1f\\\\x7f]$\",\"type\":\"string\"},\"reportsCount\":{\"type\":\"integer\"},\"url\":{\"maxLength\":1000,\"pattern\":\"^[^\\\\x00-\\\\x08\\\\x0e-\\\\x1f\\\\x7f]$\",\"type\":\"string\"},\"addressCount\":{\"type\":\"integer\"},\"statusCode\":{\"type\":\"integer\"}},\"additionalProperties\":false,\"description\":\"Usage Event Properties\"}},\"additionalProperties\":false,\"description\":\"Usage Event\"}",
+ "longSchema": "{\"type\":\"object\",\"properties\":{\"offset\":{\"maximum\":1000000000,\"minimum\":0,\"type\":\"integer\",\"description\":\"The offset of the first result returned (default 0). Can be used in pagination to defined the first item in the list to be returned, for example if you request 100 objects, to query the next page you would specify offset = 100.\"},\"endDate\":{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},\"assetStats\":{\"type\":\"object\",\"properties\":{\"period\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The period used to produce date range.\",\"enum\":[\"1y\",\"3y\",\"5y\",\"10y\"]},\"stats\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false,\"description\":\"Performance statistics.\"},\"lastUpdatedTime\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"type\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Is it rolling, none etc.\",\"enum\":[\"Rolling\",\"Calendar\",\"YTD\"]}},\"additionalProperties\":false,\"description\":\"Performance statistics.\"},\"scroll\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[0-6]?[0-9]s|1m$\",\"type\":\"string\",\"description\":\"Time for which to keep the scroll search context alive, i.e. 1m (1 minute) or 10s (10 seconds)\"},\"orderBy\":{\"maxItems\":1,\"type\":\"array\",\"items\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[-+]?[\\\\w(),]{1,80}\",\"type\":\"string\"},{\"required\":[\"field\"],\"type\":\"object\",\"properties\":{\"field\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Field to be returned\",\"enum\":[\"investmentRate\",\"startingEmmaLegalEntityId\",\"mdapiClass\",\"bidUnadjusted\",\"aggressiveFillsPercentage\",\"vehicleType\",\"totalFatalitiesByState\",\"newActive\",\"dailyRisk\",\"energy\",\"sunshineDailyForecast\",\"sentimentScore\",\"0\",\"1\",\"2\",\"3\",\"correlation\",\"exposure\",\"size\",\"4\",\"5\",\"6\",\"7\",\"marketDataAsset\",\"8\",\"9\",\"buy75cents\",\"unadjustedHigh\",\"sourceImportance\",\"closingYield\",\"wind\",\"sc16\",\"sc15\",\"sc12\",\"sc11\",\"primaryVwapInLimitUnrealizedBps\",\"displayName\",\"minutesToTrade100Pct\",\"sc14\",\"cumulativeVolumeInShares\",\"sc13\",\"newFatalities\",\"buy50bps\",\"numStaffedBeds\",\"upfrontPayment\",\"arrivalMidRealizedCash\",\"sc10\",\"sc05\",\"a\",\"sc04\",\"b\",\"sc07\",\"c\",\"yieldToMaturity\",\"sc06\",\"address\",\"sc01\",\"leg2PaymentFrequency\",\"sc03\",\"sc02\",\"geographyName\",\"borrower\",\"settlePrice\",\"performanceContribution\",\"sc09\",\"mktClass\",\"sc08\",\"collateralization\",\"futureMonthU26\",\"futureMonthU25\",\"futureMonthU24\",\"futureMonthU23\",\"futureMonthU22\",\"statementId\",\"futureMonthU21\",\"modifiedDuration\",\"shortRatesContribution\",\"impliedNormalVolatility\",\"solarGeneration\",\"mtmPrice\",\"swapSpreadChange\",\"realizedArrivalPerformanceUSD\",\"portfolioAssets\",\"pricingdate\",\"tcmCostHorizon3Hour\",\"exchangeRate\",\"potentialBedCapInc\",\"numberCovered\",\"numberOfPositions\",\"openUnadjusted\",\"strikeTime\",\"askPrice\",\"eventId\",\"sectors\",\"additionalPriceNotationType\",\"grossInvestmentQtd\",\"annualizedRisk\",\"estimatedHoldingTimeShort\",\"midcurvePremium\",\"volumeComposite\",\"sharpeQtd\",\"estimatedHoldingTimeLong\",\"external\",\"trackerName\",\"sell50cents\",\"tradePrice\",\"cleared\",\"primeIdNumeric\",\"buy8bps\",\"cid\",\"totalConfirmedSeniorHome\",\"ctdFwdPrice\",\"sinkFactor\",\"temperatureForecast\",\"bidHigh\",\"pnlQtd\",\"buy50cents\",\"sell4bps\",\"receiverDayCountFraction\",\"auctionClosePercentage\",\"targetPrice\",\"bosInBpsDescription\",\"lowPrice\",\"adv22DayPct\",\"matchedMaturitySwapSpread12m\",\"priceRangeInTicksLabel\",\"ticker\",\"notionalUnit\",\"tcmCostHorizon1Day\",\"approval\",\"testMeasure\",\"optionLockOutPeriod\",\"sourceValueForecast\",\"leg2Spread\",\"shortConvictionLarge\",\"ccgName\",\"dollarExcessReturn\",\"gsn\",\"tradeEndDate\",\"receiverRateOption\",\"gss\",\"percentOfMediandv1m\",\"lendables\",\"sell75cents\",\"optionAdjustedSpread\",\"optionAdjustedSwapSpread\",\"bosInTicksLabel\",\"positionSourceId\",\"buy1bps\",\"buy3point5bps\",\"gsSustainRegion\",\"absoluteReturnWtd\",\"deploymentId\",\"assetParametersSeniority\",\"askSpread\",\"flow\",\"futureMonthH26\",\"loanRebate\",\"futureMonthH25\",\"period\",\"indexCreateSource\",\"futureMonthH24\",\"futureMonthH23\",\"futureMonthH22\",\"futureMonthH21\",\"nonUsdOis\",\"realTWIContribution\",\"mktAsset\",\"leg2IndexLocation\",\"twapUnrealizedBps\",\"lastUpdatedMessage\",\"loanValue\",\"optionAdjustedOISSpread\",\"totalReturnPrice\",\"weightedPercentInModel\",\"initLoanSpreadRequired\",\"electionPeriod\",\"fundingAskPrice\",\"historicalBeta\",\"bondRiskPremiumIndex\",\"hitRateYtd\",\"girGsdeerGsfeer\",\"numUnits\",\"assetParametersReceiverFrequency\",\"expenseRatioGrossBps\",\"relativePayoffWtd\",\"ctdPrice\",\"paceOfRollNow\",\"product\",\"leg2ReturnType\",\"agentLenderFee\",\"disseminationId\",\"optionStrikePrice\",\"precipitationType\",\"lowerBound\",\"arrivalMidNormalized\",\"underlyingAsset2\",\"underlyingAsset1\",\"legalEntity\",\"performanceFee\",\"orderState\",\"actualDataQuality\",\"indexRatio\",\"queueInLotsLabel\",\"adv10DayPct\",\"longConvictionMedium\",\"relativeHitRateWtd\",\"dailyTrackingError\",\"sell140cents\",\"sell10bps\",\"aggressiveOffsetFromLast\",\"longitude\",\"newIcu\",\"marketCap\",\"weightedAverageMid\",\"clusterRegion\",\"valoren\",\"averageExecutionPrice\",\"proceedsAssetOISSwapSpread1m\",\"payoffWtd\",\"basis\",\"investmentRateTrend\",\"grossInvestmentMtd\",\"200\",\"hedgeId\",\"201\",\"sharpeMtd\",\"202\",\"203\",\"tcmCostHorizon8Day\",\"204\",\"residualVariance\",\"205\",\"restrictInternalDerivedData\",\"206\",\"207\",\"208\",\"adv5DayPct\",\"209\",\"midpointFillsPercentage\",\"openInterest\",\"turnoverCompositeUnadjusted\",\"fwdPoints\",\"relativeReturnWtd\",\"units\",\"payerRateOption\",\"assetClassificationsRiskCountryName\",\"extMktPoint3\",\"210\",\"211\",\"matchedMaturitySwapSpread\",\"212\",\"cityName\",\"213\",\"hourlyBucket\",\"214\",\"averageImpliedVolatility\",\"totalHospitalizedWithSymptoms\",\"215\",\"216\",\"217\",\"daysOpenRealizedCash\",\"218\",\"219\",\"adjustedHighPrice\",\"proceedsAssetOISSwapSpread\",\"extMktPoint1\",\"direction\",\"extMktPoint2\",\"subRegionCode\",\"assetParametersFixedRate\",\"isEstimatedReturn\",\"valueForecast\",\"totalIcu\",\"positionSourceType\",\"previousCloseUnrealizedCash\",\"minimumDenomination\",\"futureValueNotional\",\"participationRate\",\"obfr\",\"220\",\"221\",\"222\",\"buy9point5bps\",\"223\",\"224\",\"225\",\"optionLockPeriod\",\"226\",\"esMomentumPercentile\",\"227\",\"228\",\"advPercentage\",\"229\",\"leg1AveragingMethod\",\"turnoverComposite\",\"forecastDate\",\"internalIndexCalcRegion\",\"positionType\",\"subAssetClass\",\"shortInterest\",\"referencePeriod\",\"adjustedVolume\",\"ctdFwdYield\",\"secDB\",\"memoryUsed\",\"bpeQualityStars\",\"230\",\"231\",\"232\",\"ctd\",\"233\",\"234\",\"235\",\"236\",\"237\",\"238\",\"239\",\"intendedParticipationRate\",\"leg1PaymentType\",\"tradingPnl\",\"collateralValueRequired\",\"buy45bps\",\"priceToEarningsPositive\",\"forecast\",\"forecastValue\",\"240\",\"pnl\",\"241\",\"242\",\"243\",\"volumeInLimit\",\"244\",\"isTerritory\",\"245\",\"leg2DeliveryPoint\",\"246\",\"247\",\"248\",\"249\",\"tcmCostHorizon4Day\",\"styles\",\"shortName\",\"resetFrequency1\",\"buy4bps\",\"resetFrequency2\",\"otherPriceTerm\",\"bidGspread\",\"openPrice\",\"psId\",\"hitRateMtd\",\"fairVolatility\",\"dollarCross\",\"portfolioType\",\"currency\",\"clusterClass\",\"sell50bps\",\"futureMonthM21\",\"bidSize\",\"arrivalMid\",\"assetParametersExchangeCurrency\",\"candidateName\",\"impliedLognormalVolatility\",\"vwapInLimitUnrealizedCash\",\"ratingMoodys\",\"futureMonthM26\",\"futureMonthM25\",\"futureMonthM24\",\"futureMonthM23\",\"futureMonthM22\",\"flowPct\",\"source\",\"assetClassificationsCountryCode\",\"settleDrop\",\"dataSetSubCategory\",\"sell9point5bps\",\"quantityBucket\",\"optionStyleSDR\",\"oeName\",\"given\",\"leg2DayCountConvention\",\"liquidityScoreSell\",\"delistingDate\",\"weight\",\"accruedInterest\",\"businessScope\",\"wtdDegreeDays\",\"absoluteWeight\",\"measure\",\"temperatureHourlyForecast\",\"icebergTipRateType\",\"sharpeYtd\",\"windSpeedForecast\",\"grossInvestmentYtd\",\"yieldPrice\",\"leg1TotalNotionalUnit\",\"issuePrice\",\"askHigh\",\"expectedDataQuality\",\"regionName\",\"valueRevised\",\"discretionUpperBound\",\"adjustedTradePrice\",\"forecastTime\",\"isoSubdivisionCodeAlpha2\",\"ctdConversionFactor\",\"proceedsAssetSwapSpread\",\"isADR\",\"issueDate\",\"serviceId\",\"yes\",\"gScore\",\"marketValue\",\"entityId\",\"notionalCurrency1\",\"netDebtToEbitda\",\"numUnitsUpper\",\"notionalCurrency2\",\"inLimitParticipationRate\",\"pressureForecast\",\"paid\",\"fixedRate\",\"short\",\"time\",\"buy4point5bps\",\"sell30cents\",\"eventEndDateTime\",\"leg1PaymentFrequency\",\"cmId\",\"taxonomy\",\"buy45cents\",\"measures\",\"seasonalAdjustment\",\"rankWtd\",\"underlyer\",\"createdTime\",\"identifier\",\"priceUnit\",\"tradeReportRefId\",\"subdivisionId\",\"unadjustedLow\",\"buy160cents\",\"portfolioId\",\"zSpread\",\"capFloorAtmFwdRate\",\"esPercentile\",\"tdapi\",\"locationCode\",\"rcic\",\"nameRaw\",\"simonAssetTags\",\"hitRateQtd\",\"primaryVolumeInLimit\",\"precipitationDailyForecastPercent\",\"aumEnd\",\"premium\",\"low\",\"crossGroup\",\"reportRunTime\",\"fiveDayPriceChangeBps\",\"holdings\",\"precipitationDailyForecast\",\"priceMethod\",\"assetParametersFixedRateFrequency\",\"oisXccy\",\"daysOpen\",\"buy110cents\",\"averageSpreadBps\",\"buy55cents\",\"futureMonthQ26\",\"issueSize\",\"futureMonthQ25\",\"futureMonthQ24\",\"futureMonthQ23\",\"futureMonthQ22\",\"pendingLoanCount\",\"futureMonthQ21\",\"priceSpotStopLossUnit\",\"priceRangeInTicksDescription\",\"tradeVolume\",\"primaryCountryRic\",\"optionExpirationFrequency\",\"isActive\",\"useMachineLearning\",\"growthScore\",\"bufferThreshold\",\"buy120cents\",\"matchedMaturitySwapRate\",\"primaryVwap\",\"exchangeTypeId\",\"basisSwapRate\",\"exchangeCode\",\"group\",\"assetParametersTerminationDate\",\"estimatedSpread\",\"yieldChangeOnDay\",\"created\",\"autoTags\",\"tcmCost\",\"sustainJapan\",\"historyStartDate\",\"bidSpread\",\"hedgeTrackingError\",\"windSpeedType\",\"strikePrice\",\"parAssetSwapSpread12m\",\"tradeReportId\",\"adjustedOpenPrice\",\"countryId\",\"point\",\"pnlMtd\",\"totalReturns\",\"lender\",\"annReturn1Year\",\"ctdFwdDv01\",\"effYield7Day\",\"meetingDate\",\"calendarSpreadMispricing\",\"buy140cents\",\"priceNotation2Type\",\"fundFocus\",\"relativeStrike\",\"flagship\",\"additionalPriceNotation\",\"factorCategory\",\"equityDelta\",\"grossWeight\",\"listed\",\"sell7bps\",\"earningsRecordType\",\"mean\",\"askYield\",\"shockStyle\",\"methodology\",\"buy25cents\",\"amountOutstanding\",\"marketPnl\",\"sustainAsiaExJapan\",\"sell6point5bps\",\"neighbourAssetId\",\"countIdeasYtd\",\"simonIntlAssetTags\",\"path\",\"vwapUnrealizedCash\",\"payoffMtd\",\"bosInBpsLabel\",\"bosInBps\",\"pointClass\",\"fxSpot\",\"restrictNamedIndividuals\",\"hedgeVolatility\",\"tags\",\"population\",\"underlyingAssetId\",\"realLongRatesContribution\",\"pctprices_return\",\"domain\",\"buy80cents\",\"forwardTenor\",\"averagePrice\",\"targetPriceRealizedBps\",\"leg2FixedRate\",\"shareClassAssets\",\"annuity\",\"totalCount\",\"quoteType\",\"corporateActionStatus\",\"peggedTipSize\",\"uid\",\"esPolicyPercentile\",\"usdOis\",\"term\",\"restrictInternalGsNtk\",\"tcmCostParticipationRate100Pct\",\"relativeUniverse\",\"measureIdx\",\"fredId\",\"twiContribution\",\"cloudCoverType\",\"delisted\",\"regionalFocus\",\"volumePrimary\",\"assetParametersPayerDesignatedMaturity\",\"buy30cents\",\"fundingBidPrice\",\"series\",\"sell3bps\",\"settlementPrice\",\"quarter\",\"sell18bps\",\"assetParametersFloatingRateOption\",\"realizedVwapPerformanceBps\",\"voteShare\",\"servicingCostShortPnl\",\"totalConfirmed\",\"economicForecast\",\"plotId\",\"clusterDescription\",\"concentrationLimit\",\"windSpeed\",\"observationHour\",\"signal\",\"borrowerId\",\"dataProduct\",\"buy7point5bps\",\"limitPrice\",\"bmPrimeId\",\"dataType\",\"count\",\"conviction\",\"rfqstate\",\"benchmarkMaturity\",\"grossFlowNormalized\",\"buy14bps\",\"factorId\",\"futureMonthV26\",\"stsFxCurrency\",\"futureMonthV25\",\"bidChange\",\"month\",\"futureMonthV24\",\"investmentWtd\",\"futureMonthV23\",\"futureMonthV22\",\"futureMonthV21\",\"expiration\",\"leg2ResetFrequency\",\"controversyScore\",\"proceedAssetSwapSpread\",\"concentrationLevel\",\"importance\",\"assetClassificationsGicsSector\",\"stsAssetName\",\"netExposureClassification\",\"settlementMethod\",\"receiverDesignatedMaturity\",\"title\",\"xRefTypeId\",\"duration\",\"load\",\"alpha\",\"company\",\"settlementFrequency\",\"distAvg7Day\",\"inRiskModel\",\"dailyNetShareholderFlowsPercent\",\"filledNotionalLocal\",\"everHospitalized\",\"meetingNumber\",\"midGspread\",\"daysOpenUnrealizedBps\",\"longLevel\",\"dataDescription\",\"temperatureType\",\"gsideid\",\"repoRate\",\"division\",\"cloudCoverDailyForecast\",\"windSpeedDailyForecast\",\"assetParametersFloatingRateDayCountFraction\",\"tradeAction\",\"action\",\"ctdYield\",\"arrivalHaircutVwapNormalized\",\"priceComponent\",\"queueClockTimeDescription\",\"assetParametersReceiverDayCountFraction\",\"percentMidExecutionQuantity\",\"deltaStrike\",\"cloudCover\",\"assetParametersNotionalCurrency\",\"buy18bps\",\"valueActual\",\"upi\",\"collateralCurrency\",\"originalCountry\",\"field\",\"geographicFocus\",\"daysOpenRealizedBps\",\"fxRiskPremiumIndex\",\"skew\",\"status\",\"notionalCurrency\",\"sustainEmergingMarkets\",\"eventDateTime\",\"leg1DesignatedMaturity\",\"totalPrice\",\"onBehalfOf\",\"testType\",\"accruedInterestStandard\",\"futureMonthZ26\",\"futureMonthZ25\",\"ccgCode\",\"shortExposure\",\"leg1FixedPaymentCurrency\",\"arrivalHaircutVwap\",\"executionDays\",\"recallDueDate\",\"forward\",\"strike\",\"spreadLimit\",\"productScope\",\"assetParametersIssuerType\",\"currency1\",\"currency2\",\"previousCloseRealizedBps\",\"daysSinceReported\",\"eventStatus\",\"vwapInLimit\",\"fwdDuration\",\"return\",\"isPairBasket\",\"notionalAmount\",\"payOrReceive\",\"totalSevere\",\"unexecutedNotionalUSD\",\"expectedResidualPercentage\",\"maturityDate\",\"traceAdvSell\",\"eventName\",\"addressLine2\",\"indicationOfOtherPriceAffectingTerm\",\"unadjustedBid\",\"backtestType\",\"gsdeer\",\"assetParametersIssuer\",\"gRegionalPercentile\",\"coverageChecked\",\"oisXccyExSpike\",\"totalRisk\",\"mnav\",\"marketVolume\",\"swapAnnuity\",\"parAssetSwapSpread\",\"currYield7Day\",\"pressure\",\"shortDescription\",\"futureMonthZ24\",\"feed\",\"futureMonthZ23\",\"mktPoint1\",\"futureMonthZ22\",\"futureMonthZ21\",\"futureMonthZ20\",\"assetParametersCommoditySector\",\"priceNotation2\",\"marketBufferThreshold\",\"priceNotation3\",\"mktPoint3\",\"mktPoint2\",\"leg2Type\",\"mktPoint4\",\"degreeDaysType\",\"investmentIncome\",\"groupType\",\"forwardPointImm\",\"twap\",\"clientShortName\",\"groupCategory\",\"bidPlusAsk\",\"foreignCcyRate\",\"electionOdds\",\"windDirectionForecast\",\"requireAnonClientName\",\"pricingLocation\",\"beta\",\"lastReturnsEndDate\",\"upfrontPaymentDate\",\"sell1point5bps\",\"longExposure\",\"sell4point5bps\",\"tcmCostParticipationRate20Pct\",\"venueType\",\"multiAssetClassSwap\",\"deltaChangeId\",\"implementationId\",\"leg1FixedPayment\",\"esNumericScore\",\"inBenchmark\",\"actionSDR\",\"countIdeasQtd\",\"knockOutPrice\",\"ctdAssetId\",\"buy10bps\",\"precipitation\",\"valueType\",\"betaAdjustedNetExposure\",\"estimatedRodVolume\",\"sell14bps\",\"10\",\"11\",\"12\",\"13\",\"excessReturnPrice\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\"fxPnl\",\"assetClassificationsGicsIndustryGroup\",\"lendingSecId\",\"dollarDuration\",\"equityTheta\",\"dv01\",\"startDate\",\"20\",\"21\",\"22\",\"23\",\"mixedSwap\",\"swaptionPremium\",\"24\",\"25\",\"26\",\"snowfall\",\"liquidityBucketBuy\",\"27\",\"mic\",\"28\",\"latitude\",\"29\",\"mid\",\"impliedRepo\",\"long\",\"firstExecutionTime\",\"coveredBond\",\"regionCode\",\"buy20cents\",\"longWeight\",\"calculationTime\",\"liquidityBucketSell\",\"daysOpenUnrealizedCash\",\"temperature\",\"averageRealizedVariance\",\"ratingFitch\",\"financialReturnsScore\",\"yearOrQuarter\",\"30\",\"31\",\"32\",\"nonSymbolDimensions\",\"33\",\"commoditiesForecast\",\"34\",\"35\",\"covid19ByState\",\"36\",\"37\",\"38\",\"39\",\"percentageExpectedResidual\",\"hospitalName\",\"buy90cents\",\"periodType\",\"assetClassificationsCountryName\",\"totalHospitalized\",\"peggedRefillInterval\",\"fatalitiesProbable\",\"40\",\"administrativeRegion\",\"41\",\"open\",\"42\",\"43\",\"44\",\"45\",\"cusip\",\"totalConfirmedByState\",\"46\",\"ideaActivityTime\",\"47\",\"48\",\"49\",\"windAttribute\",\"spreadOptionAtmFwdRate\",\"netExposure\",\"isLegacyPairBasket\",\"issuerType\",\"buy70cents\",\"strikeReference\",\"assetCount\",\"50\",\"51\",\"isOrderInLimit\",\"52\",\"53\",\"54\",\"fundamentalMetric\",\"55\",\"56\",\"quoteStatusId\",\"57\",\"absoluteValue\",\"closingReport\",\"58\",\"previousTotalConfirmed\",\"59\",\"longTenor\",\"multiplier\",\"buy40cents\",\"assetCountPriced\",\"voteDirection\",\"impliedRepoRate\",\"settlementCurrency\",\"wtdDegreeDaysForecast\",\"indicationOfCollateralization\",\"futureMonthN26\",\"60\",\"lendingPartnerFee\",\"futureMonthN25\",\"61\",\"futureMonthN24\",\"62\",\"primaryVwapRealizedBps\",\"futureMonthN23\",\"63\",\"futureMonthN22\",\"64\",\"futureMonthN21\",\"65\",\"66\",\"67\",\"68\",\"69\",\"breakEvenInflation\",\"pnlYtd\",\"leg1ReturnType\",\"tenor2\",\"resetFrequency\",\"assetParametersPayerFrequency\",\"degreeDaysForecast\",\"isManuallySilenced\",\"buy3bps\",\"lastUpdatedById\",\"legalEntityAcct\",\"targetShareholderMeetingDate\",\"70\",\"71\",\"72\",\"paceOfRollp0\",\"73\",\"74\",\"controversyPercentile\",\"leg1NotionalCurrency\",\"75\",\"complianceEffectiveTime\",\"expirationDate\",\"76\",\"77\",\"78\",\"79\",\"floatingRateDayCountFraction\",\"callLastDate\",\"factorReturn\",\"passiveFlowRatio\",\"composite5DayAdv\",\"marginalContributionToRisk\",\"closeDate\",\"temperatureHourForecast\",\"newIdeasWtd\",\"assetClassSDR\",\"yieldToWorst\",\"80\",\"closingPrice\",\"81\",\"turnoverCompositeAdjusted\",\"comment\",\"sourceSymbol\",\"82\",\"83\",\"84\",\"askUnadjusted\",\"85\",\"86\",\"restrictExternalDerivedData\",\"87\",\"88\",\"89\",\"askChange\",\"countIdeasMtd\",\"endDate\",\"sunshine\",\"contractType\",\"momentumType\",\"specificRisk\",\"mdapi\",\"payoffQtd\",\"loss\",\"midcurveVol\",\"sell6bps\",\"tradingCostPnl\",\"priceNotationType\",\"price\",\"paymentQuantity\",\"90\",\"91\",\"92\",\"93\",\"94\",\"95\",\"96\",\"97\",\"98\",\"redemptionDate\",\"99\",\"leg2NotionalCurrency\",\"subRegion\",\"benchmark\",\"tcmCostParticipationRate15Pct\",\"fiscalYear\",\"recallDate\",\"esgMetricValue\",\"internal\",\"gender\",\"assetClassificationsGicsIndustry\",\"adjustedBidPrice\",\"lowUnadjusted\",\"MACSSecondaryAssetClass\",\"confirmedPerMillion\",\"dataSourceId\",\"integratedScore\",\"buy7bps\",\"arrivalMidUnrealizedCash\",\"knockInPrice\",\"event\",\"isIntradayAuction\",\"locationName\",\"coupon\",\"percentageAuctionExecutedQuantity\",\"avgYield7Day\",\"originalDisseminationId\",\"totalOnVent\",\"twapUnrealizedCash\",\"stsCreditMarket\",\"onsCode\",\"passiveTouchFillsPercentage\",\"seniority\",\"leg1Index\",\"highUnadjusted\",\"submissionEvent\",\"TVProductMnemonic\",\"avgTradeRateLabel\",\"lastActivityDate\",\"disseminationTime\",\"priceToCash\",\"buy10cents\",\"navSpread\",\"venueMIC\",\"dollarTotalReturn\",\"blockUnit\",\"midSpread\",\"istatProvinceCode\",\"totalRecoveredByState\",\"repurchaseRate\",\"dataSource\",\"totalBeingTested\",\"clearedOrBilateral\",\"metricName\",\"askGspread\",\"forecastHour\",\"leg2PaymentType\",\"calSpreadMisPricing\",\"totalTestedNegative\",\"rate366\",\"platform\",\"rate365\",\"fixedRateFrequency\",\"rate360\",\"isContinuous\",\"value\",\"payerDesignatedMaturity\",\"productType\",\"mdv22Day\",\"twapRealizedBps\",\"testMeasureLabel\",\"quantity\",\"reportId\",\"indexWeight\",\"MACSPrimaryAssetClass\",\"trader\",\"leg2PriceType\",\"totalActive\",\"gsid2\",\"matchedMaturityOISSwapSpread\",\"valuationDate\",\"restrictGsFederation\",\"positionSource\",\"tcmCostHorizon6Hour\",\"buy200cents\",\"vwapUnrealizedBps\",\"priceToBook\",\"isin\",\"plId\",\"lastReturnsStartDate\",\"collateralValueVariance\",\"year\",\"forecastPeriod\",\"callFirstDate\",\"dataSetIds\",\"economicTermsHash\",\"numBeds\",\"sell20bps\",\"clientType\",\"percentageCloseExecutedQuantity\",\"macaulayDuration\",\"availableInventory\",\"est1DayCompletePct\",\"relativeHitRateYtd\",\"createdById\",\"marketDataType\",\"realShortRatesContribution\",\"metricCategory\",\"annualizedCarry\",\"valuePrevious\",\"transmissionClassification\",\"avgTradeRate\",\"shortLevel\",\"version\",\"categoryType\",\"policyRateExpectation\",\"uploadDate\",\"blockOffFacility\",\"unrealizedVwapPerformanceUSD\",\"paceOfRollp75\",\"earningsPerSharePositive\",\"numIcuBeds\",\"bucketVolumeInPercentage\",\"estimatedTradingCost\",\"eid\",\"relativeReturnQtd\",\"assessedTestMeasure\",\"mktQuotingStyle\",\"expirationTenor\",\"priceLimit\",\"marketModelId\",\"receiverFrequency\",\"realizedCorrelation\",\"issueStatus\",\"collateralValueActual\",\"atmFwdRate\",\"tcmCostParticipationRate75Pct\",\"close\",\"esProductImpactScore\",\"equityVega\",\"executedFillQuantity\",\"lenderPayment\",\"fiveDayMove\",\"valueFormat\",\"windChillForecast\",\"targetNotional\",\"fillLegId\",\"rationale\",\"realizedTwapPerformanceBps\",\"lastUpdatedSince\",\"totalTests\",\"equitiesContribution\",\"simonId\",\"congestion\",\"notes\",\"totalProbableSeniorHome\",\"eventCategory\",\"averageFillRate\",\"unadjustedOpen\",\"criticality\",\"bidAskSpread\",\"arrivalMidUnrealizedBps\",\"optionType\",\"terminationDate\",\"queriesPerSecond\",\"liquidityType\",\"creditLimit\",\"rankQtd\",\"combinedKey\",\"girFxForecast\",\"effectiveTenor\",\"girCommoditiesForecast\",\"relativeHumidityDailyForecast\",\"std30DaysSubsidizedYield\",\"annualizedTrackingError\",\"futureMonthF26\",\"futureMonthF25\",\"volSwap\",\"futureMonthF24\",\"heatIndexDailyForecast\",\"futureMonthF23\",\"realFCI\",\"blockTradesAndLargeNotionalOffFacilitySwaps\",\"futureMonthF22\",\"buy1point5bps\",\"futureMonthF21\",\"expirationSettlementDate\",\"absoluteReturnQtd\",\"grossExposure\",\"volume\",\"adv\",\"shortConvictionMedium\",\"completeTestMeasure\",\"exchange\",\"esPolicyScore\",\"rollVolumeStd\",\"temperatureDailyForecast\",\"relativePayoffQtd\",\"onLoanPercentage\",\"twapRemainingSlices\",\"fairVariance\",\"hitRateWtd\",\"previousCloseRealizedCash\",\"realizedVolatility\",\"unexecutedQuantity\",\"proceedsAssetSwapSpread1m\",\"cloneParentId\",\"windSpeedHourlyForecast\",\"etfFlowRatio\",\"assetParametersReceiverRateOption\",\"buy60cents\",\"securitySubTypeId\",\"message\",\"stsRatesCountry\",\"sell65cents\",\"horizon\",\"wouldIfGoodLevel\",\"bufferThresholdRequired\",\"faceValue\",\"rollVolumeHist\",\"counterPartyStatus\",\"composite22DayAdv\",\"percentageFarExecutedQuantity\",\"loanSpreadRequired\",\"assetClass\",\"sovereignSpreadContribution\",\"ric\",\"bucketEndTime\",\"rateType\",\"totalFatalitiesSeniorHome\",\"loanStatus\",\"shortWeight\",\"geographyId\",\"sell7point5bps\",\"nav\",\"fiscalQuarter\",\"versionString\",\"payoffYtd\",\"marketImpact\",\"eventType\",\"assetCountLong\",\"sell180cents\",\"spot\",\"applicationId\",\"indicativeClosePrice\",\"swapSpread\",\"tradingRestriction\",\"assetParametersPayOrReceive\",\"priceSpotEntryUnit\",\"unrealizedArrivalPerformanceBps\",\"city\",\"pnlWtd\",\"covariance\",\"bucketVolumeInShares\",\"commodityForecast\",\"valid\",\"stsCommodity\",\"initialPricingDate\",\"indicationOfEndUserException\",\"windDirectionHourlyForecast\",\"esScore\",\"yield\",\"fatalitiesUnderlyingConditionsPresent\",\"priceRangeInTicks\",\"paceOfRollp25\",\"dayCloseRealizedUSD\",\"pctChange\",\"brightnessType\",\"futureMonth3M\",\"numberOfRolls\",\"isoCountryCodeNumeric\",\"priceType\",\"realizedVwapPerformanceUSD\",\"fuelType\",\"bbid\",\"vegaNotionalAmount\",\"fatalitiesUnderlyingConditionsAbsent\",\"effectiveDate\",\"capped\",\"rating\",\"optionCurrency\",\"isCloseAuction\",\"volatility\",\"avgVentUtil\",\"underlyingAssetIds\",\"buy6point5bps\",\"vwapInLimitRealizedCash\",\"estimatedClosingAuctionVolume\",\"sell2bps\",\"annualRisk\",\"eti\",\"vwapInLimitRealizedBps\",\"rankMtd\",\"marketBuffer\",\"futureMonthJ24\",\"lastUploadedTime\",\"futureMonthJ23\",\"oeId\",\"futureMonthJ22\",\"futureMonthJ21\",\"bbidEquivalent\",\"initBufferThresholdRequired\",\"leg2DesignatedMaturity\",\"matchedMaturityOISSwapRate\",\"fairPrice\",\"participationRateInLimit\",\"extMktClass\",\"priceCurrency\",\"failedCount\",\"leg1IndexLocation\",\"supraStrategy\",\"dayCountConvention\",\"roundedNotionalAmount1\",\"roundedNotionalAmount2\",\"factorSource\",\"futureMonthJ26\",\"lendingSecType\",\"futureMonthJ25\",\"leverage\",\"forecastDay\",\"optionFamily\",\"generatorOutput\",\"priceSpotStopLossValue\",\"kpiId\",\"windGeneration\",\"percentageMidExecutedQuantity\",\"borrowCost\",\"knockOutDirection\",\"riskModel\",\"assetParametersVendor\",\"fairValue\",\"openTime\",\"pressureHourlyForecast\",\"localCcyRate\",\"endUserException\",\"sell90cents\",\"executionVenue\",\"primaryVwapInLimitRealizedBps\",\"approveRebalance\",\"adjustedClosePrice\",\"lmsId\",\"rebateRate\",\"sell130cents\",\"sell32bps\",\"paceOfRollp50\",\"priceMoveVsArrival\",\"strikeRelative\",\"pressureType\",\"buy40bps\",\"priceNotation\",\"strategy\",\"issueStatusDate\",\"lenderIncome\",\"pbClientId\",\"istatRegionCode\",\"sell9bps\",\"ownerId\",\"composite10DayAdv\",\"maxLoanBalance\",\"ideaActivityType\",\"sell60cents\",\"ideaSource\",\"everOnVent\",\"buy15cents\",\"unadjustedAsk\",\"contributionName\",\"givenPlusPaid\",\"lastFillPrice\",\"shortConvictionSmall\",\"upfrontPaymentCurrency\",\"spotSettlementDate\",\"matrixOrder\",\"dateIndex\",\"payerDayCountFraction\",\"assetClassificationsIsPrimary\",\"breakEvenInflationChange\",\"buy130cents\",\"dwiContribution\",\"asset2Id\",\"averageFillPrice\",\"depthSpreadScore\",\"sell10cents\",\"subAccount\",\"buy65cents\",\"bondCdsBasis\",\"vendor\",\"dataSet\",\"notionalAmount2\",\"notionalAmount1\",\"queueingTime\",\"annReturn5Year\",\"volumeStartOfDay\",\"priceNotation3Type\",\"assetParametersFloatingRateDesignatedMaturity\",\"executedNotionalLocal\",\"businessSponsor\",\"unexplained\",\"seasonalAdjustmentShort\",\"metric\",\"ask\",\"closePrice\",\"endTime\",\"sell100cents\",\"executionTimestamp\",\"buy180cents\",\"absoluteStrike\",\"sell3point5bps\",\"liquidityScoreBuy\",\"paymentFrequency\",\"expenseRatioNetBps\",\"metricType\",\"rankYtd\",\"leg1Spread\",\"coverageRegion\",\"absoluteReturnYtd\",\"dayCountConvention2\",\"fwdtier\",\"degreeDays\",\"turnoverAdjusted\",\"priceSpotTargetValue\",\"marketDataPoint\",\"numOfFunds\",\"tradeTime\",\"executionId\",\"turnoverUnadjusted\",\"leg1FloatingIndex\",\"hedgeAnnualizedVolatility\",\"benchmarkCurrency\",\"futuresContract\",\"name\",\"aum\",\"leg1DayCountConvention\",\"cbsCode\",\"folderName\",\"apiUsage\",\"twapInterval\",\"uniqueId\",\"optionExpirationDate\",\"swaptionAtmFwdRate\",\"liveDate\",\"corporateActionType\",\"primeId\",\"description\",\"assetClassificationsIsCountryPrimary\",\"rebateRateLimit\",\"factor\",\"daysOnLoan\",\"longConvictionSmall\",\"sell40cents\",\"relativePayoffYtd\",\"gsfeer\",\"relativeHitRateQtd\",\"wam\",\"wal\",\"quantityccy\",\"backtestId\",\"dirtyPrice\",\"corporateSpreadContribution\",\"relativeHumidityHourlyForecast\",\"multipleScore\",\"betaAdjustedExposure\",\"dividendPoints\",\"brightness\",\"assetParametersReceiverDesignatedMaturity\",\"bosInTicksDescription\",\"testId\",\"impliedCorrelation\",\"normalizedPerformance\",\"bytesConsumed\",\"swaptionVol\",\"estimatedClosingVolume\",\"issuer\",\"dividendYield\",\"marketType\",\"numUnitsLower\",\"sourceOrigin\",\"proceedsAssetSwapSpread3m\",\"totalQuantity\",\"internalUser\",\"sell40bps\",\"redemptionOption\",\"notionalUnit2\",\"notionalUnit1\",\"sedol\",\"roundingCostPnl\",\"midYield\",\"unexecutedNotionalLocal\",\"sustainGlobal\",\"endingDate\",\"proceedsAssetSwapSpread12m\",\"grossInvestmentWtd\",\"annReturn3Year\",\"sharpeWtd\",\"discountFactor\",\"relativeReturnMtd\",\"priceChangeOnDay\",\"buy100cents\",\"forwardPoint\",\"fci\",\"recallQuantity\",\"fxPositioning\",\"gsidEquivalent\",\"categories\",\"extMktAsset\",\"quotingStyle\",\"errorMessage\",\"midPrice\",\"proceedsAssetSwapSpread6m\",\"stsEmDm\",\"embeddedOption\",\"tcmCostHorizon2Day\",\"ageBand\",\"returnsEnabled\",\"runId\",\"queueInLots\",\"tenderOfferExpirationDate\",\"midcurveAnnuity\",\"lendingFundNavTrend\",\"cloudCoverForecast\",\"tcmCostParticipationRate5Pct\",\"defaultBackcast\",\"newsOnIntensity\",\"priceFormingContinuationData\",\"adjustedShortInterest\",\"newHospitalized\",\"assetParametersStrike\",\"buy35cents\",\"leg2TotalNotional\",\"assetParametersEffectiveDate\",\"annReturn10Year\",\"numAdultIcuBeds\",\"daysToExpiration\",\"continuationEvent\",\"wiId\",\"marketCapCategory\",\"historicalVolume\",\"buy5cents\",\"eventStartDate\",\"leg1FixedRate\",\"equityGamma\",\"rptId\",\"grossIncome\",\"emId\",\"assetCountInModel\",\"stsCreditRegion\",\"minTemperature\",\"bucketStartTime\",\"fillType\",\"closeTime\",\"failPct\",\"isoCountryCodeAlpha2\",\"isoCountryCodeAlpha3\",\"amount\",\"lendingFundAcct\",\"rebate\",\"electionType\",\"relativeHitRateMtd\",\"impliedVolatility\",\"spread\",\"variance\",\"wtdDegreeDaysDailyForecast\",\"swaptionAnnuity\",\"buy6bps\",\"g10Currency\",\"humidityForecast\",\"relativePeriod\",\"user\",\"customer\",\"leg1ResetFrequency\",\"queueClockTimeLabel\",\"paceOfRollp100\",\"assetClassificationsGicsSubIndustry\",\"dewPointHourlyForecast\",\"locationType\",\"facetDivisionalReportingGroupId\",\"realizedTwapPerformanceUSD\",\"swapRate\",\"algoExecutionStyle\",\"clientContact\",\"minTemperatureHour\",\"tradingCurrency\",\"totalByOnset\",\"agencySwapSpread\",\"rank\",\"mixedSwapOtherReportedSDR\",\"humidity\",\"dataSetCategory\",\"vwapRealizedBps\",\"buy9bps\",\"totalTested\",\"fatalitiesConfirmed\",\"universeId1\",\"assetParametersPayerDayCountFraction\",\"universeId2\",\"bidLow\",\"bucketizePrice\",\"fairVarianceVolatility\",\"covid19\",\"clientExposure\",\"leg2TotalNotionalUnit\",\"sell45cents\",\"gsSustainSubSector\",\"sinkable\",\"isReal\",\"maxTemperatureHour\",\"leg2AveragingMethod\",\"jsn\",\"sell160cents\",\"knockInDirection\",\"dayCloseUnrealizedUSD\",\"tenor\",\"pricingConvention\",\"popularity\",\"floatingRateOption\",\"hedgeValueType\",\"assetParametersClearingHouse\",\"disclaimer\",\"payerFrequency\",\"loanFee\",\"deploymentVersion\",\"buy16bps\",\"tradeDayCount\",\"priceToSales\",\"newIdeasQtd\",\"subdivisionName\",\"adjustedAskPrice\",\"factorUniverse\",\"arrivalRt\",\"internalIndexCalcAgent\",\"excessMarginValue\",\"transactionCost\",\"centralBankSwapRate\",\"previousNewConfirmed\",\"unrealizedVwapPerformanceBps\",\"degreeDaysDailyForecast\",\"positionAmount\",\"heatIndexHourlyForecast\",\"maRank\",\"fxPositioningSource\",\"eventStartDateTime\",\"impliedVolatilityByDeltaStrike\",\"mqSymbol\",\"numTotalUnits\",\"corporateAction\",\"leg1PriceType\",\"assetParametersPayerRateOption\",\"sell20cents\",\"leg2FixedPaymentCurrency\",\"gRegionalScore\",\"hardToBorrow\",\"sell5bps\",\"rollVwap\",\"wpk\",\"bespokeSwap\",\"assetParametersExpirationDate\",\"countryName\",\"carry\",\"startingDate\",\"loanId\",\"onboarded\",\"liquidityScore\",\"longRatesContribution\",\"sourceDateSpan\",\"annYield6Month\",\"underlyingDataSetId\",\"closeUnadjusted\",\"valueUnit\",\"quantityUnit\",\"adjustedLowPrice\",\"isMomentum\",\"longConvictionLarge\",\"oad\",\"rate\",\"couponType\",\"client\",\"convictionList\",\"passiveEtfRatio\",\"futureMonthG26\",\"futureMonthG25\",\"futureMonthG24\",\"futureMonthG23\",\"typeOfReturn\",\"futureMonthG22\",\"servicingCostLongPnl\",\"excessMarginPercentage\",\"futureMonthG21\",\"totalMild\",\"realizedArrivalPerformanceBps\",\"precipitationDailyForecastInches\",\"exchangeId\",\"leg2FixedPayment\",\"tcmCostHorizon20Day\",\"realm\",\"bid\",\"hedgeValue\",\"orderStartTime\",\"isAggressive\",\"floatingRateDesignatedMaturity\",\"percentageNearExecutedQuantity\",\"orderId\",\"hospitalType\",\"dayCloseRealizedBps\",\"precipitationHourlyForecast\",\"marketCapUSD\",\"auctionFillsPercentage\",\"highPrice\",\"absoluteShares\",\"fixedRateDayCountFraction\",\"model\",\"unrealizedTwapPerformanceUSD\",\"id\",\"maturity\",\"deltaChange\",\"index\",\"unrealizedArrivalPerformanceUSD\",\"icebergSlippage\",\"sell120cents\",\"futureMonthX26\",\"assetTypes\",\"futureMonthX25\",\"bcid\",\"mktPoint\",\"futureMonthX24\",\"restrictionStartDate\",\"touchLiquidityScore\",\"futureMonthX23\",\"futureMonthX22\",\"factorCategoryId\",\"securityTypeId\",\"futureMonthX21\",\"investmentYtd\",\"leg2Notional\",\"sell1bps\",\"sell200cents\",\"expectedCompletionDate\",\"spreadOptionVol\",\"sell80cents\",\"inflationSwapRate\",\"activeQueries\",\"sell45bps\",\"embededOption\",\"eventSource\",\"qisPermNo\",\"settlement\",\"shareclassId\",\"feature2\",\"feature3\",\"stsCommoditySector\",\"exceptionStatus\",\"salesCoverage\",\"feature1\",\"tcmCostParticipationRate10Pct\",\"eventTime\",\"positionSourceName\",\"deliveryDate\",\"interestRate\",\"side\",\"dynamicHybridAggressiveStyle\",\"complianceRestrictedStatus\",\"borrowFee\",\"everIcu\",\"noWorseThanLevel\",\"updateTime\",\"loanSpread\",\"tcmCostHorizon12Hour\",\"dewPoint\",\"researchCommission\",\"buy2bps\",\"assetClassificationsRiskCountryCode\",\"newIdeasMtd\",\"varSwapByExpiry\",\"sellDate\",\"aumStart\",\"assetParametersSettlement\",\"maxTemperature\",\"acquirerShareholderMeetingDate\",\"countIdeasWtd\",\"arrivalRtNormalized\",\"reportType\",\"sourceURL\",\"estimatedReturn\",\"high\",\"sourceLastUpdate\",\"sunshineForecast\",\"quantityMW\",\"sell70cents\",\"sell110cents\",\"pnodeId\",\"humidityType\",\"prevCloseAsk\",\"level\",\"impliedVolatilityByExpiration\",\"assetParametersFixedRateDayCountFraction\",\"esMomentumScore\",\"leg2Index\",\"netWeight\",\"portfolioManagers\",\"bosInTicks\",\"assetParametersCouponType\",\"expectedResidualQuantity\",\"rollDate\",\"dynamicHybridSpeed\",\"capFloorVol\",\"targetQuantity\",\"submitter\",\"no\",\"notional\",\"esDisclosurePercentage\",\"closeExecutedQuantityPercentage\",\"twapRealizedCash\",\"isOpenAuction\",\"leg1Type\",\"wetBulbTempHourlyForecast\",\"cleanupPrice\",\"total\",\"filledNotionalUSD\",\"assetId\",\"testStatus\",\"mktType\",\"lastUpdatedTime\",\"yield30Day\",\"buy28bps\",\"proportionOfRisk\",\"futureMonthK23\",\"futureMonthK22\",\"futureMonthK21\",\"primaryEntityId\",\"cross\",\"ideaStatus\",\"contractSubtype\",\"sri\",\"fxForecast\",\"fixingTimeLabel\",\"isETF\",\"100\",\"101\",\"102\",\"103\",\"104\",\"fillId\",\"excessReturns\",\"105\",\"106\",\"dollarReturn\",\"orderInLimit\",\"expiryTime\",\"107\",\"returnOnEquity\",\"108\",\"109\",\"futureMonthK26\",\"futureMonthK25\",\"futureMonthK24\",\"restrictionEndDate\",\"queueInLotsDescription\",\"volumeLimit\",\"objective\",\"navPrice\",\"leg1UnderlyingAsset\",\"110\",\"111\",\"112\",\"113\",\"privatePlacementType\",\"114\",\"hedgeNotional\",\"115\",\"116\",\"askLow\",\"intendedPRate\",\"117\",\"118\",\"119\",\"expiry\",\"avgMonthlyYield\",\"periodDirection\",\"prevRptId\",\"earningsPerShare\",\"strikePercentage\",\"esProductImpactPercentile\",\"vwapRealizedCash\",\"parAssetSwapSpread1m\",\"prevCloseBid\",\"minimumIncrement\",\"tcmCostHorizon16Day\",\"investmentMtd\",\"settlementDate\",\"weightedAverageMidNormalized\",\"120\",\"121\",\"122\",\"salesPerShare\",\"123\",\"124\",\"125\",\"unadjustedClose\",\"126\",\"127\",\"128\",\"129\",\"loanDate\",\"matchedMaturitySwapSpread1m\",\"collateralPercentageActual\",\"vwapInLimitUnrealizedBps\",\"metricValue\",\"autoExecState\",\"totalRecovered\",\"relativeReturnYtd\",\"130\",\"tickServer\",\"131\",\"132\",\"133\",\"134\",\"cumulativeVolumeInPercentage\",\"135\",\"136\",\"137\",\"138\",\"139\",\"realTimeRestrictionStatus\",\"tradeType\",\"settlementType\",\"netChange\",\"numberOfUnderliers\",\"swapType\",\"forecastType\",\"leg1Notional\",\"sellSettleDate\",\"140\",\"141\",\"142\",\"143\",\"144\",\"145\",\"146\",\"147\",\"newIdeasYtd\",\"managementFee\",\"148\",\"149\",\"parAssetSwapSpread3m\",\"sell36bps\",\"matchedMaturitySwapSpread3m\",\"sourceId\",\"country\",\"vwap\",\"touchSpreadScore\",\"ratingSecondHighest\",\"sell24bps\",\"150\",\"151\",\"152\",\"frequency\",\"153\",\"154\",\"activityId\",\"155\",\"estimatedImpact\",\"sell35cents\",\"156\",\"loanSpreadBucket\",\"157\",\"158\",\"coronavirusGlobalActivityTracker\",\"159\",\"underlyers\",\"assetParametersPricingLocation\",\"eventDescription\",\"icebergMaxSize\",\"assetParametersCoupon\",\"details\",\"sector\",\"avgBedUtilRate\",\"buy20bps\",\"epidemic\",\"mctr\",\"exchangeTime\",\"historicalClose\",\"fipsCode\",\"160\",\"161\",\"buy32bps\",\"162\",\"163\",\"ideaId\",\"commentStatus\",\"marginalCost\",\"164\",\"165\",\"166\",\"167\",\"168\",\"clientWeight\",\"169\",\"leg1DeliveryPoint\",\"sell5cents\",\"liqWkly\",\"unrealizedTwapPerformanceBps\",\"region\",\"temperatureHour\",\"upperBound\",\"sell55cents\",\"170\",\"171\",\"numPediIcuBeds\",\"172\",\"bidYield\",\"173\",\"174\",\"expectedResidual\",\"175\",\"176\",\"optionPremium\",\"177\",\"178\",\"179\",\"ownerName\",\"parAssetSwapSpread6m\",\"zScore\",\"sell12bps\",\"eventStartTime\",\"matchedMaturitySwapSpread6m\",\"turnover\",\"priceSpotTargetUnit\",\"coverage\",\"gPercentile\",\"180\",\"181\",\"182\",\"cloudCoverHourlyForecast\",\"183\",\"184\",\"lendingFundNav\",\"sourceOriginalCategory\",\"percentCloseExecutionQuantity\",\"185\",\"latestExecutionTime\",\"186\",\"187\",\"arrivalMidRealizedBps\",\"188\",\"189\",\"location\",\"scenarioId\",\"terminationTenor\",\"queueClockTime\",\"discretionLowerBound\",\"tcmCostParticipationRate50Pct\",\"ratingLinear\",\"previousCloseUnrealizedBps\",\"190\",\"191\",\"subAssetClassForOtherCommodity\",\"192\",\"forwardPrice\",\"193\",\"type\",\"194\",\"strikeRef\",\"195\",\"196\",\"197\",\"cumulativePnl\",\"198\",\"shortTenor\",\"sell28bps\",\"fundClass\",\"199\",\"unadjustedVolume\",\"buy36bps\",\"positionIdx\",\"windChillHourlyForecast\",\"secName\",\"impliedVolatilityByRelativeStrike\",\"percentADV\",\"leg1TotalNotional\",\"contract\",\"paymentFrequency1\",\"paymentFrequency2\",\"bespoke\",\"repoTenor\",\"sell15cents\",\"investmentQtd\",\"heatIndexForecast\",\"ratingStandardAndPoors\",\"qualityStars\",\"leg2FloatingIndex\",\"sourceTicker\",\"primaryVwapUnrealizedBps\",\"gsid\",\"lendingFund\",\"sensitivity\",\"dayCount\",\"sell16bps\",\"relativeBreakEvenInflationChange\",\"sell25cents\",\"varSwap\",\"buy5point5bps\",\"blockLargeNotional\",\"sell2point5bps\",\"capacity\",\"sectorsRaw\",\"primaryVwapInLimit\",\"shareclassPrice\",\"tradeSize\",\"priceSpotEntryValue\",\"buy8point5bps\",\"symbolDimensions\",\"buy24bps\",\"observation\",\"optionTypeSDR\",\"scenarioGroupId\",\"averageImpliedVariance\",\"avgTradeRateDescription\",\"fraction\",\"assetCountShort\",\"collateralPercentageRequired\",\"sell5point5bps\",\"date\",\"zipCode\",\"totalStdReturnSinceInception\",\"sourceCategory\",\"volumeUnadjusted\",\"passiveRatio\",\"priceToEarnings\",\"orderDepth\",\"annYield3Month\",\"netFlowStd\",\"encodedStats\",\"buy5bps\",\"runTime\",\"askSize\",\"absoluteReturnMtd\",\"std30DaysUnsubsidizedYield\",\"resource\",\"averageRealizedVolatility\",\"traceAdvBuy\",\"newConfirmed\",\"sell8bps\",\"bidPrice\",\"sell8point5bps\",\"targetPriceUnrealizedBps\",\"esNumericPercentile\",\"leg2UnderlyingAsset\",\"csaTerms\",\"relativePayoffMtd\",\"dailyNetShareholderFlows\",\"buy2point5bps\",\"cai\",\"executedNotionalUSD\",\"systemTime\",\"totalHomeIsolation\",\"stationName\",\"passPct\",\"openingReport\",\"midcurveAtmFwdRate\",\"precipitationForecast\",\"equityRiskPremiumIndex\",\"fatalitiesUnderlyingConditionsUnknown\",\"buy12bps\",\"clearingHouse\",\"dayCloseUnrealizedBps\",\"stsRatesMaturity\",\"liqDly\",\"contributorRole\",\"totalFatalities\",\"adjustedClose\",\"averageValue\",\"avgInterestRate\",\"basisDuration\",\"bestMonthDate\",\"bloombergTicker\",\"capexDepreciation\",\"capexSales\",\"cashConversion\",\"category\",\"convexity\",\"countryCode\",\"croci\",\"currentValue\",\"dacf\",\"dailyVolatility\",\"divYield\",\"dpsGrowth\",\"drawdownOverReturn\",\"ebitdaGrowth\",\"ebitdaMargin\",\"ebitGrowth\",\"ebitMargin\",\"evGci\",\"fcfConversion\",\"fcfYield\",\"gci\",\"grossProfTotAssets\",\"historicCPR\",\"incrementalMargin\",\"industry\",\"informationRatio\",\"interestCover\",\"lastChange\",\"lastChangePct\",\"lastDate\",\"lastValue\",\"liborMatchedMaturitySwap\",\"liborOAS\",\"liborProceedsASW\",\"liborzSpread\",\"manEarningGrowthMeas\",\"marginalRiskContribution\",\"maxDrawdown\",\"netDebtEbitda\",\"netDebtEquity\",\"niGrowth\",\"niMargin\",\"oisMatchedMaturitySwap\",\"oisProceedsASW\",\"oiszSpread\",\"optionStyle\",\"payup\",\"positionDate\",\"preTaxProfitGrowth\",\"riskPremiaStyles\",\"roce\",\"rolldown\",\"salesGrowth\",\"sharpeRatio\",\"totalDebtCapital\",\"totalDebtTotalAsset\",\"totalReturn\",\"unleveredFcfYield\",\"worstMonthDate\"]},\"direction\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"enum\":[\"asc\",\"desc\"]}},\"additionalProperties\":false}]}},\"scrollId\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w=-]{0,512}$\",\"type\":\"string\",\"description\":\"Scroll identifier to be used to retrieve the next batch of results\"},\"delay\":{\"type\":\"integer\",\"description\":\"Number of minutes to delay returning data\"},\"limit\":{\"maximum\":10000,\"minimum\":0,\"type\":\"integer\",\"description\":\"Limit on the number of objects to be returned in the response. Can range between 1 and 10000\"},\"where\":{\"type\":\"object\",\"properties\":{\"investmentRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The rate of return on an investment. In the context of securities lending, it is the rate being earned on the reinvested collateral received from the borrower.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"startingEmmaLegalEntityId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Starting EMMA Legal Entity ID (LE) for which the SCR client agreement was signed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mdapiClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"MDAPI Asset Class.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted bid level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"aggressiveFillsPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of total order filled at aggressive touch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vehicleType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Type of investment vehicle. Only viewable after having been granted additional access to asset information.\",\"enum\":[\"Comingled HF\",\"Co-Investment\",\"Hybrid / Drawdown\",\"UCITS\",\"'40 Act\",\"Other\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"totalFatalitiesByState\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of fatalities by state.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newActive\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Change in number of active cases from previous day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dailyRisk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Daily Risk Value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"energy\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Energy price component.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sunshineDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for sunshine in percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sentimentScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A value representing a sentiment indicator.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"correlation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market implied correlation between two tenors.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Exposure of a given asset or portfolio in the denominated currency of the asset or portfolio.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"size\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Size of market execution.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketDataAsset\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '`\\\"-=\\\\^]{0,256}$\",\"type\":\"string\",\"description\":\"The market data asset (e.g. USD, USD/EUR).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy75cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 75 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unadjustedHigh\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted high level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceImportance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Source importance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"closingYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Closing yield.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wind\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of the wind attribute selected.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc16\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Document Ongoing SCR Compliance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc15\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restrict Access To Specified Named Personnel.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc12\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Exclude From Indications Of Interest.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc11\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Replace Client True Name With Alias Name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryVwapInLimitUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealised performance vs Primary VWAP In Limit, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"displayName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Display Name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"minutesToTrade100Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Minutes to trade 100 percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc14\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Notify Strat Supervisors Of Personnel Assignment Limitation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cumulativeVolumeInShares\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast of the cumulative volume of shares from start of day up to the end of the bucket interval.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc13\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restrict Access In Client Relationship Management Platforms To Sales Personnel Covering Client.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newFatalities\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"New fatal cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy50bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 50 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numStaffedBeds\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of adult bed, pediatric bed, birthing room, or newborn ICU bed (excluding newborn bassinets) maintained in a patient care area for lodging patients in acute, long term, or domiciliary areas of the hospital.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"upfrontPayment\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Upfront payment fee.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalMidRealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs Arrival Mid, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc10\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Exclude From Calibration And Research.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc05\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restrict Access To Trade Execution Mandate Personnel.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"a\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Stock specific coefficient.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc04\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restrict Access To Sales Personnel Covering Both Client And Executed Product.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"b\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Stock specific coefficient.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc07\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Exclude From Market Share Survey Submissions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"c\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Stock specific coefficient.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yieldToMaturity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The return an investor realizes on a bond sold at the mid price at maturity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc06\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restrict Access To Agency Personnel.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"address\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+#%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Street address.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc01\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Define Information Barriers Policy.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2PaymentFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Same as Leg 1 Payment Frequency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc03\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Educate Sales And Traders About SCR Agreements.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc02\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Train On Information Barriers Policy.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"geographyName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9, ]*$\",\"type\":\"string\",\"description\":\"Name of the country or region for which metric is calculated.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"borrower\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Name of the borrowing entity on a securities lending agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settlePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Settling level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"performanceContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The contribution of an underlying asset to the overall performance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc09\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Exclude From Market Color Commentary By Sales And Trading.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mktClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"The MDAPI Class (e.g. Swap, Cash).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sc08\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Exclude From Systematic Market Color Reporting And Advertised Volumes.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"collateralization\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If an SB swap is not cleared, an indication of whether a swap is Uncollateralized (UC), Partially Collateralized (PC), One-Way Collateralized (OC), or Fully Collateralized (FC).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthU26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthU25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthU24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthU23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthU22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"statementId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\"type\":\"string\",\"description\":\"Statement UUID.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthU21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"modifiedDuration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Measure of a bond's price sensitivity to changes in interest rates.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortRatesContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of short rate component to FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedNormalVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market implied volatility measured using a normal model in bps/day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"solarGeneration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Solar generation value provided by source.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mtmPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Amount of profit or loss realized over statement period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swapSpreadChange\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Swap spread change.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedArrivalPerformanceUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated Arrival (in limit) since order inception ??? realized in $.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"portfolioAssets\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total amount of assets under management across all share classes.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pricingdate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Pricing Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon3Hour\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 3 hour time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exchangeRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Amount that an exchange charges in fees, in mils. Negative numbers denote rebates.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"potentialBedCapInc\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The number of beds by which a hospital can increase bed capacity, derived from the difference between number of allowed licensed beds and number of currently staffed beds.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numberCovered\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of underlyers covered by risk model.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numberOfPositions\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of positions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"openUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted open level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"strikeTime\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Time at which order began executing.\",\"format\":\"date-time\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Ask Price (price offering to sell).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Goldman Sachs internal event identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sectors\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w ]{0,128}$\",\"type\":\"string\",\"description\":\"Sector classifications of an asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"additionalPriceNotationType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Basis points, Price, Yield, Spread, Coupon, etc., depending on the type of SB swap, which is calculated at affirmation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossInvestmentQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total gross investment Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annualizedRisk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Annualized risk.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedHoldingTimeShort\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The estimated holding time for a short position.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midcurvePremium\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Midcurve premium.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volumeComposite\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Accumulated number of shares, lots or contracts traded according to the market convention at all exchanges.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sharpeQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sharpe ratio Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedHoldingTimeLong\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The estimated holding time for a long position.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"external\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether entity was created by an external user.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"trackerName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Name of the tracker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell50cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 50 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Last trade price or value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cleared\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether or not an SB swap transaction is going to be cleared by a derivatives clearing organization.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primeIdNumeric\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Prime ID as a number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy8bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 8 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cid\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\d]{1,20}\",\"type\":\"string\",\"description\":\"Goldman Sachs internal company identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalConfirmedSeniorHome\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Confirmed cases in Senior homes and care facilities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ctdFwdPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Cheapest to deliver bond fwd price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sinkFactor\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The level to which a sinkable bond has currently sunk.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"temperatureForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast temperature of diff types of given units.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidHigh\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The highest bid (price willing to buy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pnlQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total PnL Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy50cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 50 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell4bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 4 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"receiverDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"auctionClosePercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of total order being reserved for the Closing Auction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"targetPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Target Price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bosInBpsDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,100}$\",\"type\":\"string\",\"description\":\"Description of the Stock's Bid-Offer Spread in Basis points on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lowPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Low level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adv22DayPct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Median number of shares or units of a given asset traded over a 21 day period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturitySwapSpread12m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Matched maturity swap spread vs 12m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceRangeInTicksLabel\":{\"anyOf\":[{\"type\":\"string\",\"description\":\"Label of the Stock's Price Range in Ticks on the particular date.\",\"enum\":[\"Small\",\"Medium\",\"Large\",\"Very Large\",\"Above Medium\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"ticker\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Ticker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of reported notional price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon1Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 1 day time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"approval\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Approval rating.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"testMeasure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Generic field to hold aggregatable numeric measure for test result. For e.g delay for timeliness or variance for correctness.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionLockOutPeriod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the first allowable exercise date of the option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceValueForecast\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\".*\",\"type\":\"string\",\"description\":\"TE own projections.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2Spread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortConvictionLarge\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The count of short ideas with large conviction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ccgName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Name of the clinical commissioning group.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dollarExcessReturn\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The dollar excess return of an instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsn\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Goldman Sachs internal product number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeEndDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"End date of the trade.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"receiverRateOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.()\\\\/* -]{0,32}\",\"type\":\"string\",\"description\":\"The underlying benchmark for the payer, e.g. USD-LIBOR-BBA, EUR-EURIBOR-TELERATE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gss\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Goldman Sachs internal product symbol.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentOfMediandv1m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of median daily volume calculated using 1 month period (last 22 trading days).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendables\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market value of holdings available to a securities lending program for lending.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell75cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 75 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionAdjustedSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread of a fixed-income security rate and the risk-free rate of return, which is then adjusted to take into account an embedded option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionAdjustedSwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Callability Option Adjusted Spread to yield curve.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bosInTicksLabel\":{\"anyOf\":[{\"type\":\"string\",\"description\":\"Label of the Stock's Bid-Offer Spread in Ticks on the particular date.\",\"enum\":[\"Narrow\",\"Wide\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"positionSourceId\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy1bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 1 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy3point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 3.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsSustainRegion\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Region assigned by GIR ESG SUSTAIN team.\",\"enum\":[\"W. Europe\",\"N. America\",\"CEEMEA\",\"Asia ex Japan\",\"LatAm\",\"Japan\",\"Aust/NZ\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteReturnWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Absolute Return Week to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"deploymentId\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Deployment ID.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersSeniority\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,50}$\",\"type\":\"string\",\"description\":\"The seniority of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread between the yields of a debt security and its benchmark when both are purchased at ask price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"flow\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Flow of the fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthH26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanRebate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rebate paid back to a securities lending borrower.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthH25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"period\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"Period for the relevant metric, such as 1y (1 year).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"indexCreateSource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Source of basket creation\",\"enum\":[\"API\",\"CUBE\",\"Hedger\",\"Pretrade\",\"Marquee UI\",\"Clone\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthH24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthH23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthH22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthH21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"nonUsdOis\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Non USD Currency Ois rate. Feature applied only to Xccy curves.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realTWIContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of real trade weighted exchange rate index component to real FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mktAsset\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"The MDAPI Asset (e.g. USD, USD/EUR).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2IndexLocation\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Location of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twapUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs TWAP In Limit, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lastUpdatedMessage\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\t\\\\w\\\\x{00C0}-\\\\x{017f}&\\\\/()+%\\\\.:;,!<>=~?$#* \\\\[\\\\]`\\\\x{00A7}\\\\u2022\\\\x{00B4}'\\\"\\n\\\\r-]{0,5000}$\",\"type\":\"string\",\"description\":\"Last Updated Message.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The value of the securities or cash delivered by a borrower to a lender to support a loan of securities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionAdjustedOISSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Option adjusted OIS spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalReturnPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total return price of an instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"weightedPercentInModel\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weighted percent of constituent in risk model.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"initLoanSpreadRequired\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The minimum spread requirement for a securities lending loan on the day of loan initiation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"electionPeriod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d_]{0,64}$\",\"type\":\"string\",\"description\":\"Period of election.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fundingAskPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Ask Price (price offering to sell).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"historicalBeta\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Historical beta.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bondRiskPremiumIndex\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Bond risk premium index: difference between growth rate forecast and 10y treasury yield.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hitRateYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Year to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"girGsdeerGsfeer\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"GSDEER and GSFEER quarterly estimates for currency fair values made by Global Investment Research (GIR) macro analysts.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numUnits\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Units.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersReceiverFrequency\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expenseRatioGrossBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Gives basis point measure of management fee.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativePayoffWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ctdPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price of cheapest to deliver bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paceOfRollNow\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The pace of the roll as of the pricing date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"product\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The asset, reference asset, or reference obligation for payments of a party???s obligations under the SB swap transaction reference.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2ReturnType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indication if return of leg 2 is total.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"agentLenderFee\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fee earned by the Agent Lender for facilitating a securities lending agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"disseminationId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"DDR generated unique and random ID for reconciliation purpose.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionStrikePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike price of the option. Also called option level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"precipitationType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of precipitation required: Rain or snow etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lowerBound\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Lower bound value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalMidNormalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Performance against Benchmark in pip.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"underlyingAsset2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Same as Underlying Asset 1 if populated.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"underlyingAsset1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The asset, reference asset, or reference obligation for payments of a party???s obligations under the SB swap transaction reference.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"legalEntity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Entity that has legal rights to the fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"performanceFee\":{\"anyOf\":[{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"lt\":{\"description\":\"search for values less than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gte\":{\"description\":\"search for values greater than or equal.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"lte\":{\"description\":\"search for values less than or equal to.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gt\":{\"description\":\"search for values greater than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]}},\"additionalProperties\":false,\"description\":\"Operations for searches.\"},{\"type\":\"number\",\"description\":\"Fee that funds charge for performance in percent.\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"orderState\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"State of an order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"actualDataQuality\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Actual data quality level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"indexRatio\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Bid Price (price willing to buy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queueInLotsLabel\":{\"anyOf\":[{\"type\":\"string\",\"description\":\"Label of the Stock's Queue size in Lots (if applicable) on the particular date.\",\"enum\":[\"Thin\",\"Very Thick\",\"Average\",\"Thick\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"adv10DayPct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Median number of shares or units of a given asset traded over a 10 day period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longConvictionMedium\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The count of long ideas with medium conviction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeHitRateWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Week to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dailyTrackingError\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Daily tracking error.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell140cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 140 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell10bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 10 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"aggressiveOffsetFromLast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TBD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longitude\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Longitude.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newIcu\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of new patients hospitalized in ICU.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketCap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market capitalization of a given asset in denominated currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"weightedAverageMid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weighted Average Mid.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clusterRegion\":{\"anyOf\":[{\"type\":\"string\",\"description\":\"The cluster region the stock belongs to.\",\"enum\":[\"Americas\",\"Asia Pacific\",\"EMEA\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"valoren\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Valoren or VALOR number, Swiss primary security identifier (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averageExecutionPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The average execution price of the order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedsAssetOISSwapSpread1m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proceeds Asset OIS Swap Spread 1m.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payoffWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"basis\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread to be added to the shorter tenor leg for the swap to be ATM.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"investmentRateTrend\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The day over day trend of the rate of return on an investment.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossInvestmentMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total gross investment Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hedgeId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^MH[A-Z0-9]{14}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier for a hedge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sharpeMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sharpe ratio Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon8Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 8 day time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"residualVariance\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":0,\"type\":\"number\",\"description\":\"Residual variance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"restrictInternalDerivedData\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restricts Ability to Use Internally as Part of Derived Data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adv5DayPct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Median number of shares or units of a given asset traded over a 5 day period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midpointFillsPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of total order filled at midpoint .\"},{\"maxItems\":1,\"type\":\"array\"}]},\"openInterest\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Measure of level of activity in market.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"turnoverCompositeUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Turnover composite not adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fwdPoints\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forward points.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeReturnWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Relative Return Week to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"units\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{1,128}$\",\"type\":\"string\",\"description\":\"Units for series.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payerRateOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.()\\\\/* -]{0,32}\",\"type\":\"string\",\"description\":\"The underlying benchmark for the payer, e.g. USD-LIBOR-BBA, EUR-EURIBOR-TELERATE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsRiskCountryName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Risk country.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"extMktPoint3\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,64}$\",\"type\":\"string\",\"description\":\"Third dimension of external MDAPI Point.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturitySwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread between a Matched Maturity Swap Rate and the yield of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cityName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+#%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Name of city.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hourlyBucket\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-\\\\:]{0,256}$\",\"type\":\"string\",\"description\":\"Bucket denoting hour of the day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averageImpliedVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average volatility of an asset implied by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalHospitalizedWithSymptoms\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of hospitalized cases due to symptoms, but not in ICU.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysOpenRealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Day Open, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedHighPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Adjusted high level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedsAssetOISSwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proceeds asset OIS swap spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"extMktPoint1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,64}$\",\"type\":\"string\",\"description\":\"First dimension of external MDAPI Point.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"direction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Indicates whether exposure of a given position is long or short.\",\"enum\":[\"LONG\",\"SHORT\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"extMktPoint2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,64}$\",\"type\":\"string\",\"description\":\"Second dimension of external MDAPI Point.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"subRegionCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-=]{0,50}$\",\"type\":\"string\",\"description\":\"ISO 3166 Sub Region Code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersFixedRate\":{\"anyOf\":[{\"description\":\"Strike as value, percent or at-the-money e.g. 62.5, 95%, ATM-25, ATMF, 10/vol, 100k/pv, p=10000, p=10000USD, $200K/BP, or multiple strikes 65.4/-45.8 \",\"anyOf\":[{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^((ATM|ATMF|A)|(atm|atmf|a)|[-+]?([0-9]*\\\\.[0-9]+|[0-9]+)\\\\%?)|([0-9]*[kKmMbB]?\\\\/((pv|bp|dv01|vol|ann|spot|fwd)|(PV|BP|DV01|VOL|ANN|SPOT|FWD)))|((p|P)=([0-9]*[kKmMbB]?)([a-zA-Z]{3})?)$\",\"type\":\"string\"},{\"type\":\"number\"},{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^-?\\\\d*(\\\\.\\\\d+)?(\\\\s*\\\\/\\\\s*-?\\\\d*(.\\\\d+)?)*\",\"type\":\"string\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"isEstimatedReturn\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether the return of asset over a given period is an estimate or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valueForecast\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\".*\",\"type\":\"string\",\"description\":\"Average forecast among a representative group of economists.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalIcu\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of cases in intensive care.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"positionSourceType\":{\"anyOf\":[{\"maxLength\":16,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Source object for position data\",\"enum\":[\"Portfolio\",\"Asset\",\"Backtest\",\"RiskRequest\",\"Hedge\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"previousCloseUnrealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealised performance vs Previous Close, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"minimumDenomination\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The lowest denomination of an issue that can be purchased as authorized by the bond documents.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureValueNotional\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The future notional value of the asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"participationRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Executed quantity over market volume (e.g. 5, 10, 20).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"obfr\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The overnight bank funding rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy9point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 9.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionLockPeriod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the first allowable exercise date of the option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esMomentumPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A percentile that captures a company's E&S momentum ranking within its subsector.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"advPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of ADV.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1AveragingMethod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Averaging method of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"turnoverComposite\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Turnover composite.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forecastDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Date of forecasted electricity loads.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"internalIndexCalcRegion\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Classification for the asset based on index calculation region\",\"enum\":[\"HKG\",\"LDN\",\"NYC\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"positionType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Type of positions.\",\"enum\":[\"Eq Swap\",\"Material Order Control\",\"Material Position Control\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"subAssetClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the sub asset class.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortInterest\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Short interest value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"referencePeriod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,128}$\",\"type\":\"string\",\"description\":\"The period for which released data refers to.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Accumulated number of shares, lots or contracts traded according to the market convention adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ctdFwdYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Cheapest to deliver bond fwd yield.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"secDB\":{\"anyOf\":[{\"maxLength\":100,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,100}$\",\"type\":\"string\",\"description\":\"Internal Goldman Sachs security database location for the asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"memoryUsed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total memory used by tickserver.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bpeQualityStars\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Confidence in the BPE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ctd\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{0,64}$\",\"type\":\"string\",\"description\":\"Description of cheapest to deliver bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"intendedParticipationRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Intended participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1PaymentType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Type of payment stream on leg 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradingPnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Trading Profit and Loss (PNL).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"collateralValueRequired\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of collateral required to cover a given position.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy45bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 45 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceToEarningsPositive\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price to earnings positive.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forward FX forecast.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forecastValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Profit and Loss.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volumeInLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated Volume In Limit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isTerritory\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not a territory.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2DeliveryPoint\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Delivery point of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon4Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 4 day time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"styles\":{\"anyOf\":[{\"maxItems\":50,\"type\":\"array\",\"description\":\"Styles or themes associated with the asset (max 50)\",\"items\":{\"maxLength\":128,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\"}},{\"maxItems\":1,\"type\":\"array\"}]},\"shortName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Short name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"resetFrequency1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An integer multiplier of a period describing how often the parties to an SB swap transaction shall evaluate and, when applicable, change the price used for the underlying assets of the swap transaction. Such reset frequency may be described as one letter preceded by an integer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy4bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 4 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"resetFrequency2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Same as Reset Frequency 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"otherPriceTerm\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication that the publicly reportable SB swap transaction has one or more additional term(s) or provision(s), other than those listed in the required real-time data fields, that materially affect(s) the price of the swap transaction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidGspread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Bid G spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"openPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Opening level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"psId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Platts Symbol.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hitRateMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Month to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fairVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike in volatility terms, calculated as square root of fair variance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dollarCross\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"USD cross symbol for a particular currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"portfolioType\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Portfolio type differentiates the portfolio categorization\",\"enum\":[\"Securities Lending\",\"Draft Portfolio\",\"Draft Bond\",\"PCO Portfolio\",\"PCO Share Class\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"currency\":{\"anyOf\":[{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"clusterClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^([1-9]|1[0-3])$\",\"type\":\"string\",\"description\":\"The Cluster the stock belongs to on the particular date. The cluster class will be assigned to a value between 1 and 13 (inclusive).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell50bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 50 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthM21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidSize\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The number of shares, lots, or contracts willing to buy at the Bid price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalMid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Arrival Mid Price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersExchangeCurrency\":{\"anyOf\":[{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"candidateName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w_]{0,128}$\",\"type\":\"string\",\"description\":\"Name of candidate in election.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedLognormalVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market implied volatility measured using a lognormal model in percent/year.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapInLimitUnrealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs VWAP In Limit, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ratingMoodys\":{\"anyOf\":[{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Bond rating from Moody's.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthM26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthM25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthM24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthM23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthM22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"flowPct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of flow of the fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"source\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source of data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsCountryCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,100}$\",\"type\":\"string\",\"description\":\"Country code (ISO 3166).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settleDrop\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Bid Price (price willing to buy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataSetSubCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]\\\\.\\\\?@#:,$ \\\\t'\\\"-=\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Second level grouping of dataset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell9point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 9.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quantityBucket\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Range of pricing hours.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionStyleSDR\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Style of the option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"oeName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Name of user's organization.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"given\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of trades given.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2DayCountConvention\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The determination of how interest accrues over time for the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liquidityScoreSell\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The liquidity score assigned to selling the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"delistingDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Date at which the entity is delisted.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"weight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weight of a given position within a portfolio, by default calcualted as netWeight.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"accruedInterest\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The accrued interest paid on the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"businessScope\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Business/Product Scope.\",\"enum\":[\"All\",\"EQ\",\"FICC\",\"Futures\",\"FX\",\"FX (Derivs)\",\"GSCO\",\"GSET\",\"Research\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"wtdDegreeDays\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The actual/forecast value for weighted degree days.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteWeight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weight in terms of absolute notional.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"measure\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w ]{2,36}$\",\"type\":\"string\",\"description\":\"A calculated metric in the risk scenario.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"temperatureHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for temperature in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"icebergTipRateType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"The unit associated with an Iceberg tip rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sharpeYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sharpe ratio Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windSpeedForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for wind speed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossInvestmentYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total gross investment Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yieldPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Yield price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1TotalNotionalUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of reported notional price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"issuePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The price for which the instrument is issued.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askHigh\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The highest Ask Price (price offering to sell).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expectedDataQuality\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Expected data quality level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"regionName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[a-zA-Z ]*\",\"type\":\"string\",\"description\":\"Name of the region for which FCI is calculated ??? Developed Markets, Emerging Markets, Euro Area, Global.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valueRevised\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\".*\",\"type\":\"string\",\"description\":\"Revised value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"discretionUpperBound\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TThe upper bound of the discretion band as published from the algo.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedTradePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Last trade price or value adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isoSubdivisionCodeAlpha2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"ISO 3166-2 code for identifying the principal subdivisions of all countries coded in ISO 3166-1 (up to three alphanumeric characters).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ctdConversionFactor\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Cheapest to deliver bond's conversion factor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedsAssetSwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proceeds asset swap spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isADR\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Is ADR or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"issueDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"serviceId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,128}$\",\"type\":\"string\",\"description\":\"Service ID.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yes\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price of yes contract.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for G metrics relative to the entire ESG universe.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Marketable value of a given position, generally the market price for a given date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"entityId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Identifies an entity uniquely; in use for Data Quality Checker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalCurrency1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the type of currency of the notional or principal amount.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"netDebtToEbitda\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net Debt to EBITDA.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numUnitsUpper\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Units Upper.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalCurrency2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Same as Notional Currency 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"inLimitParticipationRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average in-limit participation rate of the order, including principal fills.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pressureForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The pressure forecast of a given unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of trades paid.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fixedRate\":{\"anyOf\":[{\"description\":\"Strike as value, percent or at-the-money e.g. 62.5, 95%, ATM-25, ATMF, 10/vol, 100k/pv, p=10000, p=10000USD, $200K/BP, or multiple strikes 65.4/-45.8 \",\"anyOf\":[{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^((ATM|ATMF|A)|(atm|atmf|a)|[-+]?([0-9]*\\\\.[0-9]+|[0-9]+)\\\\%?)|([0-9]*[kKmMbB]?\\\\/((pv|bp|dv01|vol|ann|spot|fwd)|(PV|BP|DV01|VOL|ANN|SPOT|FWD)))|((p|P)=([0-9]*[kKmMbB]?)([a-zA-Z]{3})?)$\",\"type\":\"string\"},{\"type\":\"number\"},{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^-?\\\\d*(\\\\.\\\\d+)?(\\\\s*\\\\/\\\\s*-?\\\\d*(.\\\\d+)?)*\",\"type\":\"string\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"short\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Short exposure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy4point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 4.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell30cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 30 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1PaymentFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An integer multiplier of a time period describing how often the parties to the SB swap transaction exchange payments associated with each party???s obligation. Such payment frequency may be described as one letter preceded by an integer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cmId\":{\"anyOf\":[{\"maxLength\":16,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,16}$\",\"type\":\"string\",\"description\":\"Prime Client Master Party Id.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"taxonomy\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the product taxonomy.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy45cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 45 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"measures\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Fields that are nullable.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"seasonalAdjustment\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,64}$\",\"type\":\"string\",\"description\":\"Indicates if and how the forecast is seasonally adjusted.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rankWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rank of the Contributor Week to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"underlyer\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"The underlyer of the security. The cross for FX forwards, for example.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"identifier\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Filter by any identifier of an asset like ticker, bloomberg id etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of reported price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeReportRefId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"On cancellations and corrections, this ID will hold the Dissemination ID of the originally published real-time message.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"subdivisionId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee subdivision object identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unadjustedLow\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted low level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy160cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 160 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"portfolioId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^MP[A-Z0-9]{14}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier for a portfolio.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"zSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Zero Volatility Spread to yield curve.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"capFloorAtmFwdRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Cap Floor ATM forward rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"An aggregate percentile that captures a company's E&S ranking within its subsector.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tdapi\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"TDAPI Description.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"locationCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-A-Za-z0-9\\\\(\\\\)\\\\/*+!&. :,'_??\\\\x{00C0}-\\\\x{E007A}]{0,255}$\",\"type\":\"string\",\"description\":\"The unique government-designated code for locations (of varying granularity).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rcic\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Reuters composite instrument code (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"nameRaw\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,2048}$\",\"type\":\"string\",\"description\":\"Legal or published name for the asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"simonAssetTags\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,2048}$\",\"type\":\"string\",\"description\":\"SIMON Asset Tags.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hitRateQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Quarter to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryVolumeInLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Primary volume in limit as specified on the order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"precipitationDailyForecastPercent\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for precipitation in Percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"aumEnd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Assets under management at the end of the period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"premium\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"An indication of the market value at the time of execution.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"low\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Low level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"crossGroup\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Economic cross groupings.\",\"enum\":[\"G10\",\"EM\",\"Total\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"fiveDayPriceChangeBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The five day movement in price measured in basis points.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"holdings\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of units of a given asset held within a portfolio.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"precipitationDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for precipitation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceMethod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,64}$\",\"type\":\"string\",\"description\":\"Method used to calculate net price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersFixedRateFrequency\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"oisXccy\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Xccy OIS Spread rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysOpen\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Day Open price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy110cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 110 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averageSpreadBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Time-weighted average bid/ask spread during the life of the order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy55cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 55 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthQ26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"issueSize\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The notional issue size of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthQ25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthQ24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthQ23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthQ22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pendingLoanCount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The number of pending loans that exist on a given date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthQ21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceSpotStopLossUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]{0,20}$\",\"type\":\"string\",\"description\":\"Unit in which the stop loss price is reported.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceRangeInTicksDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,100}$\",\"type\":\"string\",\"description\":\"Description of the Stock's Price Range in Ticks on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market trade volume.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryCountryRic\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Reuters primary country instrument code (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionExpirationFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Option Expiration Frequency provided by Participant (e.g., Daily, Monthly).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isActive\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether this entry is active.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"useMachineLearning\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Use Machine learning.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"growthScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Growth percentile relative to Americas coverage universe (a higher score means faster growth).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bufferThreshold\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The buffer between holdings and on loan quantity for an asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy120cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 120 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturitySwapRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Matched maturity swap rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryVwap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Primary VWAP benchmark price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exchangeTypeId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"PadMaster unique exchange type identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"basisSwapRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fixed spread or basis that equates the two floating legs of a basis swap at the time of the trade.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exchangeCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"EEX Exchange Code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"group\":{\"anyOf\":[{\"maxLength\":80,\"minLength\":0,\"pattern\":\"^\\\\w+\",\"type\":\"string\",\"description\":\"Region or sector following the MSCI Global Industry Classification Standard (GICS).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersTerminationDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Relative termination date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average bid-ask quoted spread of the stock (bps) over the execution horizon (1 day).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yieldChangeOnDay\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Yield change on day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"autoTags\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$*| '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Metadata associated with the object\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCost\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Pretrade computation of trading out cost.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sustainJapan\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"True if the stock is on the SUSTAIN Japan list as of the corresponding date. False if the stock is removed from the SUSTAIN Japan list on the corresponding date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"historyStartDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Asset history start date.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread between the yields of a debt security and its benchmark when both are purchased at bid price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hedgeTrackingError\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\",\"description\":\"Standard deviation of the difference in the portfolio and benchmark returns over time.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windSpeedType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The hourly or average speed of wind.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"strikePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"parAssetSwapSpread12m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Par asset swap spread vs 12m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeReportId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"DDR generated unique and random ID for reconciliation purpose.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedOpenPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Opening level of an asset based on official exchange fixing or calculation agent marked level adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"countryId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee country object identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"point\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"MDAPI point.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pnlMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total PnL Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalReturns\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total returns for backtest.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lender\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Name of the lending entity on a securities lending agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annReturn1Year\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total return representing past performance, used for GS Money Market onshore funds over one year.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ctdFwdDv01\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"DV01 of cheapest to deliver bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"effYield7Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average income return over the previous 7 days reduced by any capital gains that may have been included in rate calculation, assuming the rate stays the same for one year and that dividends are reinvested.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"meetingDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Date the Central bank Meeting took place.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"calendarSpreadMispricing\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The richness/cheapness of the calendar spread relative to Libor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy140cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 140 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceNotation2Type\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Basis points, Price, Yield, Spread, Coupon, etc., depending on the type of SB swap, which is calculated at affirmation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fundFocus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Focus of the fund. For example whether it is ESG or Non-ESG.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeStrike\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike relative to spot or forward level in terms of percent of either spot or forward level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"flagship\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not it is a flagship basket.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"additionalPriceNotation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The additional price notation value includes execution events, the presence of collateral, frontend payments, back-end payments, or other noneconomic characteristics (e.g. counterparty credit risk) not illustrated in the reporting field for pricing characteristic.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"factorCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w -]{0,32}$\",\"type\":\"string\",\"description\":\"The text to display when representing a factor category. Must match the regex ^[\\\\w -]{0,32}$.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"equityDelta\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Delta exposure to equity products.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossWeight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sum of the absolute weight values, which equals the sum of absolute long and short weights. If you have IBM stock with shortWeight 0.2 and also IBM stock with longWeight 0.4, then the grossWeight would be 0.6 (0.2+0.4).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"listed\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether the asset is listed or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell7bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 7 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"earningsRecordType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The violation status for this particular line item.\",\"enum\":[\"Loan Earnings\",\"Adjustment\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"mean\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Calculated average.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The return an investor realizes on a bond sold at the ask price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shockStyle\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Style of shocks to be used.\",\"enum\":[\"implied\",\"specified\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"methodology\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{0,8192}$\",\"type\":\"string\",\"description\":\"Methodology of dataset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy25cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 25 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"amountOutstanding\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The aggregate principal amount of the total number of bonds not redeemed or otherwise discharged.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketPnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market Profit and Loss (PNL).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sustainAsiaExJapan\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"True if the stock is on the SUSTAIN Asia Ex Japan list as of the corresponding date. False if the stock is removed from the SUSTAIN Asia Ex Japan list on the corresponding date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell6point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 6.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"neighbourAssetId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee identifier for the corresponding neighbour.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"countIdeasYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas alive at a time Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"simonIntlAssetTags\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,2048}$\",\"type\":\"string\",\"description\":\"SIMON International Asset Tags.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"path\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Path to value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapUnrealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs VWAP, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payoffMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bosInBpsLabel\":{\"anyOf\":[{\"type\":\"string\",\"description\":\"Label of the Stock's Bid-Offer Spread in Basis points on the particular date.\",\"enum\":[\"Narrow\",\"Wide\",\"Average\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"bosInBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Bid-Offer Spread of the stock in Basis points on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pointClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"MDAPI Class.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fxSpot\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"FX spot rate as determined by fixing source.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"restrictNamedIndividuals\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restricts Visbility to Named Individuals.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hedgeVolatility\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\",\"description\":\"Standard deviation of the annualized returns.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tags\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$*| '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Metadata associated with the object\"},{\"maxItems\":1,\"type\":\"array\"}]},\"population\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total population of a country, state or subdivision.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"underlyingAssetId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee identifier for constituents of an index or portfolio.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realLongRatesContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of long rate component to real FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pctprices_return\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The pctprices(return) function.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"domain\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,1024}$\",\"type\":\"string\",\"description\":\"Domain that request came from.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy80cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 80 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forwardTenor\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"Start of swap after option expiry.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averagePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"targetPriceRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Target Price, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2FixedRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"If fixed rate leg, the fixed rate of leg 2.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shareClassAssets\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total amount of assets under management in this shareclass.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annuity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Local currency annuity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalCount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of entities in the test.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quoteType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Quote Type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"corporateActionStatus\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Different statuses for corporate actions from solactive\",\"enum\":[\"Confirmed\",\"Effective\",\"Executed\",\"Pending\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"peggedTipSize\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Pegged order tip size in terms of the quantity unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"uid\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[A-Z]{2}\",\"type\":\"string\",\"description\":\"Two-digit code for countries and regions for which FCI numbers are represented. For countries it is the ISO 3166 2-digit country code. Regions are denoted as DM (Developed Markets), EM (Emerging Markets), EA (Euro Area) and GL (Global).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esPolicyPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A percentile that captures a company's E&S policy ranking within its subsector.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"usdOis\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"USD Ois rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"term\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Allowed risk model terms\",\"enum\":[\"Short\",\"Medium\",\"Long\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"restrictInternalGsNtk\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restricts Internal Visibility to Data Beyond GS NTK.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostParticipationRate100Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 100 percent participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeUniverse\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,256}$\",\"type\":\"string\",\"description\":\"Universe relative to which the metric is computed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"measureIdx\":{\"anyOf\":[{\"type\":\"integer\",\"description\":\"The index of the corresponding measure in the risk request.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fredId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d\\\\.]{1,256}$\",\"type\":\"string\",\"description\":\"FRED series identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twiContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of trade weighted exchange rate index component to FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cloudCoverType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of cloud cover: Average etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"delisted\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"(yes|no)\",\"type\":\"string\",\"description\":\"Whether the security has been delisted.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"regionalFocus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Section of the world a fund is focused on from an investment perspective. Same view permissions as the asset.\",\"enum\":[\"Global\",\"Asia ex-Japan\",\"China\",\"Emerging Europe\",\"Europe\",\"Global Emerging Markets\",\"Japan\",\"Latin America\",\"Middle East / North Africa\",\"North America\",\"Pan-Asia (inc. Japan)\",\"India\",\"Other\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"volumePrimary\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Accumulated number of shares, lots or contracts traded according to the market convention at the primary exchange.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersPayerDesignatedMaturity\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy30cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 30 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fundingBidPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Bid Price (price willing to buy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"series\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Series.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell3bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 3 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settlementPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Settlement Price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quarter\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Quarter of forecast.\",\"enum\":[\"Q1\",\"Q2\",\"Q3\",\"Q4\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"sell18bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 18 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersFloatingRateOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9-\\\\. ]{0,31}$\",\"type\":\"string\",\"description\":\"The underlying benchmark for the floating rate, e.g. USD-LIBOR-BBA, EUR-EURIBOR-TELERATE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedVwapPerformanceBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated VWAP (in limit) since order inception ??? realized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"voteShare\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Vote share (predicted) in election.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"servicingCostShortPnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Servicing Cost Short Profit and Loss.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalConfirmed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of confirmed cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"economicForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"plotId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Plot Identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clusterDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,100}$\",\"type\":\"string\",\"description\":\"Description of the Cluster characteristics.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"concentrationLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The allowable percentage of the outstanding loan balance of a lenders book that can be lent to/in a particular borrower/market/etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windSpeed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average wind speed in knots.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"observationHour\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[0-9]+(am|pm)$\",\"type\":\"string\",\"description\":\"The observation hour for hourly weather forecast.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"signal\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Signal.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"borrowerId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Id of the borrowing entity on a securities lending agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataProduct\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d]{0,256}$\",\"type\":\"string\",\"description\":\"Product that dataset belongs to.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy7point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 7.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"limitPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Limit price of the order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bmPrimeId\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Benchmark prime ID of the treasury.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of data for Power or Load. Values may include Actual, Forecast.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"count\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of a collection.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"conviction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Confidence level in the trade idea.\",\"enum\":[\"Small\",\"Medium\",\"Large\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"rfqstate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-\\\\\\\\/]{0,256}$\",\"type\":\"string\",\"description\":\"RFQ State.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"benchmarkMaturity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"The benchmark tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossFlowNormalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Gross flow for the asset divided by average in defined history.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy14bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 14 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"factorId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]{0,20}$\",\"type\":\"string\",\"description\":\"Id for Factors.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthV26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsFxCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Currency of underlying FX risk for STS assets.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthV25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidChange\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Change in BID price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"month\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^\\\\d{4}(0[1-9]|1[0-2])$\",\"type\":\"string\",\"description\":\"Month in YYYYMM format.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthV24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"investmentWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total net investment Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthV23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthV22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthV21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expiration\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The expiration date of the associated contract and the last date it trades.\",\"enum\":[\"IMM1\",\"IMM2\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2ResetFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An integer multiplier of a period describing how often the parties to an SB swap transaction shall evaluate and, when applicable, change the price used for the underlying assets of the swap transaction (leg 2). Such reset frequency may be described as one letter preceded by an integer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"controversyScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for controversy metrics.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedAssetSwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread between a Proceed Swap Rate and the yield of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"concentrationLevel\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The percentage of the outstanding loan balance of a lenders book that is being lent to/in a particular borrower/market/etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"importance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Importance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsGicsSector\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"GICS Sector classification (level 1).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsAssetName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Name of risk asset for STS underliers.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"netExposureClassification\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Classification for net exposure of fund.\",\"enum\":[\"Short Only / Short Bias\",\"Market Neutral\",\"Low Net\",\"Variable Net\",\"Long Biased\",\"Long Only\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"settlementMethod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Settlement method of the swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"receiverDesignatedMaturity\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"title\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{1,512}$\",\"type\":\"string\",\"description\":\"Title of series.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"xRefTypeId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"PadMaster unique xref type identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"duration\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Candle duration. Supported values are??1m, 1h, 1d etc.\",\"enum\":[\"1m\",\"2m\",\"3m\",\"4m\",\"5m\",\"10m\",\"15m\",\"30m\",\"1h\",\"2h\",\"3h\",\"4h\",\"12h\",\"1d\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"load\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Quantity of power in the electrical grid.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"alpha\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Alpha.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"company\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Activity user company.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settlementFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Settlement Frequency provided by Participant (e.g., Monthly, Daily).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"distAvg7Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Goldman custom calculated value, only used for GS onshore Money Market Funds, assumes sum of the past 7 days divided by 7 and expressed as a percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"inRiskModel\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not the asset is in the risk model universe.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dailyNetShareholderFlowsPercent\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of assets paid daily.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"filledNotionalLocal\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Executed notional in Local.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"everHospitalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total number of patients ever hospitalized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"meetingNumber\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Central bank meeting number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midGspread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Mid G spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysOpenUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealised performance vs Day Open, in BPS.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longLevel\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Level of the 5-day normalized flow for long selling/buying.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Description of data that client is requesting.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"temperatureType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of temperature required: max or min or average etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsideid\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Goldman Sachs internal composite equity and exchange identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"repoRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Repurchase Rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"division\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Division that owns the data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cloudCoverDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for cloud cover in percentage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windSpeedDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for wind speed in Miles per hour.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersFloatingRateDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeAction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication that a publicly reportable securitybased (SB) swap transaction has been incorrectly or erroneously publicly disseminated and is canceled or corrected or a new transaction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"action\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The activity action. For example: Viewed\",\"enum\":[\"Approved\",\"Calculated\",\"Canceled\",\"Closed\",\"Created\",\"Deleted\",\"Evaluated\",\"Failed\",\"Filled\",\"Followed\",\"Forwarded\",\"Priced\",\"Published\",\"Onboarded\",\"Rebalanced\",\"Resumed\",\"Run\",\"Saved\",\"Scheduled\",\"Sent\",\"Shared\",\"Suspended\",\"Succeeded\",\"Updated\",\"Uploaded\",\"Viewed\",\"Consumed\",\"Requested\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"ctdYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Yield of cheapest to deliver bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalHaircutVwapNormalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Performance against Benchmark in pip.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceComponent\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Component of total price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queueClockTimeDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,100}$\",\"type\":\"string\",\"description\":\"Description of the Stock's Queue Clock Time on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersReceiverDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"percentMidExecutionQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of order filled at mid-touch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"deltaStrike\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Option strike price expressed in terms of delta * 100.\",\"enum\":[\"5DC\",\"10DC\",\"15DC\",\"20DC\",\"25DC\",\"30DC\",\"35DC\",\"40DC\",\"DN\",\"5DP\",\"10DP\",\"15DP\",\"20DP\",\"25DP\",\"30DP\",\"35DP\",\"40DP\",\"ATMS\",\"ATMF\",\"ATM\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"cloudCover\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of the cloud cover for a given unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersNotionalCurrency\":{\"anyOf\":[{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"buy18bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 18 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valueActual\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\".*\",\"type\":\"string\",\"description\":\"Latest released value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"upi\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unique product identifier for product.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"collateralCurrency\":{\"anyOf\":[{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"originalCountry\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,128}$\",\"type\":\"string\",\"description\":\"Country in source dataset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"field\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,32}$\",\"type\":\"string\",\"description\":\"The market data field (e.g. rate, price). This can be resolved into a dataset when combined with vendor and intraday=true/false.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"geographicFocus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Geographic region of focus.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysOpenRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Day Open, in BPS.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fxRiskPremiumIndex\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"FX risk premium index: percentage difference between average spot rate and OECD PPP exchange rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"skew\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Volatility skew.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"status\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Status of report run\",\"enum\":[\"new\",\"ready\",\"executing\",\"calculating\",\"done\",\"error\",\"cancelled\",\"waiting\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the type of currency of the notional or principal amount.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sustainEmergingMarkets\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"True if the stock is on the SUSTAIN Emerging Markets list as of the corresponding date. False if the stock is removed from the SUSTAIN Emerging Markets list on the corresponding date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1DesignatedMaturity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Floating rate index designated maturity period of leg 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net price of the asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"onBehalfOf\":{\"anyOf\":[{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^[0-9a-fA-F]{0,32}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"testType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Semantic category that test belongs to e.g. Timeliness, Completeness etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"accruedInterestStandard\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The accrued interest paid on the bond if it is settled two business days after the trade date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthZ26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthZ25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ccgCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Derived CCG code based on location of the patient at the time of triage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortExposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Exposure of a given portfolio to securities which are short in direction. If you are $60 short and $40 long, shortExposure would be $60.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1FixedPaymentCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If fixed payment leg, the unit of fixed payment.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalHaircutVwap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Arrival Haircut VWAP.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"executionDays\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of days to used to execute.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"recallDueDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Date in which the recall of securities in a stock loan recall activity must be complete.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forward\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forward value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"strike\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike level relative to at the money in basis points.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"spreadLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread limit (in pips) for which order slices will be skipped.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"productScope\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Asset Class(es) for which the restriction is required.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersIssuerType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The type of the bond issuer.\",\"enum\":[\"Government\",\"Supranational\",\"Corporate\",\"Municipal\",\"Agency\",\"TRUST\",\"SUPR\",\"UNKN\",\"REIT\",\"PART\",\"AGCY\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"currency1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the type of currency of the notional or principal amount.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"currency2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Same as currency 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"previousCloseRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Previous Close, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysSinceReported\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Days since last reported.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Included if there is additional information about an event, such as the event being cancelled.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapInLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"VWAP In Limit benchmark price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fwdDuration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Duration of the forward.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"return\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Return of asset over a given period (e.g. close-to-close).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isPairBasket\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not it is a pair basket.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalAmount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Only applicable on Commodity Index products.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payOrReceive\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Pay or receive fixed\",\"enum\":[\"Pay\",\"Receive\",\"Straddle\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"totalSevere\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of active cases with severe symptoms.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unexecutedNotionalUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Notional that has not bee executed, in dollars.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expectedResidualPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of order that will be left as residual.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"maturityDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The maturity, termination, or end date of the reportable SB swap transaction.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"traceAdvSell\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TRACE ADV for the sell side.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventName\":{\"anyOf\":[{\"maxLength\":512,\"minLength\":0,\"pattern\":\".*\",\"type\":\"string\",\"description\":\"Event name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"addressLine2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+#%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Second line of the street address, typically reserved for apartment, suite or space number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"indicationOfOtherPriceAffectingTerm\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication that the publicly reportable SB swap transaction has one or more additional term(s) or provision(s), other than those listed in the required real-time data fields, that materially affect(s) the price of the swap transaction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unadjustedBid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted bid level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"backtestType\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Backtest type differentiates the backtest type.\",\"enum\":[\"Basket\",\"Volatility\",\"Volatility Flow\",\"Enhanced Beta\",\"ISelect\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"gsdeer\":{\"anyOf\":[{\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,64}$\",\"type\":\"number\",\"description\":\"Goldman Sachs Dynamic Equilibrium Exchange Rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersIssuer\":{\"anyOf\":[{\"maxLength\":100,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,100}$\",\"type\":\"string\",\"description\":\"The issuer of this bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gRegionalPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A percentile that captures a company's G ranking relative to its region.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"coverageChecked\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of entities checked in a run of the checker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"oisXccyExSpike\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Xccy OIS Spread rate excluding Xccy Spikes impacts.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalRisk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total risk.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mnav\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net asset value, assets of a fund (ex dividend) divided by total number of shares.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Volume of the stock throughout the whole day across primaries and MTFs.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swapAnnuity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Annuity for a benchmark tenor on a currency's fixed-floating swap curve.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"parAssetSwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread between a Par Swap Rate and the yield of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"currYield7Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average income return over previous 7 days reduced by any capital gains that may have been included in rate calculation, according to the current amount.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pressure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average barometric pressure on a given day in inches of mercury.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{0,1024}$\",\"type\":\"string\",\"description\":\"Short description of dataset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthZ24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"feed\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indicates the source feed of the data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthZ23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mktPoint1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"The first dimension of Mkt Point, e.g. 3M, 11Y, DEC19.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthZ22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthZ21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthZ20\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersCommoditySector\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The sector of the commodity\",\"enum\":[\"Base metals\",\"Precious metals\",\"Energy\",\"Agriculturals\",\"Power\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"priceNotation2\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Basis points, Price, Yield, Spread, Coupon, etc., value depending on the type of SB swap, which is calculated at affirmation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketBufferThreshold\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The required buffer between holdings and on loan quantity for a market.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceNotation3\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Basis points, Price, Yield, Spread, Coupon, etc., value depending on the type of SB swap, which is calculated at affirmation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mktPoint3\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"The third dimension of Mkt Point, e.g. 3M, 11Y, DEC19.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mktPoint2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"The second dimension of Mkt Point, e.g. 3M, 11Y, DEC19.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2Type\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indication if leg 2 is fixed or floating or Physical.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mktPoint4\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"The fourth dimension of Mkt Point, e.g. 3M, 11Y, DEC19.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"degreeDaysType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of degree days required: Heating or Cooling etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"investmentIncome\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The income earned by the reinvested collateral.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"groupType\":{\"anyOf\":[{\"maxLength\":80,\"minLength\":0,\"pattern\":\"^\\\\w+\",\"type\":\"string\",\"description\":\"Type of the group.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forwardPointImm\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Immutable outright forward minus spot.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Time weighted average price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clientShortName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\-\\\\s\\\\w:,\\\\\\\\/'&.)(*+!]{0,256}$\",\"type\":\"string\",\"description\":\"The short name of a client.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"groupCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The type of group: region or sector.\",\"enum\":[\"region\",\"sector\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"bidPlusAsk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sum of bid & ask.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"foreignCcyRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The interest rate of the foreign currency of the associated FX contract.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"electionOdds\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Odds of winning election.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windDirectionForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast wind direction of given units.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"requireAnonClientName\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Requires Anonymization of Client Name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pricingLocation\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Quill pricing location.\",\"enum\":[\"NYC\",\"LDN\",\"TKO\",\"HKG\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"beta\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Beta.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lastReturnsEndDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Hedge fund last returns before end date.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"upfrontPaymentDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Date of upront payment.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell1point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 1.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longExposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Exposure of a given portfolio to securities which are long in direction. If you are $60 short and $40 long, longExposure would be $40.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell4point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 4.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostParticipationRate20Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 20 percent participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"venueType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"The type of venue (Exchange, BD, Dark Pool).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"multiAssetClassSwap\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indicates if the swap falls under multiple asset classes.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"deltaChangeId\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"PadMaster unique delta change identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"implementationId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique Implementation identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1FixedPayment\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"If fixed payment leg, the fixed payment amount, which is price*number of contracts bought*contract unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esNumericScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for E&S subsector-relative numeric metrics.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"inBenchmark\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not the asset is in the benchmark.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"actionSDR\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication that a publicly reportable securitybased (SB) swap transaction has been incorrectly or erroneously publicly disseminated and is canceled or corrected or a new transaction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"countIdeasQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas alive at a time Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"knockOutPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market level required for an order to knock out.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ctdAssetId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Id of cheapest to deliver bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy10bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 10 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"precipitation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Amount of rainfall in inches.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valueType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,64}$\",\"type\":\"string\",\"description\":\"Value type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"betaAdjustedNetExposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Beta adjusted net exposure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedRodVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated rest of day volume in continuous trading and closing auction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell14bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 14 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"excessReturnPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The excess return price of an instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fxPnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Foreign Exchange Profit and Loss (PNL).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsGicsIndustryGroup\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"GICS Industry Group classification (level 2).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendingSecId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Securities lending identifiter for the security on loan.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dollarDuration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Measure of a bond's price sensitivity to changes in interest rates.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"equityTheta\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Theta exposure to equity products.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dv01\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Measure of the dollar change in a bond's value to a change in the market interest rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"startDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Start date specific to an asset. For example start date for the swap.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mixedSwap\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indicates if the swap falls under both the CFTC and SEC jurisdictions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swaptionPremium\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Swaption premium.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"snowfall\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Amount of snowfall in inches.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liquidityBucketBuy\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Liquidity bucket for buying the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mic\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,32}$\",\"type\":\"string\",\"description\":\"Market identifier code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"latitude\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latitude.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Mid.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedRepo\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rate of return that can be earned by simultaneously selling a bond futures and then buying that actual bond of equal amount in the cash market using borrowed money.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"long\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Long exposure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"coveredBond\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether the debt security is collateralized against a pool of assets that, in case of failure of the issuer, can cover claims at any point of time.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"regionCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-=]{0,50}$\",\"type\":\"string\",\"description\":\"ISO 3166 Region Code. Generally a three digit code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy20cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 20 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longWeight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Long weight of a position in a given portfolio. Equivalent to position long exposure / total long exposure. If you have a position with a longExposure of $20, and your portfolio longExposure is $100, longWeight would be 0.2 (20/100).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"calculationTime\":{\"anyOf\":[{\"type\":\"integer\",\"description\":\"Time taken to calculate risk metric (ms).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liquidityBucketSell\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Liquidity bucket for selling the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysOpenUnrealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealised performance vs Day Open, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"temperature\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of the temperature of given type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averageRealizedVariance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average variance of an asset realized by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ratingFitch\":{\"anyOf\":[{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Bond rating from Fitch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"financialReturnsScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Financial Returns percentile relative to Americas coverage universe (a higher score means stronger financial returns).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yearOrQuarter\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[0-9]{4}(Q[1-4]){0,1}$\",\"type\":\"string\",\"description\":\"Year or quarter for which the forecast holds.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"nonSymbolDimensions\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Fields that are not nullable.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"commoditiesForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"covid19ByState\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of people affected by covid19 for a given state country.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentageExpectedResidual\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage expected residual quantity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hospitalName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+#%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Name of hospital, where hospital is defined as a healthcare institution providing at inpatient, therapeutic, and/or rehabilitation services under the supervision of physicians.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy90cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 90 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"periodType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,64}$\",\"type\":\"string\",\"description\":\"Period type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsCountryName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Country name of asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalHospitalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of hospitalized cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"peggedRefillInterval\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Time interval (in seconds) between a tranche getting filled and the next tranche being shown on the order book.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fatalitiesProbable\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of probable fatalities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"administrativeRegion\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Name of the county or province.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"open\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Opening level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cusip\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Z0-9]{8}[0-9]$\",\"type\":\"string\",\"description\":\"CUSIP - Committee on Uniform Securities Identification Procedures number (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalConfirmedByState\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of confirmed cases by state.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windAttribute\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The attribute of wind required: direction or speed etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"spreadOptionAtmFwdRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread Option ATM forward rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"netExposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The difference between long and short exposure in the portfolio. If you are $60 short and $40 long, then the netExposure would be -$20 (-60+40).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isLegacyPairBasket\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not it is a legacy pair baskets which are not tradabale as don't have secdb asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"issuerType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The type of the bond issuer.\",\"enum\":[\"Government\",\"Supranational\",\"Corporate\",\"Municipal\",\"Agency\",\"TRUST\",\"SUPR\",\"UNKN\",\"REIT\",\"PART\",\"AGCY\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"buy70cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 70 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"strikeReference\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Reference for strike level (enum: spot, forward).\",\"enum\":[\"spot\",\"forward\",\"normalized\",\"delta\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"assetCount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of assets in a portfolio or index.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isOrderInLimit\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Is the order???s passive leg in limit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fundamentalMetric\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fundamental metric value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quoteStatusId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"PadMaster unique quote status identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The notional value of the asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"closingReport\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,2048}$\",\"type\":\"string\",\"description\":\"Report that was published when the trade idea was closed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"previousTotalConfirmed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The previous day's number of total confirmed cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longTenor\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"Tenor of instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"multiplier\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Underlying unit per asset multiplier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy40cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 40 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetCountPriced\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of assets in a portfolio which could be priced.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"voteDirection\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Direction of vote.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedRepoRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rate of return that can be earned by simultaneously selling a bond futures or forward contract, and then buying that actual bond of equal amount in the cash market using borrowed money.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settlementCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The settlement currency type for SB swap transactions in the FX asset class.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wtdDegreeDaysForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast for weighted degree days of diff types of given units.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"indicationOfCollateralization\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If an SB swap is not cleared, an indication of whether a swap is Uncollateralized (UC), Partially Collateralized (PC), One-Way Collateralized (OC), or Fully Collateralized (FC).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthN26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendingPartnerFee\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fee earned by the Lending Partner in a securities lending agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthN25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthN24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryVwapRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Primary VWAP, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthN23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthN22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthN21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"breakEvenInflation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Break even inflation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pnlYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total PnL Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1ReturnType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indication if return of leg 1 is total.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tenor2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"Tenor of instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"resetFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An integer multiplier of a period describing how often the parties to an SB swap transaction shall evaluate and, when applicable, change the price used for the underlying assets of the swap transaction. Such reset frequency may be described as one letter preceded by an integer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersPayerFrequency\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"degreeDaysForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for degree days.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isManuallySilenced\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Indicates if an entity was silenced at time of check.\",\"enum\":[\"true\",\"false\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"buy3bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 3 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lastUpdatedById\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Unique identifier of user who last updated the object.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"legalEntityAcct\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Account assoicated with the entity that has legal rights to the fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"targetShareholderMeetingDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Target acquisition entity shareholder meeting date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paceOfRollp0\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The 0th-percentile of the pace of roll compared to previous quarters (over the last 5 years) at the same point in time, i.e. cross-sectional.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"controversyPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A percentile that captures a company's controversy ranking.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1NotionalCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the type of currency of the notional or principal amount.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expirationDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The expiration date of the associated contract and the last date it trades.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"floatingRateDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"callLastDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"factorReturn\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Factor return.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"passiveFlowRatio\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ratio of signed passive dollar flow to market cap of global stocks, i.e. proportion of company's market cap that is part of passive fund flows observed weekly. Normalised between -3 (low level) and +3 (high level).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"composite5DayAdv\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Composite 5 day ADV.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marginalContributionToRisk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Marginal contribution of a given asset to portfolio variance, is dependent on covariance matrix.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"closeDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Date the trade idea was closed.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"temperatureHourForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hour forecast of max/min temperature of a given day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newIdeasWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas received by clients Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassSDR\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of one of the broad categories. For our use case will typically be CO.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yieldToWorst\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The return an investor realizes on a bond sold at the mid price at the worst call date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"closingPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Closing price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"turnoverCompositeAdjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Turnover composite adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"comment\":{\"anyOf\":[{\"maxLength\":2048,\"minLength\":0,\"pattern\":\"^[\\\\w.,:;$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source symbol.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted ask level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"restrictExternalDerivedData\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restricts Ability to Use Externally as Part of Derived Data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askChange\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Change in the ask price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"countIdeasMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas alive at a time Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"endDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The maturity, termination, or end date of the reportable SB swap transaction.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sunshine\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The percent of sunshine for a given location.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"contractType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Contract type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"momentumType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"The mode/gear of an algo order running in range bound or directional mode.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"specificRisk\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\",\"description\":\"Specific Risk.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mdapi\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"MDAPI Asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payoffQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loss\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Loss price component.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midcurveVol\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Historical implied normal volatility for a liquid point on midcurve vol surface.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell6bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 6 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradingCostPnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Trading cost profit and loss (PNL).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceNotationType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Basis points, Price, Yield, Spread, Coupon, etc., depending on the type of SB swap, which is calculated at affirmation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"price\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price of instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paymentQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Quantity in the payment currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"redemptionDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2NotionalCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the type of currency of the notional or principal amount.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"subRegion\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"ISO 3166 Country Sub Region.\",\"type\":\"string\",\"description\":\"Filter by any identifier of an asset like ticker, bloomberg id etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"benchmark\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\-\\\\s\\\\w:,\\\\\\\\/'&.)(*+!]{0,256}$\",\"type\":\"string\",\"description\":\"Benchmark index of a contributor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostParticipationRate15Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 15 percent participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fiscalYear\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^\\\\d{4}$\",\"type\":\"string\",\"description\":\"The Calendar Year.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"recallDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The date at which the securities on loan were recalled.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esgMetricValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of the metric corresponding to the parameter metricName.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"internal\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether request came from internal or external.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gender\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Gender of the patient.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsGicsIndustry\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"GICS Industry classification (level 3).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedBidPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Bid Price (price willing to buy) adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lowUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted low level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"MACSSecondaryAssetClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indicates the secondary asset class the multi asset class swap falls under.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"confirmedPerMillion\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of confirmed cases per population of one million.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataSourceId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"PadMaster unique data source identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"integratedScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average of the Growth, Financial Returns and (1-Multiple) percentile (a higher score means more attractive).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy7bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 7 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalMidUnrealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs Arrival Mid, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"knockInPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market level required for an order to knock in.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"event\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether an SB swap transaction is a post-execution event that affects the price of the swap transaction, e.g. terminations, assignments, novations, exchanges, transfers, amendments, conveyances or extinguishing of rights that change the price of the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isIntradayAuction\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Flag indicating whether the bucket is within the intraday auction or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"locationName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-A-Za-z0-9\\\\(\\\\)\\\\/*+!&. :,'_??\\\\x{00C0}-\\\\x{E007A}]{0,255}$\",\"type\":\"string\",\"description\":\"Name of geographical region (of varying granularity).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"coupon\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The fixed coupon for this bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentageAuctionExecutedQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of total order filled at auction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"avgYield7Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Only used for GS Money Market funds, assumes sum of the past 7 days, divided by 7, and expressed as a percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"originalDisseminationId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"On cancellations and corrections, this ID will hold the Dissemination ID of the originally published real-time message.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalOnVent\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of patients on ventilator for COVID.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twapUnrealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs TWAP In Limit, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsCreditMarket\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Credit risk market.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"onsCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Z0-9]{0,255}$\",\"type\":\"string\",\"description\":\"ONS Region code in the UK.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"passiveTouchFillsPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of total order filled at passive touch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"seniority\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,50}$\",\"type\":\"string\",\"description\":\"The seniority of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1Index\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If floating index leg, the index.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"highUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted high level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"submissionEvent\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Describes the status of the submission.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"TVProductMnemonic\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unique by Trade Vault Product based on Product Taxonomy.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"avgTradeRateLabel\":{\"anyOf\":[{\"type\":\"string\",\"description\":\"Label of the Stock's Average Trading Rate on the particular date.\",\"enum\":[\"Small\",\"Medium\",\"Large\",\"Very Large\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"lastActivityDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Last date an idea was entered in Alpha Capture.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceToCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price to cash.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy10cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 10 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"navSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net asset value spread. Quoted (running) spread (mid) of the underlying basket of single name CDS. (Theoretical Index value). In basis points.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"venueMIC\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"MIC code of the venue.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dollarTotalReturn\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The dollar total return of an instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"blockUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of measure used for Block trades.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread between the yields of a debt security and its benchmark when both are purchased at mid price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"istatProvinceCode\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Italian province code (ISTAT 2019).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalRecoveredByState\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of recovered cases by state.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"repurchaseRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Repurchase Rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataSource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The source of data from the feeds.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalBeingTested\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of cases waiting for test results.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clearedOrBilateral\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether or not an SB swap transaction is going to be cleared by a derivatives clearing organization.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"metricName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,256}$\",\"type\":\"string\",\"description\":\"Name of the metric.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askGspread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ask G spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forecastHour\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hour forecast for maximum or minumum temperature of given location.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2PaymentType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Type of payment stream on leg 2.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"calSpreadMisPricing\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Futures implied funding rate relative to interest rate benchmark (usually Libor-based). Represents dividend-adjusted rate at which investor is borrowing (lending) when long (short) future.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalTestedNegative\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of test cases that are negative.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rate366\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rate with interest calculated according to the number of days in a leap year, 366.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"platform\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-\\\\\\\\/]{0,256}$\",\"type\":\"string\",\"description\":\"Originating platform.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rate365\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rate with interest calculated according to a normal number of days in the total year, 365.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fixedRateFrequency\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rate360\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rate with interest calculated according to the discount method, using the number of days used by banks, 360.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isContinuous\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Flag indicating whether the bucket is within continuous or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"value\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The given value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payerDesignatedMaturity\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"productType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Product type of basket\",\"enum\":[\"Flow\",\"MPS\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"mdv22Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Twenty two day median of shares traded per day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twapRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs TWAP In Limit, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"testMeasureLabel\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Semantic label for the test measure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of units of a given asset held within a portfolio.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"reportId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,32}$\",\"type\":\"string\",\"description\":\"Report Identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"indexWeight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weight of MSCI World positions in the region or sector (%).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"MACSPrimaryAssetClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indicates the primary asset class the multi asset class swap falls under.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"trader\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w-]*$\",\"type\":\"string\",\"description\":\"Trader name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2PriceType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Price denomination and unit of leg 2.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalActive\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of confirmed cases minus recovered and fatalities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsid2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\d]{1,20}\",\"type\":\"string\",\"description\":\"GSID identifier for datasets which have two gsids in the measures.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturityOISSwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread between a Matched Maturity OIS Swap Rate and the yield of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valuationDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Specific to rates, the date a valuation is recorded.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"restrictGsFederation\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Restricts GS Federation Visibility.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"positionSource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Specifies position data location for the portfolio\",\"enum\":[\"Marquee\",\"Prime\",\"SecDB\",\"GRDB\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon6Hour\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 6 hour time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy200cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 200 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs VWAP, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceToBook\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price to book.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isin\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Z]{2}[A-Z0-9]{9}\\\\d$\",\"type\":\"string\",\"description\":\"ISIN - International securities identifier number (subect to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"plId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Platts Symbol Name or description.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lastReturnsStartDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Hedge fund last returns after start date.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"collateralValueVariance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Diffrence between actual and required collateral levels.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"year\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,2048}$\",\"type\":\"string\",\"description\":\"Year of forecast.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forecastPeriod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^(([0-9]{4}(Q[1-4]){0,1})|((3|6|12)m))$\",\"type\":\"string\",\"description\":\"Year, quarter or horizon for which the forecast holds.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"callFirstDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataSetIds\":{\"anyOf\":[{\"maxItems\":1,\"type\":\"array\",\"description\":\"The dataset Ids.\",\"items\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Z\\\\d_]{0,64}$\",\"type\":\"string\",\"description\":\"Unique id of dataset.\"}},{\"maxItems\":1,\"type\":\"array\"}]},\"economicTermsHash\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Hash code for an asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numBeds\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Maximum number of beds for which a hospital holds a license to operate; however, many hospitals do not operate all the beds for which they are licensed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell20bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 20 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clientType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Client type, ex: Hedge Fund, Real Money, Corporate, etc.\",\"enum\":[\"Hedge Fund\",\"Real Money\",\"Model\",\"Corporate\",\"Other\",\"All\",\"Banks\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"percentageCloseExecutedQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of order filled in closing Auction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"macaulayDuration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Measure of a bond's price sensitivity to changes in interest rates.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"availableInventory\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"An estimated indication of the share quantity potentially available to borrow in the relevant asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"est1DayCompletePct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Estimated 1 day completion percentage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeHitRateYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Year to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"createdById\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Unique identifier of user who created the object.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketDataType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,64}$\",\"type\":\"string\",\"description\":\"The market data type (e.g. IR BASIS, FX Vol). This can be resolved into a dataset when combined with vendor and intraday=true/false.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realShortRatesContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of short rate component to real FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"metricCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,256}$\",\"type\":\"string\",\"description\":\"Category of the metric.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annualizedCarry\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Annualized carry of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valuePrevious\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\".*\",\"type\":\"string\",\"description\":\"Value for the previous period after the revision (if revision is applicable).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"transmissionClassification\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Classification of the transmission.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"avgTradeRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Average Trading Rate of the stock on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortLevel\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Level of the 5-day normalized flow for short selling/covering.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"version\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Version number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"categoryType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Type of the category.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"policyRateExpectation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Returns the historical policy rate expectations for a given meeting date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"uploadDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Index Position Upload Date.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"blockOffFacility\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether this is a block trade or off-facility swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unrealizedVwapPerformanceUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated VWAP (in limit) since order inception ??? unrealized in $.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paceOfRollp75\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The 75th-percentile of the pace of roll compared to previous quarters (over the last 5 years) at the same point in time, i.e. cross-sectional..\"},{\"maxItems\":1,\"type\":\"array\"}]},\"earningsPerSharePositive\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Earnings per share positive.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numIcuBeds\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"All qualified Intensive Care Unit beds, including psychiatric ICU beds and Detox ICU beds.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bucketVolumeInPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast of the percentage volume of shares in the bucket relative to the total volume forecasted for the day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedTradingCost\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The estimated transaction cost of the order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eid\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\d]{1,20}\",\"type\":\"string\",\"description\":\"Goldman Sachs internal exchange identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeReturnQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Relative Return Quarter to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assessedTestMeasure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Generic field to hold aggregated assessed numeric measure for test result. This field stores the aggregated test measures of entities which are not temporarily ignored by silencing (like max delay across unsilenced assets). This is further used to perform any follow up actions like alerting.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mktQuotingStyle\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"Quoting style.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expirationTenor\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Limit price of the order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketModelId\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique market model identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"receiverFrequency\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedCorrelation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Correlation of an asset realized by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"issueStatus\":{\"anyOf\":[{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:;,!$* '\\\"\\\\n\\\\r-]{0,50}$\",\"type\":\"string\",\"description\":\"Status of the issue.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"collateralValueActual\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of collateral covering the given position.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"atmFwdRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"ATM forward rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostParticipationRate75Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 75 percent participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"close\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Closing level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esProductImpactScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for E&S subsector-relative product impact metrics.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"equityVega\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Vega exposure to equity products.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"executedFillQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Executed quantity - on executions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lenderPayment\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Payment made to lender's bank in support of the income accrued from securities lending.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fiveDayMove\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Five day move in the price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valueFormat\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[a-zA-Z ]*\",\"type\":\"string\",\"description\":\"Value format.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windChillForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast wind chill of given units.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"targetNotional\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\",\"description\":\"Notional value of the hedge target.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fillLegId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w-]*$\",\"type\":\"string\",\"description\":\"Unique identifier for the leg on which the fill executed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rationale\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Reason for changing the status of a trade idea.\",\"enum\":[\"Upcoming Catalyst\",\"Research Recommendation\",\"Valuation/Technical\",\"Management Meeting\",\"Aged\",\"Cutting Losses\",\"News Flow Driven\",\"Thesis Not Working\",\"Thesis Realised\",\"Other\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedTwapPerformanceBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated TWAP since order inception ??? realized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lastUpdatedSince\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted timestamp\",\"format\":\"date-time\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalTests\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of tests administered.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"equitiesContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of equity component to FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"simonId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"SIMON application asset identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"congestion\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Congestion price component.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notes\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{1,2048}$\",\"type\":\"string\",\"description\":\"Notes for series.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalProbableSeniorHome\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Probable cases in Senior homes and care facilities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Category.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averageFillRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent complete.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unadjustedOpen\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted open level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"criticality\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The upgrade criticality of a deployment.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidAskSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Bid ask spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalMidUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs Arrival Mid, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"One of two option types.\",\"enum\":[\"payer\",\"receiver\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"terminationDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The date at which the measure becomes terminated.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queriesPerSecond\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Queries per second.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liquidityType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Taking/Providing.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"creditLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The allowed credit limit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rankQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rank of the Contributor Quarter to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"combinedKey\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Key for epidemic data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"girFxForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"FX forecast value for the relative period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"effectiveTenor\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"girCommoditiesForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeHumidityDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for humidity in Percentage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"std30DaysSubsidizedYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average annual total returns as of most recent calendar quarter-end, does not account for any fee waivers or expense reimbursements.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annualizedTrackingError\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Annualized tracking error.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthF26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthF25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volSwap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The strike in volatility terms, calculated as square root of fair variance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthF24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"heatIndexDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The daily forecast value for heat index in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthF23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realFCI\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Real FCI value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"blockTradesAndLargeNotionalOffFacilitySwaps\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether this is a block trade or off-facility swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthF22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy1point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 1.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthF21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expirationSettlementDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Date on which cash/stocks are settled after expiration date.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteReturnQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Absolute Return Quarter to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossExposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sum of absolute long and short exposures in the portfolio. If you are $60 short and $40 long, then the grossExposure would be $100 (60+40).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Accumulated number of shares, lots or contracts traded according to the market convention.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adv\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average number of shares or units of a given asset traded over a defined period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortConvictionMedium\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The count of short ideas with medium conviction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"completeTestMeasure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Generic field to hold aggregated complete numeric measure for test result. This field stores the aggregated test measures of all the entities (like max delay across all the assets, either silenced or unsilenced).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exchange\":{\"anyOf\":[{\"maxLength\":100,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\\\\\/ ]{0,100}$\",\"type\":\"string\",\"description\":\"Name of marketplace where security, derivative or other instrument is traded\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esPolicyScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for E&S subsector-relative policy metrics.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rollVolumeStd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Cross-sectional roll volume in standard deviations.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"temperatureDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast temperature of diff types in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativePayoffQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"onLoanPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"On loan ratio as compared net asset value of lending fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twapRemainingSlices\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The number of slices remaining on a TWAP.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fairVariance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike such that the price of an uncapped variance swap on the underlying index is zero at inception.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hitRateWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Week to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"previousCloseRealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Previous Close, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Volatility of an asset realized by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unexecutedQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Un-executed quantity, in shares.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedsAssetSwapSpread1m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proceeds asset swap spread vs 1m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cloneParentId\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windSpeedHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for wind speed in Mph.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"etfFlowRatio\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ratio of signed ETF dollar flow to market cap of global stocks, i.e. proportion of company's market cap that is part of ETF flows observed weekly. Normalised between -3 (low level) and +3 (high level).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersReceiverRateOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.()\\\\/* -]{0,32}\",\"type\":\"string\",\"description\":\"The underlying benchmark for the payer, e.g. USD-LIBOR-BBA, EUR-EURIBOR-TELERATE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy60cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 60 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"securitySubTypeId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"PadMaster unique security sub type identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"message\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]+$\",\"type\":\"string\",\"description\":\"Miscellaneous remarks.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsRatesCountry\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Country of interest rate risk for STS assets.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell65cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 65 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"horizon\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[0-9][dwmy]$\",\"type\":\"string\",\"description\":\"Time period indicating the validity of the idea. Eg. 2d (2 days), 1w (1 week), 3m (3 months), 1y (1 year).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wouldIfGoodLevel\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"If specified, allows the remaining quantity of an algo to be filled like a limit order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bufferThresholdRequired\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The required buffer between holdings and on loan quantity for an asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"faceValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The face value of the instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rollVolumeHist\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Five Year historical roll volume.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"counterPartyStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The lending status of a counterparty for a particular portfolio.\",\"enum\":[\"Approved\",\"Not-Approved\",\"Restricted\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"composite22DayAdv\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Composite 22 day ADV.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentageFarExecutedQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of total order filled at aggressive touch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanSpreadRequired\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The minimum spread requirement for a securities lending loan.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClass\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Asset classification of security. Assets are classified into broad groups which exhibit similar characteristics and behave in a consistent way under different market conditions\",\"enum\":[\"Cash\",\"Commod\",\"Credit\",\"Cross Asset\",\"Econ\",\"Equity\",\"Fund\",\"FX\",\"Mortgage\",\"Rates\",\"Loan\",\"Social\",\"Cryptocurrency\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"sovereignSpreadContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of sovereign spread component to FCI. Only applicable to Euro countries.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ric\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Reuters instrument code (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rateType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Type of swap structured for a Central Bank swap: rate type = Meeting Forward, swaps structured trough End of Year, rateType = EOY Forward and for Spot OIS value rateType = Spot.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalFatalitiesSeniorHome\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Confirmed fatalities in Senior homes and care facilities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Notes which point of the lifecyle a securities lending loan is in.\",\"enum\":[\"PENDING\",\"OPEN\",\"CLOSED\",\"RETURNED\",\"PARTIAL RETURN\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"shortWeight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Short weight of a position in a given portfolio. Equivalent to position short exposure / total short exposure. If you have a position with a shortExposure of $20, and your portfolio shortExposure is $100, then your asset shortWeight would be 0.2 (20/100).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"geographyId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,10}$\",\"type\":\"string\",\"description\":\"Region or Country code for which the numbers are represented. For countries, it is the ISO 3166 2-digit country code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell7point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 7.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"nav\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net asset value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fiscalQuarter\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"One of the four three-month periods that make up the fiscal year.\",\"enum\":[\"Q1\",\"Q2\",\"Q3\",\"Q4\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"versionString\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,128}$\",\"type\":\"string\",\"description\":\"String representing the version number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payoffYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketImpact\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market impact is based on the Goldman Sachs Shortfall Model where available alongside best estimates from the desk.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Equals Analyst Meeting if the event indicates an analyst meeting. Equals Earnings Release if the event indicates an earnings release. Equals Sales Release when the event indicates a sales release. Indicates Drug Data when the event indicates an event related to drugs data. Equals Other for any other events.\",\"enum\":[\"Analyst Meeting\",\"Earnings Release\",\"Sales Release\",\"Drug Data\",\"Other\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"assetCountLong\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of assets in a portfolio with long exposure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell180cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 180 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"spot\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spot price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"applicationId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Client application identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"indicativeClosePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Indicative close price of an asset forseen at end of the day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Swap spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradingRestriction\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not the asset has trading restrictions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersPayOrReceive\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Pay or receive fixed\",\"enum\":[\"Pay\",\"Receive\",\"Straddle\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"priceSpotEntryUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]{0,20}$\",\"type\":\"string\",\"description\":\"Unit in which the opening price is reported.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unrealizedArrivalPerformanceBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated Arrival (in limit) since order inception ??? unrealized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"city\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w -]{0,64}$\",\"type\":\"string\",\"description\":\"City for which the weather data was gathered.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pnlWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total PnL Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"covariance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Covariance between two .\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bucketVolumeInShares\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast of the total volume of shares in the bucket.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"commodityForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commodity forecast value for the tenor, year or quarter.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Valid.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsCommodity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Commodity name for STS assets.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"initialPricingDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Initial pricing date for basket actions.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"indicationOfEndUserException\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If buyer or seller or both is electing the End User Exception.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windDirectionHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for wind direction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for E&S subsector-relative metrics.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The return an investor realizes on a bond sold at the mid price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fatalitiesUnderlyingConditionsPresent\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of fatalities of people with underlying conditions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceRangeInTicks\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Price Range of the stock in Ticks on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paceOfRollp25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The 25th-percentile of the pace of roll compared to previous quarters (over the last 5 years) at the same point in time, i.e. cross-sectional.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dayCloseRealizedUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Previous Close, in $.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pctChange\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage change of the latest trade price or value from the adjusted historical close.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"brightnessType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of measure required like visibility or sunshine.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonth3M\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numberOfRolls\":{\"anyOf\":[{\"type\":\"integer\",\"description\":\"Contract's number of rolls per year.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isoCountryCodeNumeric\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Iso numeric code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Price denomination and unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedVwapPerformanceUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated VWAP (in limit) since order inception ??? realized in $.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fuelType\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The type of fuel used to generate electricity\",\"enum\":[\"Solar\",\"Wind\",\"Hydro\",\"Nuclear\",\"Natural Gas\",\"Coal\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"bbid\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Bloomberg identifier (ticker and exchange code).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vegaNotionalAmount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The notional for a variance swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fatalitiesUnderlyingConditionsAbsent\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of fatalities of people with no underlying conditions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"effectiveDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The date at which the measure becomes effective.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"capped\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Whether a trade is capped at the block notional threshold.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rating\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Analyst Rating, which may take on the following values.\",\"enum\":[\"Buy\",\"Sell\",\"Neutral\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"optionCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of type of currency on the option premium.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isCloseAuction\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Flag indicating whether the bucket is within the close auction or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market implied correlation between two tenors.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"avgVentUtil\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average number of patients on a ventilator per week.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"underlyingAssetIds\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee IDs of the underlying assets.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy6point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 6.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapInLimitRealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs VWAP In Limit, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedClosingAuctionVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Estimated closing auction volume.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell2bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 2 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annualRisk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Annualized risk of a given portfolio, position or asset. Generally computed as annualized daily standard deviation of returns.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eti\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^(SDB|sdb)[0-9]{0,20}$\",\"type\":\"string\",\"description\":\"External Trade Identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapInLimitRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs VWAP In Limit, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rankMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rank of the Contributor Month to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketBuffer\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The actual buffer between holdings and on loan quantity for a market.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthJ24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthJ23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"oeId\":{\"anyOf\":[{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^[0-9a-fA-F]{0,32}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthJ22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthJ21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bbidEquivalent\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Bloomberg identifier (ticker and country code) equivalent - i.e. for OTCs options, the equivalent BBID on exchange.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"initBufferThresholdRequired\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The required buffer between holdings and on loan quantity for an asset on the date of loan initiation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2DesignatedMaturity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Floating rate index designated maturity period of leg 2.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturityOISSwapRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Matched Maturity OIS swap rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fairPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fair Price for a swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"participationRateInLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average in limit participation rate of the order, including principal fills.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"extMktClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w- ]{0,64}$\",\"type\":\"string\",\"description\":\"External MDAPI Class.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Denominated pricing currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"failedCount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of entities that failed the test.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1IndexLocation\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Location of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"supraStrategy\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Broad descriptor of a fund's investment approach. Same view permissions as the asset\",\"enum\":[\"Composite\",\"Credit\",\"Equity\",\"Equity Hedge\",\"Event Driven\",\"Fund of Funds\",\"Macro\",\"Multi-Strategy\",\"Other\",\"Quant\",\"Relative Value\",\"Risk Parity\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"dayCountConvention\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The determination of how interest accrues over time for the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"roundedNotionalAmount1\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total Notional amount or quantity of units of the underlying asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"roundedNotionalAmount2\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Same as Rounded Notional Amount 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"factorSource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w -]{0,32}$\",\"type\":\"string\",\"description\":\"Factor source. One of: Axioma, Prime.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthJ26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendingSecType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Securities lending identifiter for the security on loan.\",\"enum\":[\"ABS\",\"ADR\",\"BND\",\"CEF\",\"CMO\",\"COD\",\"COM\",\"CPN\",\"CPR\",\"CSH\",\"CVB\",\"CVP\",\"ETF\",\"GDR\",\"GOV\",\"LLP\",\"MBS\",\"MUN\",\"NVR\",\"NYR\",\"OEF\",\"OPT\",\"PFD\",\"RET\",\"RTS\",\"UIT\",\"WTS\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthJ25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leverage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Leverage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forecastDay\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The offset for forecast day for weather from today.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionFamily\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Style of the option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"generatorOutput\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The output of an electricity generator in MW.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceSpotStopLossValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Stop loss price value of the trade idea.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"kpiId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee unique KPI identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windGeneration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Wind generation value provided by source.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentageMidExecutedQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of total order filled at midpoint.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"borrowCost\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"An indication of the rate one would be charged for borrowing/shorting the relevant asset on that day, expressed in bps. Rates may change daily.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"knockOutDirection\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"The side of the knock-out price the market level must reach to trigger the knock out.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"riskModel\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\. -]{0,256}$\",\"type\":\"string\",\"description\":\"Model used to compute risk or performance attribution. Defines universe, factors, calibration period etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersVendor\":{\"anyOf\":[{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^[0-9a-fA-F]{0,32}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fairValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fair Value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pressureHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for pressure in Inch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"localCcyRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The interest rate of the local currency of the associated FX contract.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"endUserException\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If buyer or seller or both is electing the End User Exception.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell90cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 90 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"executionVenue\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether the SB swap transaction was executed on a registered swap execution facility or designated contract market or was executed as an off-facility swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryVwapInLimitRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Primary VWAP In Limit, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"approveRebalance\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"An approved basket.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedClosePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Closing Price adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lmsId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,32}$\",\"type\":\"string\",\"description\":\"Market identifier code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rebateRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Defines the rate of the cash-back payment to an investor who puts up collateral in borrowing a stock. A rebate rate of interest implies a fee for the loan of securities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell130cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 130 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell32bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 32 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paceOfRollp50\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The 50th-percentile of the pace of roll compared to previous quarters (over the last 5 years) at the same point in time, i.e. cross-sectional.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceMoveVsArrival\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Stock price return versus arrival price return (in bps).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"strikeRelative\":{\"anyOf\":[{\"description\":\"Strike as value, percent or at-the-money e.g. 62.5, 95%, ATM-25, ATMF, 10/vol, 100k/pv, p=10000, p=10000USD, $200K/BP, or multiple strikes 65.4/-45.8 \",\"anyOf\":[{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^((ATM|ATMF|A)|(atm|atmf|a)|[-+]?([0-9]*\\\\.[0-9]+|[0-9]+)\\\\%?)|([0-9]*[kKmMbB]?\\\\/((pv|bp|dv01|vol|ann|spot|fwd)|(PV|BP|DV01|VOL|ANN|SPOT|FWD)))|((p|P)=([0-9]*[kKmMbB]?)([a-zA-Z]{3})?)$\",\"type\":\"string\"},{\"type\":\"number\"},{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^-?\\\\d*(\\\\.\\\\d+)?(\\\\s*\\\\/\\\\s*-?\\\\d*(.\\\\d+)?)*\",\"type\":\"string\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"pressureType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of pressure required like sea level or barometric.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy40bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 40 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceNotation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Basis points, Price, Yield, Spread, Coupon, etc., value depending on the type of SB swap, which is calculated at affirmation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"strategy\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"More specific descriptor of a fund's investment approach. Same view permissions as the asset.\",\"enum\":[\"All\",\"Overall Book\",\"Active\",\"Active Extension\",\"Active Trading\",\"Activist\",\"Co-Invest / SPV\",\"Commodity\",\"Commodities\",\"Composite\",\"Conservative\",\"Convert Arb\",\"Convertible Arbitrage\",\"Credit Arbitrage\",\"Cross-Capital-Structure\",\"CTA / Managed Futures\",\"Currency\",\"Discretionary\",\"Discretionary Thematic\",\"Distressed\",\"Distressed Securities\",\"Distressed/Restructuring\",\"Diversified\",\"Energy and Basic Material\",\"Equity Hedge\",\"Equity Market Neutral\",\"Equity Only\",\"Event-Driven\",\"Fixed Income Arb\",\"Fixed Income-Asset Backed\",\"Fixed Income-Corporate\",\"Fixed Income-Sovereign\",\"Fundamental Equity Long / Short\",\"Fundamental Growth\",\"Fundamental Value\",\"General\",\"General Multi-Strategy\",\"Generalist\",\"Hybrid / Illiquid\",\"Long / Short\",\"Macro\",\"Market Defensive\",\"Merger Arb\",\"Merger Arbitrage\",\"Multi-Strategy\",\"Passive\",\"Quantitative Directional\",\"Relative Value Arbitrage\",\"Risk Premia\",\"Sector - Energy/Basic Materials\",\"Sector - Healthcare\",\"Sector - Technology\",\"Sector - Technology/Healthcare\",\"Sector-Specific\",\"Short Bias\",\"Special Situations\",\"Stat Arb\",\"Statistical Arbitrage\",\"Strategic\",\"Structured\",\"Systematic\",\"Systematic Diversified\",\"Technology and Healthcare\",\"Vol Arb / Options\",\"Volatility\",\"Volatility Target 10\",\"Volatility Target 12\",\"Volatility Target 15\",\"Yield Alternative\",\"Yield Alternatives\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"issueStatusDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lenderIncome\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Income earned by the Lender for the loan of securities to a borrower.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pbClientId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Prime Brokerage client identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"istatRegionCode\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Italian region code (ISTAT 2019).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell9bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 9 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ownerId\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier for user who owns the object.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"composite10DayAdv\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Composite 10 day ADV.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"maxLoanBalance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The maximum allowable balance that can be lent out for a lender.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ideaActivityType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Equals CorporateAction if the activity originates as a result of a corporate action. Equals GovernanceAction if the activity originates as a result of a control measure. Equals UserAction if the activity is user driven.\",\"enum\":[\"CorporateAction\",\"GovernanceAction\",\"UserAction\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"sell60cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 60 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ideaSource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Equals User if the idea activity originates from a sales person. Equals System if the idea activity is system generated.\",\"enum\":[\"User\",\"System\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"everOnVent\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of patients ever on ventilator for COVID.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy15cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 15 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unadjustedAsk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted ask level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"contributionName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[a-zA-Z ]*\",\"type\":\"string\",\"description\":\"Sector or type group name for the corresponding geography when metricName is either CAI_CONTRIBUTION_SECTOR or CAI_CONTRIBUTION_TYPE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"givenPlusPaid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total of given & paid.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lastFillPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Last execution price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortConvictionSmall\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The count of short ideas with small conviction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"upfrontPaymentCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Currency of upfront payment.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"spotSettlementDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The settlement date for a spot FX trade.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matrixOrder\":{\"anyOf\":[{\"maximum\":249,\"minimum\":0,\"type\":\"number\",\"description\":\"The matrix col/row order. Number from 0-249.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dateIndex\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"For a rates asset, represents the proxy for a meeting number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payerDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsIsPrimary\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not it is the primary exchange asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"breakEvenInflationChange\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Break even inflation change.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy130cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 130 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dwiContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of debt weighted exchange rate index to FCI. Only applicable to EM countries.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"asset2Id\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee unique asset identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averageFillPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average fill price for the order since it started.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"depthSpreadScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Z-score of the difference between the mid price and the best price an order to buy or sell a specific notional can be filled at.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell10cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 10 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"subAccount\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[0-9]{0,20}$\",\"type\":\"string\",\"description\":\"Subaccount.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy65cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 65 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bondCdsBasis\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Difference between bond spread and underlying cds of the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vendor\":{\"anyOf\":[{\"anyOf\":[{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Risk model vendor name\"},{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"additionalProperties\":false,\"enum\":[\"Goldman Sachs\",\"Thomson Reuters\",\"Solactive\",\"Bloomberg\",\"Axioma\",\"Goldman Sachs Prime Services\",\"Goldman Sachs Global Investment Research\",\"National Weather Service\",\"WM\",\"Hedge Fund Research, Inc.\",\"London Stock Exchange\",\"Goldman Sachs MDFarm\",\"PredictIt\",\"Iowa Electronic Markets\",\"RealClearPolitics\",\"538\",\"FiveThirtyEight\",\"Opinium\",\"YouGov\",\"MorningStar\",\"Survation\",\"Survation, YouGov\",\"European Centre for Disease Prevention and Control\",\"Centers for Disease Control and Prevention\",\"Johns Hopkins University\",\"Google\",\"National Health Service\",\"World Health Organization\",\"Wikipedia\",\"StarSchema\",\"Covid Working Group\",\"CovidTracking\",\"Bing\",\"FRED\",\"Institute for Health Metrics and Evaluation\",\"Refinitiv\",\"Goldman Sachs Global Investment Research, Refinitiv\",\"EPFR\"]}]},{\"maxItems\":1,\"type\":\"array\"}]},\"dataSet\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Z\\\\d_]{0,64}$\",\"type\":\"string\",\"description\":\"Unique id of dataset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalAmount2\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total Notional amount or quantity of units of the underlying asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalAmount1\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total Notional amount or quantity of units of the underlying asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queueingTime\":{\"anyOf\":[{\"type\":\"integer\",\"description\":\"Time for which risk calculation was queued (ms).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annReturn5Year\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total return representing past performance, used for GS Money Market onshore funds, over five years.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volumeStartOfDay\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Start of day forecast of daily volume.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceNotation3Type\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Basis points, Price, Yield, Spread, Coupon, etc., depending on the type of SB swap, which is calculated at affirmation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersFloatingRateDesignatedMaturity\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"executedNotionalLocal\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Executed notional in local currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"businessSponsor\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Business Sponsor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unexplained\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"PNL unexplained by risk model.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"seasonalAdjustmentShort\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d\\\\. ]{1,64}$\",\"type\":\"string\",\"description\":\"Seasonal adjustment of series.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"metric\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Metric for the associated asset.\",\"enum\":[\"Dividend Yield\",\"Earnings per Share\",\"Earnings per Share Positive\",\"Net Debt to EBITDA\",\"Price to Book\",\"Price to Cash\",\"Price to Earnings\",\"Price to Earnings Positive\",\"Price to Sales\",\"Return on Equity\",\"Sales per Share\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"ask\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Ask Price (price offering to sell).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"closePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Closing level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell100cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 100 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy180cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 180 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteStrike\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Actual strike value specified in outright terms.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell3point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 3.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liquidityScoreBuy\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The liquidity score assigned to buying the bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paymentFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An integer multiplier of a time period describing how often the parties to the SB swap transaction exchange payments associated with each party???s obligation. Such payment frequency may be described as one letter preceded by an integer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expenseRatioNetBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Gives basis point measure of management fee, net.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"metricType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,256}$\",\"type\":\"string\",\"description\":\"Type of the metric.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rankYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rank of the Contributor Year to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1Spread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Spread of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"coverageRegion\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\-\\\\s\\\\w:,\\\\\\\\/'&.)(*+!]{0,256}$\",\"type\":\"string\",\"description\":\"Coverage Region of a contributor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteReturnYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Absolute Return Year to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dayCountConvention2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Day count convention for leg 2.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fwdtier\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-\\\\\\\\/]{0,256}$\",\"type\":\"string\",\"description\":\"Forward tier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"degreeDays\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of the temperature of given type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"turnoverAdjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Turnover adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceSpotTargetValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Target price value of the trade idea.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketDataPoint\":{\"anyOf\":[{\"maxItems\":1,\"type\":\"array\",\"description\":\"The market data point (e.g. 3m, 10y, 11y, Dec19).\",\"items\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\^]{0,256}$\",\"type\":\"string\"}},{\"maxItems\":1,\"type\":\"array\"}]},\"numOfFunds\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of funds used for aggregation for a given period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"executionId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Execution ID.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"turnoverUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Turnover not adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1FloatingIndex\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If floating index leg, the index.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hedgeAnnualizedVolatility\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\",\"description\":\"Standard deviation of the annualized returns.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"benchmarkCurrency\":{\"anyOf\":[{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"futuresContract\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The related futures contract code if applicable.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"name\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,2048}$\",\"type\":\"string\",\"description\":\"Legal or published name for the asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"aum\":{\"anyOf\":[{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"lt\":{\"description\":\"search for values less than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gte\":{\"description\":\"search for values greater than or equal.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"lte\":{\"description\":\"search for values less than or equal to.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gt\":{\"description\":\"search for values greater than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]}},\"additionalProperties\":false,\"description\":\"Operations for searches.\"},{\"type\":\"number\",\"description\":\"Assets under management.\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1DayCountConvention\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The determination of how interest accrues over time for the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cbsCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Z0-9]{0,255}$\",\"type\":\"string\",\"description\":\"CBS Code for Municipality in Netherlands.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"folderName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Folder Name of a chart.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"apiUsage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Usage Metrics for a given client entity based on specified service and metrics feature.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twapInterval\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Time interval (in milliseconds) at which a TWAP attempts to fill.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"uniqueId\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A unique identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionExpirationDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"An indication of the date that the option is no longer available for exercise.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swaptionAtmFwdRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Swaption ATM forward rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liveDate\":{\"anyOf\":[{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"lt\":{\"description\":\"search for values less than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gte\":{\"description\":\"search for values greater than or equal.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"lte\":{\"description\":\"search for values less than or equal to.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gt\":{\"description\":\"search for values greater than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]}},\"additionalProperties\":false,\"description\":\"Operations for searches.\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Date a given asset was initially publishing official levels.\",\"format\":\"date\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"corporateActionType\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Different types of corporate actions from solactive\",\"enum\":[\"Acquisition\",\"Cash Dividend\",\"Identifier Change\",\"Rights Issue\",\"Share Change\",\"Special Dividend\",\"Spin Off\",\"Stock Dividend\",\"Stock Split\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"primeId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Prime Id.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"description\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\t\\\\w&\\\\[\\\\]\\\\\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,5000}$\",\"type\":\"string\",\"description\":\"Description of asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsIsCountryPrimary\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not it is the primary exchange asset for the exchange country.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rebateRateLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The maximum allowable rebate rate that can be paid out to the borrower of a stock.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"factor\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,2048}$\",\"type\":\"string\",\"description\":\"Name of the factor. i.e. Momentum\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysOnLoan\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The number of days this loan as been on our books.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longConvictionSmall\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The count of long ideas with small conviction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell40cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 40 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativePayoffYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsfeer\":{\"anyOf\":[{\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,64}$\",\"type\":\"number\",\"description\":\"Goldman Sachs Fundamental Equilibrium Exchange Rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeHitRateQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Quarter to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wam\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weighted average maturity, average of effective maturities of all securities held in portfolio, weighted.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wal\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weighted average life, measures sensitivity to changes in liquidity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quantityccy\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,4}$\",\"type\":\"string\",\"description\":\"Quantity unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"backtestId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee unique backtest identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dirtyPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price of the Bond including accrued interest.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"corporateSpreadContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of corporate spread component to FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeHumidityHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for relative humidity in percentage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"multipleScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Multiple percentile relative to Americas coverage universe (a higher score means more expensive).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"betaAdjustedExposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Beta adjusted exposure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dividendPoints\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Expected Dividend in index points.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"brightness\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of the measure type selected.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersReceiverDesignatedMaturity\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bosInTicksDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,100}$\",\"type\":\"string\",\"description\":\"Description of the Stock's Bid-Offer Spread in Ticks on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"testId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,64}$\",\"type\":\"string\",\"description\":\"Unique ID for test configuration.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedCorrelation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Correlation of an asset implied by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"normalizedPerformance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Performance that is normalized to 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bytesConsumed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Bytes of raw data fetched in a run of the checker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swaptionVol\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Historical implied normal volatility for a liquid point on swaption vol surface.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedClosingVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Estimated closing auction volume (in shares).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"issuer\":{\"anyOf\":[{\"maxLength\":100,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,100}$\",\"type\":\"string\",\"description\":\"The issuer of this bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dividendYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Annualized Dividend Yield.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The type of market for Power. Values may include Physical Bilateral, Day Ahead or Real Time.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numUnitsLower\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Units Lower.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceOrigin\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source origin.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedsAssetSwapSpread3m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proceeds asset swap spread vs 3m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rounded total quantity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"internalUser\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether user is internal or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell40bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 40 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"redemptionOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^^[\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"-]{0,128}\",\"type\":\"string\",\"description\":\"Indicates the calculation convention for callable instruments.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalUnit2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of reported notional price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notionalUnit1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of reported notional price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sedol\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Z0-9]{6}[0-9]$\",\"type\":\"string\",\"description\":\"SEDOL - Stock Exchange Daily Official List (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"roundingCostPnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rounding Cost Profit and Loss.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The return an investor realizes on a bond sold at the mid price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unexecutedNotionalLocal\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total un-executed notional in local currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sustainGlobal\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"True if the stock is on the SUSTAIN (Global) 50 list as of the corresponding date. False if the stock is removed from the SUSTAIN (Global) 50 list on the corresponding date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"endingDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"End date of the period the valuation refers to.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedsAssetSwapSpread12m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proceeds asset swap spread vs 12m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossInvestmentWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total gross investment Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annReturn3Year\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total return representing past performance, used for GS Money Market onshore funds, over three years.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sharpeWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sharpe ratio Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"discountFactor\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Present Value of 1 unit of currency on an earlier date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeReturnMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Relative Return Month to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceChangeOnDay\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price change on day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy100cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 100 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forwardPoint\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Outright forward minus spot.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fci\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Nominal FCI value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"recallQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Defines the amount of shares being recalled in a stock loan recall activity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fxPositioning\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Positioning data in the FX market.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsidEquivalent\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\d]{1,20}\",\"type\":\"string\",\"description\":\"Goldman Sachs internal equity identifier equivalent - i.e. - for Corporate Bonds, the equivalent GSID of the underlying security.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"categories\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{1,512}$\",\"type\":\"string\",\"description\":\"Categories for series.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"extMktAsset\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w- ]{0,64}$\",\"type\":\"string\",\"description\":\"External MDAPI Asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quotingStyle\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,32}$\",\"type\":\"string\",\"description\":\"Quoting style.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"errorMessage\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,5000}$\",\"type\":\"string\",\"description\":\"Error message to correspond to error in factor field.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The mid price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proceedsAssetSwapSpread6m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proceeds asset swap spread vs 6m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsEmDm\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Emerging or developed market classification.\",\"enum\":[\"EM\",\"DM\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"embeddedOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether or not the option fields are for an embedded option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon2Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 2 day time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ageBand\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"The age band of the caller based on the date of birth provided.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"returnsEnabled\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not returns are enabled for the Hedge Fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"runId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Unique id for check runs, used to query entity data corresponding to dataset level data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queueInLots\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Queue size in Lots (if applicable) of the stock on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tenderOfferExpirationDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Expiration date of the tender offer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midcurveAnnuity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Midcurve annuity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendingFundNavTrend\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net Asset Value trend of a securities lending fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cloudCoverForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for cloud cover.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostParticipationRate5Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 5 percent participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"defaultBackcast\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Is basket backcasted using initial positions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newsOnIntensity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A measure of the intensity of media attention to each stock compared with other stocks in the index.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceFormingContinuationData\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether an SB swap transaction is a post-execution event that affects the price of the swap transaction, e.g. terminations, assignments, novations, exchanges, transfers, amendments, conveyances or extinguishing of rights that change the price of the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedShortInterest\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Adjusted Short Interest rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newHospitalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of new patients hospitalized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersStrike\":{\"anyOf\":[{\"description\":\"Strike as value, percent or at-the-money e.g. 62.5, 95%, ATM-25, ATMF, 10/vol, 100k/pv, p=10000, p=10000USD, $200K/BP, or multiple strikes 65.4/-45.8 \",\"anyOf\":[{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^((ATM|ATMF|A)|(atm|atmf|a)|[-+]?([0-9]*\\\\.[0-9]+|[0-9]+)\\\\%?)|([0-9]*[kKmMbB]?\\\\/((pv|bp|dv01|vol|ann|spot|fwd)|(PV|BP|DV01|VOL|ANN|SPOT|FWD)))|((p|P)=([0-9]*[kKmMbB]?)([a-zA-Z]{3})?)$\",\"type\":\"string\"},{\"type\":\"number\"},{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^-?\\\\d*(\\\\.\\\\d+)?(\\\\s*\\\\/\\\\s*-?\\\\d*(.\\\\d+)?)*\",\"type\":\"string\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"buy35cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 35 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2TotalNotional\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total Notional amount or quantity of units of the leg 2 underlying asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersEffectiveDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Relative effective date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annReturn10Year\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total return representing past performance, used for GS Money Market onshore funds, over ten years.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numAdultIcuBeds\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"All ICU beds, burn ICU beds, surgical ICU beds, or trauma ICU beds meant for emergency situations where hospitals may use additional intensive care beds to supplement an influx of patients. Does not include neonatal, pediatric or premature ICU beds.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"daysToExpiration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Days to Expiration for the contract.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"continuationEvent\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether an SB swap transaction is a post-execution event that affects the price of the swap transaction, e.g. terminations, assignments, novations, exchanges, transfers, amendments, conveyances or extinguishing of rights that change the price of the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wiId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Weather Index Identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketCapCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Category of market capitalizations a fund is focused on from an investment perspective. Same view permissions as the asset.\",\"enum\":[\"All\",\"Large\",\"Mid\",\"Small\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"historicalVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"One month rolling average.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy5cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 5 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventStartDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The start date of the event if the event occurs during a time window, in the time zone of the exchange where the company is listed (optional).\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1FixedRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"If fixed rate leg, the fixed rate of leg 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"equityGamma\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Gamma exposure to equity products.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rptId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"DDR generated unique and random ID for reconciliation purpose.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"grossIncome\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The income earned by the reinvested collateral including the rebate or fee, excluding lender or partner fees.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"emId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Entity Master identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetCountInModel\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of assets in a portfolio in a given risk model.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsCreditRegion\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Credit risk region.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"minTemperature\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Minimum temperature observed on a given day in fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fillType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Dark/Lit etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"failPct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fail percentage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isoCountryCodeAlpha2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Iso2 country codes (2-digit).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isoCountryCodeAlpha3\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Iso3 country codes (3-digit).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"amount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Amount corporate actions pay out.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendingFundAcct\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Account associated with the securities lending fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rebate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Amount of the payment to an investor who puts up collateral in borrowing a stock.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"electionType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w_]{0,128}$\",\"type\":\"string\",\"description\":\"Type of election e.g. presidential.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeHitRateMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Hit Rate Ratio Month to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Volatility of an asset implied by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"spread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Quoted (running) spread (mid) of buying / selling protection on an index. (Equally weighted CDS basket). In basis points.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"variance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market implied variance between two tenors.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wtdDegreeDaysDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for weighted degree day in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swaptionAnnuity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Swaption annuity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy6bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 6 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"g10Currency\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Is a G10 asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"humidityForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for humidity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativePeriod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The relative period forward for which the forecast is available.\",\"enum\":[\"3m\",\"6m\",\"12m\",\"EOY1\",\"EOY2\",\"EOY3\",\"EOY4\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"user\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-\\\\\\\\/]{0,256}$\",\"type\":\"string\",\"description\":\"Originating platform.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"customer\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-\\\\\\\\/]{0,256}$\",\"type\":\"string\",\"description\":\"Customer kerberos.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1ResetFrequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An integer multiplier of a period describing how often the parties to an SB swap transaction shall evaluate and, when applicable, change the price used for the underlying assets of the swap transaction (leg 1). Such reset frequency may be described as one letter preceded by an integer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queueClockTimeLabel\":{\"anyOf\":[{\"type\":\"string\",\"description\":\"Label of the Stock's Queue Clock Time on the particular date.\",\"enum\":[\"Long\",\"Very Long\",\"Medium\",\"Short\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"paceOfRollp100\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The 100th-percentile of the pace of roll compared to previous quarters (over the last 5 years) at the same point in time, i.e. cross-sectional.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsGicsSubIndustry\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"GICS Sub Industry classification (level 4).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dewPointHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for dew point in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"locationType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-A-Za-z0-9\\\\(\\\\)\\\\/*+!&. :,'_??\\\\x{00C0}-\\\\x{E007A}]{0,255}$\",\"type\":\"string\",\"description\":\"The type of location (of varying granularity), e.g. Nation, Region, etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"facetDivisionalReportingGroupId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"FACET Divisional Reporting Group ID (DRG) most relevant to the Starting EMMA LE ID which contains all associated OE IDs.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedTwapPerformanceUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated TWAP since order inception ??? realized in $.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swapRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"ATM fixed rate for a benchmark tenor on a currency's fixed-floating swap curve.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"algoExecutionStyle\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Algo execution style. Dictates how an algo order is being filled.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clientContact\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Name of client(s) requesting data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"minTemperatureHour\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value of hour with minimum temperature in a day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradingCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Execution currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalByOnset\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number, by onset of infection, as opposed to confirmation. The date of onset is calculated as illness onset date if known. If not, an estimated illness onset date was calculated using specimen collection date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"agencySwapSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Agency swap spread.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rank\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rank to determine most relevant asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mixedSwapOtherReportedSDR\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indicates the other SDR to which a mixed swap is reported.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"humidity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The humidity forecast for given location.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dataSetCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]\\\\.\\\\?@#:,$ \\\\t'\\\"-=\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Top level grouping of dataset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs VWAP, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy9bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 9 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalTested\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of tested cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fatalitiesConfirmed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of confirmed fatalities.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"universeId1\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"The risk model universe identifier value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersPayerDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"universeId2\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"The risk model universe identifier value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidLow\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Lowest Bid Price (price willing to buy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bucketizePrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Aggregated price of power for given quantity bucket.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fairVarianceVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The strike in volatility terms, calculated as square root of fair variance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"covid19\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of people affected by covid19 for agiven country.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clientExposure\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Exposure of client positions to the factor in percent of equity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2TotalNotionalUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of reported notional price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell45cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 45 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsSustainSubSector\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,256}$\",\"type\":\"string\",\"description\":\"GS SUSTAIN sector.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sinkable\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"A bond that is protected by a fund (called a sinking fund) that sets aside money to ensure principal and interest payments are made by the issuer as promised.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isReal\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Flag indicating whether the value is real or nominal.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"maxTemperatureHour\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value of hour with max temperature in a day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2AveragingMethod\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Averaging method of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"jsn\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Japan security number (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell160cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 160 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"knockInDirection\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"The side of the knock-in price the market level must reach to trigger the knock in.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dayCloseUnrealizedUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealized performance vs Close, in $.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tenor\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"Tenor of instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pricingConvention\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Pricing convention that is used.\",\"enum\":[\"Discount Margin\",\"Money Market Yield\",\"Price\",\"Spread\",\"Yield To Convention\",\"Z Spread\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"popularity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Popularity of series.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"floatingRateOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9-\\\\. ]{0,31}$\",\"type\":\"string\",\"description\":\"The underlying benchmark for the floating rate, e.g. USD-LIBOR-BBA, EUR-EURIBOR-TELERATE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hedgeValueType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The type of value for which is applied to value the hedge in the comparison.\",\"enum\":[\"Notional\",\"Ratio\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersClearingHouse\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Clearing house.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"disclaimer\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,5000}$\",\"type\":\"string\",\"description\":\"The legal disclaimer associated with the record.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"payerFrequency\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanFee\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fee charged for the loan of securities to a borrower in a securities lending agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"deploymentVersion\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d&\\\\/()+%\\\\.:;,!<>$* '\\\"]{0,128}$\",\"type\":\"string\",\"description\":\"Deployment version.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy16bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 16 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeDayCount\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The determination of how interest accrues over time for the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceToSales\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price to sales.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newIdeasQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas received by clients Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"subdivisionName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-A-Za-z0-9\\\\(\\\\)\\\\/*+!&. :,'_??\\\\x{00C0}-\\\\x{E007A}]{0,255}$\",\"type\":\"string\",\"description\":\"Name of the region or subdivision.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedAskPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Ask Price (price offering to sell) adjusted for corporate actions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"factorUniverse\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,2048}$\",\"type\":\"string\",\"description\":\"Factor universe.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalRt\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Arrival Realtime.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"internalIndexCalcAgent\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Calculation agent of the index.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"excessMarginValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Available credit value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"transactionCost\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Transaction cost.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"centralBankSwapRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Returns the OIS swap rate for a swap structured between consecutive meeting dates.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"previousNewConfirmed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The previous day's number of new confirmed cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unrealizedVwapPerformanceBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated VWAP (in limit) since order inception ??? unrealized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"degreeDaysDailyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for degree days in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"positionAmount\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Corporate actions amount * shares.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"heatIndexHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for heat index in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"maRank\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"M&A Rank, which may take on the following values: 1 represents high (at least 30%, but less than 50%) probability of the company becoming an acquisition target, 2 represents medium (at least 15%, but less than 30%) probability and 3 represents low (less than 15%) probability.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fxPositioningSource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Source of positioning data in the FX market.\",\"enum\":[\"PositioningScore\",\"OptionsPos\",\"Flow\",\"CFTC\",\"ETF\",\"RiskReversal\",\"EMFI\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedVolatilityByDeltaStrike\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Volatility of an asset implied by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mqSymbol\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,31}$\",\"type\":\"string\",\"description\":\"Goldman Sachs Marquee Symbol applied to entities such as Backtester.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numTotalUnits\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total Units.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"corporateAction\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not it is a corporate action.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1PriceType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Price denomination and unit of leg 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersPayerRateOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.()\\\\/* -]{0,32}\",\"type\":\"string\",\"description\":\"The underlying benchmark for the payer, e.g. USD-LIBOR-BBA, EUR-EURIBOR-TELERATE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell20cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 20 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2FixedPaymentCurrency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If fixed payment leg, the unit of fixed payment.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gRegionalScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for G metrics within its region.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hardToBorrow\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not an asset is hard to borrow.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rollVwap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Calendar spread vwap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wpk\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Wertpapierkennnummer (WKN, WPKN, Wert), German security identifier code (subject to licensing).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bespokeSwap\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indication if the trade is bespoke.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersExpirationDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Relative expiration date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"countryName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-A-Za-z0-9\\\\(\\\\)\\\\/*+!& .:,'_??\\\\x{00C0}-\\\\x{E007A}]{0,255}$\",\"type\":\"string\",\"description\":\"Country name for which FCI is calculated.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"carry\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Carry.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"startingDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Start date of the period the valuation refers to.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-\\\\:]{0,256}$\",\"type\":\"string\",\"description\":\"Loan reference for a securities lending loan.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"onboarded\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not social domain has been onboarded.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liquidityScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Liquidity conditions in the aggregate market, calculated as the average of touch liquidity score, touch spread score, and depth spread score.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longRatesContribution\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Contribution of long rate component to FCI.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceDateSpan\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Date span for event in days.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annYield6Month\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Calculates the total return for 6 months, representing past performance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"underlyingDataSetId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d_]{0,64}$\",\"type\":\"string\",\"description\":\"Dataset on which this (virtual) dataset is based.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"closeUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted Close level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"valueUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,64}$\",\"type\":\"string\",\"description\":\"Value unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quantityUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Unit of measure for trade quantity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"adjustedLowPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Adjusted low level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isMomentum\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Flag indicating whether the value is a momentum value or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"longConvictionLarge\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The count of long ideas with large conviction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"oad\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Option-adjusted duration.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rate of the asset for the time period in percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"couponType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The coupon type of the bond.\",\"enum\":[\"Fixed\",\"Float\",\"Hybrid\",\"Zero\",\"Stepup\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"client\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Entity name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"convictionList\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Conviction List, which is true if the security is on the Conviction Buy List or false otherwise. Securities with a convictionList value equal to true are by definition a subset of the securities with a rating equal to Buy.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"passiveEtfRatio\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ratio of passive ETF ownership, i.e the proportion of a company's marketcap that is held by passive ETFs. Normalised between -3 (low level) and +3 (high level).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthG26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthG25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthG24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthG23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"typeOfReturn\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The type of return for the commodity index. Only applicable for commodity indices.\",\"enum\":[\"Spot\",\"Total\",\"Roll\",\"Excess\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthG22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"servicingCostLongPnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Servicing Cost Long Profit and Loss.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"excessMarginPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Available credit percentage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthG21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalMild\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of active cases with mild symptoms.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realizedArrivalPerformanceBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated Arrival (in limit) since order inception ??? realized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"precipitationDailyForecastInches\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for precipitation in Inch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exchangeId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]*$\",\"type\":\"string\",\"description\":\"Unique identifier for an exchange.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2FixedPayment\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"If fixed payment leg, the fixed payment amount, which is price*number of contracts bought*contract unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon20Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 20 day time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realm\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\.\\\\w\\\\d ]{0,64}$\",\"type\":\"string\",\"description\":\"Realm.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Bid Price (price willing to buy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hedgeValue\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isAggressive\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Indicates if the fill was aggressive or passive.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"floatingRateDesignatedMaturity\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentageNearExecutedQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of total order filled at passive touch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"orderId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w-]*$\",\"type\":\"string\",\"description\":\"The unique ID of the order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hospitalType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+#%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Categorical type of hospital as defined by the last four digits of the hospital???s Medicare Provider Number, ie. Critical Access Hospital, Short Term Acute Care Hospital, etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dayCloseRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Realised performance vs Previous Close, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"precipitationHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for precipitation in Inch.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marketCapUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Market capitalization of a given asset denominated in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"auctionFillsPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of total order filled in auction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"highPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"High level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteShares\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The number of shares without adjusting for side.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fixedRateDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"model\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,2048}$\",\"type\":\"string\",\"description\":\"Model.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unrealizedTwapPerformanceUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated TWAP since order inception ??? unrealized in $.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"id\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"maturity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Maturity of the instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"deltaChange\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w.,:;$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\"}},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthX24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"restrictionStartDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The date at which the security restriction was enacted.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"touchLiquidityScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Z-score of the amount available to trade at the top of the aggregated order book.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthX23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthX22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"factorCategoryId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]{0,20}$\",\"type\":\"string\",\"description\":\"Id for Factor Categories. Note that 'CUR' should be used to represent the currency category. Must match regex pattern ^[a-zA-Z0-9]{0,20}$.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"securityTypeId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"PadMaster unique security type identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthX21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"investmentYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total net investment Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2Notional\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total Notional amount or quantity of units of the leg 2 underlying asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell1bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 1 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell200cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 200 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expectedCompletionDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Expected day of acquisition completion.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"spreadOptionVol\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Historical implied normal volatility for a liquid point on spread option vol surface.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell80cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 80 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"inflationSwapRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Zero coupon inflation swap break-even rate for a given currency.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"activeQueries\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Active Queries.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell45bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 45 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"embededOption\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether or not the option fields are for an embedded option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventSource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Equals GS if the event is sourced from Goldman Sachs Global Investment Research analysts. Equals TR if the event is sourced from Refinitive StreetEvents.\",\"enum\":[\"GS\",\"TR\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"qisPermNo\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"QIS Permanent Security Number.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settlement\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Swap Settlement Type\",\"enum\":[\"Phys.CLEARED\",\"Physical\",\"Cash.CollatCash\",\"Cash.PYU\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"shareclassId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[a-z0-9]{24}\",\"type\":\"string\",\"description\":\"Identifies shareclass with a unique code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"feature2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Second metrics feature.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"feature3\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Third metrics feature.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsCommoditySector\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Commodity sector for STS assets.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exceptionStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The violation status for this particular line item.\",\"enum\":[\"Open\",\"Closed\",\"N/A\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"salesCoverage\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Primary or secondary sales coverage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"feature1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"First metrics feature.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostParticipationRate10Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 10 percent participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventTime\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^([01]?[0-9]|2[0-3]):[0-5][0-9]$\",\"type\":\"string\",\"description\":\"The time of the event if the event has a specific time or the end time of the event if the event occurs during a time window (optional). It is represented in HH:MM 24 hour format in the time zone of the exchange where the company is listed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"positionSourceName\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Position source name for quick access.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"deliveryDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The final date by which the underlying commodity for a futures contract must be delivered in order for the terms of the contract to be fulfilled.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"interestRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Interest rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"side\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Long or short.\",\"enum\":[\"Long\",\"Short\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"dynamicHybridAggressiveStyle\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Aggressive style of a Dynamic Hybrid order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"complianceRestrictedStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\/]{0,16}$\",\"type\":\"string\",\"description\":\"Restricted status as set by compliance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"borrowFee\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"An indication of the rate one would be charged for borrowing/shorting the relevant asset on that day, expressed in annualized percent terms. Rates may change daily.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"everIcu\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of patients ever in intensive care for COVID.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"noWorseThanLevel\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Prevents execution if the market level is worse than the specified level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanSpread\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The difference between the investment rate on cash collateral and the rebate rate of a loan.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon12Hour\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 12 hour time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dewPoint\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Temperature in fahrenheit below which water condenses.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"researchCommission\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The dollar amount of commissions received from clients.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy2bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 2 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetClassificationsRiskCountryCode\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{0,100}$\",\"type\":\"string\",\"description\":\"Risk Country code (ISO 3166).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newIdeasMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas received by clients Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"varSwapByExpiry\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike such that the price of an uncapped variance swap on the underlying index is zero at inception.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sellDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Sell date of the securities triggering the stock loan recall activity.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"aumStart\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Assets under management at the start of the period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersSettlement\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Settlement type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"maxTemperature\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Maximum temperature observed on a given day in fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"acquirerShareholderMeetingDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Shareholders meeting date for acquiring entity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"countIdeasWtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas alive at a time Week to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalRtNormalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Performance against Benchmark in pip.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"reportType\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Type of report to execute\",\"enum\":[\"Portfolio Performance Analytics\",\"Portfolio Factor Risk\",\"Portfolio Aging\",\"Asset Factor Risk\",\"Basket Create\",\"Basket Backcast\",\"Scenario\",\"Iselect Backtest\",\"Backtest Run\",\"Analytics\",\"Risk Calculation\",\"PCO Generate Orders\",\"PCO Program Change\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceURL\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source URL.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedReturn\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Estimated return of asset over a given period (e.g. close-to-close).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"high\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"High level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceLastUpdate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source last update.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sunshineForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for sunshine.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"quantityMW\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Quantity of electricity in megawatts.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell70cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 70 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell110cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 110 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"pnodeId\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Pricing node identifier sourced from Morningstar.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"humidityType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The humidity type: Relative, average etc.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"prevCloseAsk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Previous business day's close ask price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"level\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Level of the 5-day normalized flow in a given factor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedVolatilityByExpiration\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Volatility of an asset implied by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersFixedRateDayCountFraction\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"esMomentumScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A company's score for E&S subsector-relative momentum.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2Index\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If floating index leg, the index.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"netWeight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Difference between the longWeight and shortWeight. If you have IBM stock with shortWeight 0.2 and also IBM stock with longWeight 0.4, then the netWeight would be 0.2 (-0.2+0.4).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"portfolioManagers\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Portfolio managers of asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bosInTicks\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Bid-Offer Spread of the stock in Ticks on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersCouponType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The coupon type of the bond.\",\"enum\":[\"Fixed\",\"Float\",\"Hybrid\",\"Zero\",\"Stepup\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"expectedResidualQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Expected Residual Quantity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"rollDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Roll Dates for Equity Index Quarterly Futures.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dynamicHybridSpeed\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Execution speed of a Dynamic Hybrid order.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"capFloorVol\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Historical implied normal volatility for a liquid point on cap and floor vol surface.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"targetQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The target executed quantity as published from the algo.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"submitter\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Name of person submitting request.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"no\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price of no contract.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"notional\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Notional.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esDisclosurePercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The percentage of E&S metrics the company discloses relative to the number of E&S metrics relevant for its subsector.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"closeExecutedQuantityPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of order executed in closing Auction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"twapRealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs TWAP In Limit, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isOpenAuction\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Flag indicating whether the bucket is within the open auction or not.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1Type\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indication if leg 1 is fixed or floating or Physical.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"wetBulbTempHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for wet bulb temperature in Fahrenheit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cleanupPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The WIG price used by the algo.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"total\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total exposure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"filledNotionalUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Executed Notional in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee unique asset identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"testStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Result of the quality test.\",\"enum\":[\"Pass\",\"Fail\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"mktType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\[\\\\]`\\\\.#:!<>$* \\\\t'\\\"\\\\-=\\\\\\\\\\\\^\\\\|]{0,256}$\",\"type\":\"string\",\"description\":\"The MDAPI Type (e.g. IR BASIS, FX Vol).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"yield30Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net income per share for last 30 days/NAV.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy28bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 28 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"proportionOfRisk\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Proportion of risk with respect to the portfolio's total risk.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthK23\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthK22\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthK21\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryEntityId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Primary Key for an entity; in use for Data Quality Checker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cross\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"FX cross symbol.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ideaStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The activity status of the idea.\",\"enum\":[\"Open\",\"Closed\",\"Cancelled\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"contractSubtype\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Contract subtype.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sri\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not an asset is classified as socially responsible investing.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fxForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"FX forecast value for the relative period.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fixingTimeLabel\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\d+:\\\\d+]{0,128}$\",\"type\":\"string\",\"description\":\"Time at which the fixing was taken.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"isETF\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Whether or not assetType is an ETF.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fillId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w-]*$\",\"type\":\"string\",\"description\":\"Unique identifier for a fill.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"excessReturns\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Excess returns for backtest.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dollarReturn\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Dollar return of asset over a given period (e.g. close-to-close).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"orderInLimit\":{\"anyOf\":[{\"type\":\"boolean\",\"description\":\"Is the order???s passive leg in limit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expiryTime\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Order expiration date.\",\"format\":\"date-time\"},{\"maxItems\":1,\"type\":\"array\"}]},\"returnOnEquity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Return on equity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthK26\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthK25\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"futureMonthK24\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Commods future month code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"restrictionEndDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The date at which the security restriction was lifted.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queueInLotsDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,100}$\",\"type\":\"string\",\"description\":\"Description of the Stock's Queue size in Lots (if applicable) on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volumeLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The allowed percentage of ATV that can be placed on loan for a lender.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"objective\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The objective of the hedge.\",\"enum\":[\"Minimize Factor Risk\",\"Replicate Performance\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"navPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net asset value price. Quoted price (mid, 100 ??? Upfront) of the underlying basket of single name CDS. (Theoretical Index value). In percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1UnderlyingAsset\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The asset, reference asset, or reference obligation for payments of a party???s obligations under the SB swap transaction reference.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"privatePlacementType\":{\"anyOf\":[{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:;,!$* '\\\"\\\\n\\\\r-]{0,50}$\",\"type\":\"string\",\"description\":\"Regulation that applies to a bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"hedgeNotional\":{\"anyOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\",\"description\":\"Notional value of the hedge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askLow\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The lowest ask Price (price offering to sell).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"intendedPRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The intended participation rate from the algo.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expiry\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"The time period before the option expires.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"avgMonthlyYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Only used for GS Money Market funds, assumes sum of the past 30 days, divided by 30, and expressed as a percent.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"periodDirection\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Direction of the outlook period.\",\"enum\":[\"forward\",\"trailing\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"prevRptId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"On cancellations and corrections, this ID will hold the Dissemination ID of the originally published real-time message.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"earningsPerShare\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Earnings per share.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"strikePercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike compared to market value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esProductImpactPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A percentile that captures a company's E&S product impact ranking within its subsector.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapRealizedCash\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs VWAP, in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"parAssetSwapSpread1m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Par asset swap spread vs 1m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"prevCloseBid\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Previous close BID price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"minimumIncrement\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The minimum increment size of the bond purchase allowed above the minimum denomination as authorized by the bond documents.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostHorizon16Day\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 16 day time horizon.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"investmentMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total net investment Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settlementDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The settlement date of the associated FX contract.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"weightedAverageMidNormalized\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Performance against Benchmark in pip.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"salesPerShare\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sales per share.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unadjustedClose\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted Close level of an asset based on official exchange fixing or calculation agent marked level.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"The date at which the securities loan was enacted.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturitySwapSpread1m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Matched maturity swap spread vs 1m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"collateralPercentageActual\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Collateral percentage covering contractual the given position.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwapInLimitUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated unrealised performance vs VWAP In Limit, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"metricValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value of the metric calculated for the given geographyName and date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"autoExecState\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Auto Execution State.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalRecovered\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of recovered cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeReturnYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Relative Return Year to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tickServer\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!$* '\\\"-=]{0,256}$\",\"type\":\"string\",\"description\":\"Tickserver Symbol.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cumulativeVolumeInPercentage\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Forecast of the percentage of the cumulative volume of shares from start of day to the end of the bucket interval, relative to the total daily volume.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"realTimeRestrictionStatus\":{\"anyOf\":[{\"maxItems\":1,\"type\":\"array\",\"description\":\"Real Time Restricted status as set by compliance.\",\"items\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\/]{0,16}$\",\"type\":\"string\"}},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w \\\\-\\\\\\\\/]{0,128}$\",\"type\":\"string\",\"description\":\"Trade type.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"settlementType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Swap Settlement Type\",\"enum\":[\"Phys.CLEARED\",\"Physical\",\"Cash.CollatCash\",\"Cash.PYU\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"netChange\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Difference between the lastest trading price or value and the adjusted historical closing value or settlement price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numberOfUnderliers\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of underliers.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"swapType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Swap type of position.\",\"enum\":[\"Eq CFD Standard\",\"Eq Execution Swap\",\"Eq Swap\",\"Eq Swap OET Asymmetric\",\"Eq Swap OET Simple\",\"Eq Swap Term Facility\",\"Eq Synthetic OET\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"forecastType\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Type of return for commodity indices. Spot for individual commodities.\",\"enum\":[\"spotReturn\",\"totalReturn\",\"rollReturn\",\"spot\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1Notional\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total Notional amount or quantity of units of the leg 1 underlying asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sellSettleDate\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Data that the sell of securities will settle.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newIdeasYtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ideas received by clients Year to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"managementFee\":{\"anyOf\":[{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"lt\":{\"description\":\"search for values less than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gte\":{\"description\":\"search for values greater than or equal.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"lte\":{\"description\":\"search for values less than or equal to.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]},\"gt\":{\"description\":\"search for values greater than.\",\"anyOf\":[{\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"format\":\"date\"}]}},\"additionalProperties\":false,\"description\":\"Operations for searches.\"},{\"type\":\"number\",\"description\":\"Fee that funds charge for management in percent.\"}]},{\"maxItems\":1,\"type\":\"array\"}]},\"parAssetSwapSpread3m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Par asset swap spread vs 3m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell36bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 36 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturitySwapSpread3m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Matched maturity swap spread vs 3m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Unique id of data provider.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"country\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\., ']*$\",\"type\":\"string\",\"description\":\"Country of incorporation of asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"vwap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"VWAP benchmark price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"touchSpreadScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Z-score of the difference between highest bid and lowest offer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ratingSecondHighest\":{\"anyOf\":[{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Second highest bond rating between Moody's, Fitch, and Standard and Poor's.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell24bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 24 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"frequency\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Requested frequency of data delivery.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"activityId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique Activity identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"estimatedImpact\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Likely impact of a proposed trade on the price of an asset (bps). The model's shortfall estimates reflect how much it cost to execute similar trades in the past, as opposed to providing a hypothetical cost derived using tick data.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell35cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 35 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"loanSpreadBucket\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The difference between the investment rate on cash collateral and the rebate rate of a loan.\",\"enum\":[\"<25\",\"25-49\",\"50-199\",\"200-500\",\">500\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"coronavirusGlobalActivityTracker\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Value for the global activity tracker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"underlyers\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^(top_\\\\d{1,4}|all)$\",\"type\":\"string\",\"description\":\"Top underlyers. Supported values are \\\"all\\\" or \\\"top_10\\\", \\\"top_15\\\", etc..\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersPricingLocation\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The location in which the asset was priced.\",\"enum\":[\"NYC\",\"LDN\",\"TKO\",\"HKG\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"eventDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\t\\\\w&\\\\/()+%\\\\.:;,@!<>$* '\\\"\\\\n\\\\r-\\\\?-=#]{0,2048}$\",\"type\":\"string\",\"description\":\"Short description of the event, providing additional information beyond eventType.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"icebergMaxSize\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Iceberg max size in terms of the quantity unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetParametersCoupon\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The fixed coupon for this bond.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"details\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,2048}$\",\"type\":\"string\",\"description\":\"Corporate action details.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sector\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"The risk model sector of the stock.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"avgBedUtilRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average rate of bed utilization, computed as Total Patient Days (excluding nursery days) / Bed Days Available.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy20bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 20 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"epidemic\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of people affected by an epidemic.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"mctr\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Marginal contribution of a given asset to portfolio variance, is dependent on covariance matrix.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"exchangeTime\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"Local time at the executing venue.\",\"format\":\"date-time\"},{\"maxItems\":1,\"type\":\"array\"}]},\"historicalClose\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Historical Close Price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fipsCode\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"County FIPS code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy32bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 32 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ideaId\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{1,256}$\",\"type\":\"string\",\"description\":\"Marquee unique trade idea identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"commentStatus\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Corporate action comment status.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"marginalCost\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Marginal cost.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clientWeight\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Weight of client positions in the region or sector (%).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1DeliveryPoint\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Delivery point of leg.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell5cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 5 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liqWkly\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of assets that could be quickly and easily converted into investable cash without loss of value within a week.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unrealizedTwapPerformanceBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average execution price vs Consolidated TWAP since order inception ??? unrealized.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"region\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Regional classification for the asset\",\"enum\":[\"\",\"Americas\",\"Asia\",\"EM\",\"Europe\",\"Global\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"temperatureHour\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value of hour with min/max temperature in a day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"upperBound\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Upper bound value.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell55cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 55 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"numPediIcuBeds\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"All neonatal, pediatric and premature ICU beds.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The return an investor realizes on a bond sold at the bid price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"expectedResidual\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Expected residual quantity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionPremium\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"An indication of the market value of the option at the time of execution.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ownerName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Name of person submitting request.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"parAssetSwapSpread6m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Par asset swap spread vs 6m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"zScore\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Z Score.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell12bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 12 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"eventStartTime\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^([01]?[0-9]|2[0-3]):[0-5][0-9]$\",\"type\":\"string\",\"description\":\"The start time of the event if the event occurs during a time window and the event has a specific start time. It is represented in HH:MM 24 hour format in the time zone of the exchange where the company is listed.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"matchedMaturitySwapSpread6m\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Matched maturity swap spread vs 6m tenor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"turnover\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Turnover.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceSpotTargetUnit\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]{0,20}$\",\"type\":\"string\",\"description\":\"Unit in which the target price is reported.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"coverage\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w{}&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:;,!<>$* \\\\t'\\\"-=\\\\\\\\\\\\d\\\\r\\\\n]{0,8192}$\",\"type\":\"string\",\"description\":\"Coverage of dataset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A percentile that captures a company's G ranking relative to the entire ESG universe.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cloudCoverHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for cloud cover.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendingFundNav\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net Asset Value of a securities lending fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceOriginalCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source category's original name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentCloseExecutionQuantity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percentage of order filled at auction close.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"latestExecutionTime\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted timestamp\",\"format\":\"date-time\"},{\"maxItems\":1,\"type\":\"array\"}]},\"arrivalMidRealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Consolidated realised performance vs Arrival Mid, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"location\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^(NYC|LDN|HKG|SYD)$\",\"type\":\"string\",\"description\":\"The location at which a price fixing has been taken.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"scenarioId\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique scenario identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"terminationTenor\":{\"anyOf\":[{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},{\"maxItems\":1,\"type\":\"array\"}]},\"queueClockTime\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The Queue Clock Time of the stock on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"discretionLowerBound\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The lower bound of the discretion band as published from the algo.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tcmCostParticipationRate50Pct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TCM cost with a 50 percent participation rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ratingLinear\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Rating of the bond in linear form.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"previousCloseUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealised performance vs Previous Close, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"subAssetClassForOtherCommodity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the sub asset class.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"forwardPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Trader's estimate for the price of power in MWh.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"type\":{\"anyOf\":[{\"maxLength\":128,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Asset type differentiates the product categorization or contract type\",\"enum\":[\"Access\",\"Any\",\"AveragePriceOption\",\"Basis\",\"BasisSwap\",\"Benchmark\",\"Benchmark Rate\",\"Binary\",\"Bond\",\"BondFuture\",\"BondFutureOption\",\"BondOption\",\"Calendar Spread\",\"Cap\",\"Cash\",\"Certificate\",\"CD\",\"Cliquet\",\"CMSOption\",\"CMSOptionStrip\",\"CMSSpreadOption\",\"CMSSpreadOptionStrip\",\"Commodity\",\"CommodityReferencePrice\",\"CommodVarianceSwap\",\"CommodityPowerNode\",\"CommodityPowerAggregatedNodes\",\"CommodityNaturalGasHub\",\"Company\",\"Convertible\",\"Credit Basket\",\"Cross\",\"Crypto Currency\",\"CSL\",\"Currency\",\"Custom Basket\",\"Default Swap\",\"Economic\",\"Endowment\",\"Equity Basket\",\"ETF\",\"ETN\",\"Event\",\"FRA\",\"Fixing\",\"Floor\",\"Forward\",\"Fund\",\"Future\",\"FutureContract\",\"FutureMarket\",\"FutureOption\",\"FutureStrategy\",\"Hedge Fund\",\"Index\",\"InflationSwap\",\"Inter-Commodity Spread\",\"InvoiceSpread\",\"Market Location\",\"MLF\",\"Multi-Asset Allocation\",\"MultiCrossBinary\",\"MultiCrossBinaryLeg\",\"Mutual Fund\",\"Note\",\"Option\",\"OptionLeg\",\"OptionStrategy\",\"Pension Fund\",\"Preferred Stock\",\"Physical\",\"Precious Metal\",\"Precious Metal Swap\",\"Precious Metal RFQ\",\"Reference Entity\",\"Research Basket\",\"Rate\",\"Risk Premia\",\"Roll\",\"Securities Lending Loan\",\"Share Class\",\"Single Stock\",\"Swap\",\"SwapLeg\",\"SwapStrategy\",\"Swaption\",\"Synthetic\",\"Systematic Hedging\",\"VarianceSwap\",\"VolatilitySwap\",\"WeatherIndex\",\"XccySwap\",\"XccySwapFixFix\",\"XccySwapFixFlt\",\"XccySwapMTM\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"strikeRef\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Reference for strike level (enum: spot, forward,delta_call, delta_put, delta_neutral).\",\"enum\":[\"spot\",\"forward\",\"delta_call\",\"delta_put\",\"delta_neutral\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"cumulativePnl\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Cumulative PnL from the start date to the current date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shortTenor\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"Tenor of instrument.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell28bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 28 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fundClass\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,256}$\",\"type\":\"string\",\"description\":\"Class of the fund.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"unadjustedVolume\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted volume traded.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy36bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 36 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"positionIdx\":{\"anyOf\":[{\"type\":\"integer\",\"description\":\"The index of the corresponding position in the risk request.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"windChillHourlyForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The hourly forecast value for wind chill.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"secName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+\\\\[\\\\]%\\\\^\\\\?\\\\.#=:,!<>$* '\\\"-=]{0,31}$\",\"type\":\"string\",\"description\":\"Internal Goldman Sachs security name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"impliedVolatilityByRelativeStrike\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Volatility of an asset implied by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"percentADV\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Size of trade as percentage of average daily volume (e.g. .05, 1, 2, ..., 20).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg1TotalNotional\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The total Notional amount or quantity of units of the leg 1 underlying asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"contract\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Contract month code and year (e.g. F18).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paymentFrequency1\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An integer multiplier of a time period describing how often the parties to the SB swap transaction exchange payments associated with each party???s obligation. Such payment frequency may be described as one letter preceded by an integer.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"paymentFrequency2\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Same as Payment Frequency 1.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bespoke\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"Indication if the trade is bespoke.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"repoTenor\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w]{2,3}$\",\"type\":\"string\",\"description\":\"Maturity of repurchase agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell15cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 15 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"investmentQtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total net investment Quarter to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"heatIndexForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The heat index forecast in a given unit.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"ratingStandardAndPoors\":{\"anyOf\":[{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]*$\",\"type\":\"string\",\"description\":\"Bond rating from Standard And Poor's.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"qualityStars\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Confidence in the BPE.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2FloatingIndex\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"If floating index leg, the index.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceTicker\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source ticker.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryVwapUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealised performance vs Primary VWAP, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"gsid\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\d]{1,20}\",\"type\":\"string\",\"description\":\"Goldman Sachs internal equity identifier.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"lendingFund\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}\",\"type\":\"string\",\"description\":\"Name of the lending fund on a securities lending agreement.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sensitivity\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Sensitivity.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dayCount\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The determination of how interest accrues over time for the SB swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell16bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 16 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativeBreakEvenInflationChange\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Relative break even inflation change.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell25cents\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 25 cents charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"varSwap\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Strike such that the price of an uncapped variance swap on the underlying index is zero at inception.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy5point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 5.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"blockLargeNotional\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of whether this is a block trade or off-facility swap.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell2point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 2.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"capacity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Risk/agency execution.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sectorsRaw\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Sector classifications of an asset.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"primaryVwapInLimit\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Primary VWAP In Limit benchmark price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"shareclassPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price of the shareclass on a certain day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"tradeSize\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Size of trade ($mm).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceSpotEntryValue\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Opening price value of the trade idea.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy8point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 8.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"symbolDimensions\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\d]{0,64}$\",\"type\":\"string\",\"description\":\"Set of fields that determine database table name.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy24bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 24 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"observation\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[-\\\\w\\\\d\\\\. ]{1,128}$\",\"type\":\"string\",\"description\":\"Value of observation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"optionTypeSDR\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"An indication of the type of the option.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"scenarioGroupId\":{\"anyOf\":[{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\-]{0,256}$\",\"type\":\"string\",\"description\":\"Marquee unique scenario group identifier\"},{\"maxItems\":1,\"type\":\"array\"}]},\"averageImpliedVariance\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average variance of an asset implied by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"avgTradeRateDescription\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[~\\\\t\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,100}$\",\"type\":\"string\",\"description\":\"Description of the Stock's Average Trading Rate on the particular date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fraction\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Fraction.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"assetCountShort\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Number of assets in a portfolio with short exposure.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"collateralPercentageRequired\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Collateral percentage requied to cover the given position.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell5point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 5.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"date\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601 formatted date.\",\"format\":\"date\"},{\"maxItems\":1,\"type\":\"array\"}]},\"zipCode\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Postal code.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalStdReturnSinceInception\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average annual total returns as of most recent calendar quarter-end.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sourceCategory\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,256}$\",\"type\":\"string\",\"description\":\"Source category of event.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"volumeUnadjusted\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unadjusted volume traded.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"passiveRatio\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Ratio of passive ownership (including ETFs and mutual funds), i.e the proportion of a company's marketcap that is held by passive ETFs and mutual funds. Normalised between -3 (low level) and +3 (high level).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"priceToEarnings\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Price to earnings.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"orderDepth\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Order book depth level of the corresponding ask or bid.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"annYield3Month\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Calculates the total return for 3 months, representing past performance.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"netFlowStd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Net flow for the asset in standard deviations.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"encodedStats\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[A-Za-z0-9]{0,31}$\",\"type\":\"string\",\"description\":\"Asset stats object in json format.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"runTime\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Time that a run of the QA checker took.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"askSize\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The number of shares, lots, or contracts willing to sell at the Ask price.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"absoluteReturnMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Absolute Return Month to Date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"std30DaysUnsubsidizedYield\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average annual total returns as of most recent calendar quarter-end.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"resource\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The event resource. For example: Asset\",\"enum\":[\"Application\",\"Asset\",\"Backtest\",\"Basket\",\"Data\",\"DataSet\",\"Document\",\"Email\",\"Hedge\",\"Idea\",\"Order\",\"Portfolio\",\"Positions\",\"Report\",\"Risk\",\"RiskModel\",\"Scenario\",\"ScenarioGroup\",\"User\",\"PreTrade\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"averageRealizedVolatility\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Average volatility of an asset realized by observations of market prices.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"traceAdvBuy\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"TRACE ADV for the buy side.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"newConfirmed\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"New cofirmed cases.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell8bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 8 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"bidPrice\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Latest Bid Price (price willing to buy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"sell8point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would sell for 8.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"targetPriceUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealised performance vs Target Price, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"esNumericPercentile\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"A percentile that captures a company's E&S numeric ranking within its subsector.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"leg2UnderlyingAsset\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The asset, reference asset, or reference obligation for payments of a party???s obligations under the SB swap transaction reference.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"csaTerms\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w\\\\.\\\\- \\\\\\\\/]{0,256}$\",\"type\":\"string\",\"description\":\"Identifier of terms or rules under which collateral is posted or transferred between swap counterparties (e.g. ccy-1 means LCH clearing with collateral in currency ccy).\"},{\"maxItems\":1,\"type\":\"array\"}]},\"relativePayoffMtd\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total win divided by total loss Month to date.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"dailyNetShareholderFlows\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Cash dividends paid daily.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy2point5bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 2.5 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"cai\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Current Activity Indicator (CAI) and Current Activity Indicator Innovations (CAII) for each of the world's large economies and many smaller ones, as well as aggregate CAIs and CAIIs for regions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"executedNotionalUSD\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Executed notional in USD.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalHomeIsolation\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of cases in home isolation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stationName\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\x20-\\\\x7E]{0,256}$\",\"type\":\"string\",\"description\":\"The name of weather station where data is recorded/forecast.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"passPct\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Pass percentage.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"openingReport\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+\\\\[\\\\]`%\\\\.\\\\?@#:,!<>$* \\\\t'\\\"-=\\\\\\\\]{0,2048}$\",\"type\":\"string\",\"description\":\"Report that was published when the trade idea was opened.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"midcurveAtmFwdRate\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Midcurve ATM forward rate.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"precipitationForecast\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The forecast value for precipitation.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"equityRiskPremiumIndex\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Equity risk premium index: difference between cost of equity and 10y treasury yield.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"fatalitiesUnderlyingConditionsUnknown\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of fatalities of people that it is unknown whether they had underlying conditions.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"buy12bps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"The amount GS would buy for 12 bps charge.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"clearingHouse\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Swap Clearing House\",\"enum\":[\"LCH\",\"EUREX\",\"JSCC\",\"CME\",\"NONE\"]},{\"maxItems\":1,\"type\":\"array\"}]},\"dayCloseUnrealizedBps\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Unrealized performance vs Close, in bps.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"stsRatesMaturity\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:,!<>$* '\\\"-]{0,256}$\",\"type\":\"string\",\"description\":\"Risk maturity bucket for STS assets.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"liqDly\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Percent of assets that could be quickly and easily converted into investable cash without loss of value within a day.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"contributorRole\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\-\\\\s\\\\w:,\\\\\\\\/'&.)(*+!]{0,256}$\",\"type\":\"string\",\"description\":\"Role (specialist / generalist..) of a contributor.\"},{\"maxItems\":1,\"type\":\"array\"}]},\"totalFatalities\":{\"anyOf\":[{\"type\":\"number\",\"description\":\"Total number of fatalities.\"},{\"maxItems\":1,\"type\":\"array\"}]}},\"additionalProperties\":false},\"assetParameters\":{\"type\":\"object\",\"properties\":{\"internalIndexCalcRegion\":{\"maxLength\":256,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Classification for the asset based on index calculation region\",\"enum\":[\"HKG\",\"LDN\",\"NYC\"]},\"issueStatusDate\":{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},\"callable\":{\"type\":\"boolean\",\"description\":\"Bond is callable\"},\"amountOutstanding\":{\"type\":\"number\",\"description\":\"The aggregate principal amount of the total number of bonds not redeemed or otherwise discharged.\"},\"callFirstDate\":{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},\"putAmount\":{\"type\":\"number\"},\"perpetual\":{\"type\":\"boolean\",\"description\":\"Bond is a perpetual\"},\"issuer\":{\"maxLength\":100,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,100}$\",\"type\":\"string\",\"description\":\"The issuer of this bond\"},\"indexDivisor\":{\"type\":\"number\",\"description\":\"Divisor to be applied to the overall position set of the index\"},\"indexCalculationAgent\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Calculation agent of the index.\",\"enum\":[\"Solactive\",\"GS\"]},\"privatePlacementType\":{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:;,!$* '\\\"\\\\n\\\\r-]{0,50}$\",\"type\":\"string\",\"description\":\"Regulation that applies to a bond.\"},\"fixedRateDayCountFraction\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},\"puttable\":{\"type\":\"boolean\",\"description\":\"Bond is puttable\"},\"redemptionDate\":{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},\"deliveryPoint\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\"},\"index\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,50}$\",\"type\":\"string\",\"description\":\"The rate index (e.g. USD-LIBOR-BBA) for the floating rate coupon of this bond\"},\"minimumIncrement\":{\"maximum\":1000000000000,\"minimum\":0,\"type\":\"number\",\"description\":\"The minimum increment size of the bond purchase allowed above the minimum denomination as authorized by the bond documents\"},\"quoteCurrency\":{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},\"premiumCurrency\":{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},\"sinkable\":{\"type\":\"boolean\",\"description\":\"A bond that is protected by a fund (called a sinking fund) that sets aside money to ensure principal and interest payments are made by the issuer as promised.\"},\"style\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:;,!<>$* '\\\"\\\\n\\\\r-]{0,2048}$\",\"type\":\"string\",\"description\":\"Asset style\"},\"receiveFrequency\":{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},\"payDayCountFraction\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},\"loadType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\"},\"onBehalfOf\":{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^[0-9a-fA-F]{0,32}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},\"accruedInterestStandard\":{\"type\":\"number\",\"description\":\"The accrued interest paid on the bond if it is settled two business days after the trade date.\"},\"issueStatus\":{\"maxLength\":20,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()+%\\\\.:;,!$* '\\\"\\\\n\\\\r-]{0,50}$\",\"type\":\"string\",\"description\":\"Status of the issue.\"},\"coveredBond\":{\"type\":\"boolean\",\"description\":\"Whether the debt security is collateralized against a pool of assets that, in case of failure of the issuer, can cover claims at any point of time.\"},\"premium\":{\"type\":\"number\"},\"vendor\":{\"maxLength\":32,\"minLength\":0,\"pattern\":\"^[0-9a-fA-F]{0,32}$\",\"type\":\"string\",\"description\":\"Marquee unique identifier\"},\"currency\":{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},\"settlementType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"enum\":[\"Cash\",\"Physical\"]},\"coupon\":{\"type\":\"number\",\"description\":\"The fixed coupon for this bond\"},\"commoditySector\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The sector of the commodity\",\"enum\":[\"Base metals\",\"Precious metals\",\"Energy\",\"Agriculturals\",\"Power\"]},\"issuerCountryCode\":{\"maxLength\":2,\"minLength\":0,\"pattern\":\"^[A-Z]{2}$\",\"type\":\"string\",\"description\":\"The country code (ISO 3166) in which this bond was issued\"},\"resettableLeg\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Pay or receive fixed\",\"enum\":[\"Pay\",\"Receive\",\"Straddle\"]},\"inflationLag\":{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},\"basketType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Type of basket / implementation\",\"enum\":[\"Worst Of\",\"Share Weighted\",\"Generic\",\"Long Short\"]},\"issueSize\":{\"type\":\"number\",\"description\":\"The notional issue size of the bond\"},\"automaticExercise\":{\"type\":\"boolean\"},\"seniority\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,50}$\",\"type\":\"string\",\"description\":\"The seniority of the bond\"},\"indexTerm\":{\"maxLength\":4,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,4}$\",\"type\":\"string\",\"description\":\"The term of rate index (e.g. USD-LIBOR-BBA) for the floating rate coupon of this bond\"},\"isPairBasket\":{\"type\":\"boolean\"},\"putCurrency\":{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},\"defaultBackcast\":{\"type\":\"boolean\",\"description\":\"Is basket backcasted using initial positions.\"},\"optionType\":{\"maxLength\":11,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Option Type\",\"enum\":[\"Call\",\"Put\",\"Binary Call\",\"Binary Put\"]},\"isLegacyPairBasket\":{\"type\":\"boolean\"},\"indexApprovalIds\":{\"maxItems\":1000,\"type\":\"array\",\"description\":\"Array of approval identifiers related to the object\"},\"issuerType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The type of the bond issuer\",\"enum\":[\"Government\",\"Supranational\",\"Corporate\",\"Municipal\",\"Agency\",\"TRUST\",\"SUPR\",\"UNKN\",\"REIT\",\"PART\",\"AGCY\",\"SPAC\"]},\"expirationLocation\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"enum\":[\"LDN\",\"NYC\",\"HKG\",\"TKO\",\"SGP\"]},\"fxIndex\":{\"maxLength\":31,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,31}$\",\"type\":\"string\",\"description\":\"FX index\"},\"indexCalculationType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Determines the index calculation methodology with respect to dividend reinvestment\",\"enum\":[\"Gross Return\",\"IgnoredDivs Return\",\"Price Return\",\"Total Return\"]},\"receiveDayCountFraction\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},\"redemptionPrice\":{\"maximum\":1000000000000,\"minimum\":0,\"type\":\"number\",\"description\":\"The price for which the issuer will repurchase the security for at the redemption date.\"},\"multiplier\":{\"type\":\"number\",\"description\":\"Underlying unit per asset multiplier\"},\"attributionDatasetId\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Identifier of dataset which provides performance attribution data\",\"enum\":[\"STSATTR\"]},\"indexReturnType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Determines the return calculation type method with respect to cash accrual / funding\",\"enum\":[\"Excess Return\",\"Total Return\"]},\"supraStrategy\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Broad descriptor of a fund's investment approach. Same view permissions as the asset\",\"enum\":[\"Composite\",\"Credit\",\"Equity\",\"Equity Hedge\",\"Event Driven\",\"Fund of Funds\",\"Macro\",\"Multi-Strategy\",\"Other\",\"Quant\",\"Relative Value\",\"Risk Parity\"]},\"region\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\"},\"strikePrice\":{\"oneOf\":[{\"maximum\":1000000000000,\"minimum\":-1000000000000,\"type\":\"number\"},{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,100}$\",\"type\":\"string\"}]},\"issuePrice\":{\"maximum\":1000000000000,\"minimum\":0,\"type\":\"number\",\"description\":\"The price for which the instrument is issued\"},\"optionStyle\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"enum\":[\"European\",\"American\",\"Bermuda\"]},\"callAmount\":{\"type\":\"number\"},\"pricingIndex\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\"},\"sinkFactor\":{\"type\":\"number\",\"description\":\"The level to which a sinkable bond has currently sunk.\"},\"payFrequency\":{\"maxLength\":8,\"minLength\":0,\"pattern\":\"^[0-9]+[bdwmyBDWMY]$\",\"type\":\"string\",\"description\":\"Tenor\"},\"callCurrency\":{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},\"couponType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"The coupon type of the bond\",\"enum\":[\"Fixed\",\"Float\",\"Gains\",\"Hybrid\",\"Pay In Kind\",\"Stepup\",\"Zero\"]},\"tradeAs\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"How to trade the Option.\",\"enum\":[\"Listed\",\"Listed Look alike OTC\",\"Flex\",\"OTC\"]},\"contractUnit\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\"},\"indexInitialPrice\":{\"type\":\"number\",\"description\":\"Initial Price for the Index\"},\"productType\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Product type of basket\",\"enum\":[\"Flow\",\"MPS\"]},\"floatingRateDayCountFraction\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Day Count Fraction\",\"enum\":[\"ACT/360\",\"ACT/365 (Fixed)\",\"ACT/365 ISDA\",\"ACT/ACT ISDA\",\"30/360\",\"30E/360\"]},\"minimumDenomination\":{\"maximum\":1000000000000,\"minimum\":0,\"type\":\"number\",\"description\":\"The lowest denomination of an issue that can be purchased as authorized by the bond documents\"},\"callLastDate\":{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"},\"indexMargin\":{\"type\":\"number\",\"description\":\"The spread over the rate index (e.g. USD-LIBOR-BBA) for the floating rate coupon of this bond\"},\"contractMonths\":{\"maxItems\":1,\"type\":\"array\",\"description\":\"Contract months\",\"items\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[A-Z]{3}[0-9]{1,2}\",\"type\":\"string\"}},\"exerciseTime\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Time at which the asset can be exercised\",\"enum\":[\"MktOpen\",\"MktClose\",\"MktPreOpen\",\"MktPrevClose\",\"SQ\"]},\"ultimateTicker\":{\"maxLength\":12,\"minLength\":0,\"pattern\":\"^[\\\\w&\\\\/()@+%\\\\.#=:,!<>$* '\\\"-=]{0,12}$\",\"type\":\"string\",\"description\":\"The ultimate ticker for this security (e.g. SPXW)\"},\"exchangeCurrency\":{\"maxLength\":3,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Currency, ISO 4217 currency code or exchange quote modifier (e.g. GBP vs GBp)\",\"enum\":[\"\",\"ACU\",\"ADP\",\"AED\",\"AFA\",\"ALL\",\"AMD\",\"ANG\",\"AOA\",\"AOK\",\"AON\",\"ARA\",\"ARS\",\"ARZ\",\"ATS\",\"AUD\",\"AUZ\",\"AZM\",\"B03\",\"BAD\",\"BAK\",\"BAM\",\"BBD\",\"BDN\",\"BDT\",\"BEF\",\"BGL\",\"BGN\",\"BHD\",\"BIF\",\"BMD\",\"BND\",\"BR6\",\"BRE\",\"BRF\",\"BRL\",\"BRR\",\"BSD\",\"BTC\",\"BTN\",\"BTR\",\"BWP\",\"BYR\",\"BZD\",\"C23\",\"CAC\",\"CAD\",\"CAZ\",\"CCI\",\"CDF\",\"CFA\",\"CHF\",\"CHZ\",\"CLF\",\"CLP\",\"CLZ\",\"CNH\",\"CNO\",\"CNY\",\"CNZ\",\"COP\",\"COZ\",\"CPB\",\"CPI\",\"CRC\",\"CUP\",\"CVE\",\"CYP\",\"CZH\",\"CZK\",\"DAX\",\"DEM\",\"DIJ\",\"DJF\",\"DKK\",\"DOP\",\"DZD\",\"E51\",\"E52\",\"E53\",\"E54\",\"ECI\",\"ECS\",\"ECU\",\"EEK\",\"EF0\",\"EGP\",\"ESP\",\"ETB\",\"EUR\",\"EUZ\",\"F06\",\"FED\",\"FIM\",\"FJD\",\"FKP\",\"FRF\",\"FT1\",\"GBP\",\"GBZ\",\"GEK\",\"GHC\",\"GHS\",\"GHY\",\"GIP\",\"GMD\",\"GNF\",\"GQE\",\"GRD\",\"GTQ\",\"GWP\",\"GYD\",\"HKB\",\"HKD\",\"HNL\",\"HRK\",\"HSI\",\"HTG\",\"HUF\",\"IDB\",\"IDO\",\"IDR\",\"IEP\",\"IGP\",\"ILS\",\"INO\",\"INP\",\"INR\",\"IPA\",\"IPX\",\"IQD\",\"IRR\",\"IRS\",\"ISI\",\"ISK\",\"ISO\",\"ITL\",\"J05\",\"JMD\",\"JNI\",\"JOD\",\"JPY\",\"JPZ\",\"JZ9\",\"KES\",\"KGS\",\"KHR\",\"KMF\",\"KOR\",\"KPW\",\"KRW\",\"KWD\",\"KYD\",\"KZT\",\"LAK\",\"LBA\",\"LBP\",\"LHY\",\"LKR\",\"LRD\",\"LSL\",\"LSM\",\"LTL\",\"LUF\",\"LVL\",\"LYD\",\"MAD\",\"MDL\",\"MGF\",\"MKD\",\"MMK\",\"MNT\",\"MOP\",\"MRO\",\"MTP\",\"MUR\",\"MVR\",\"MWK\",\"MXB\",\"MXN\",\"MXP\",\"MXW\",\"MXZ\",\"MYO\",\"MYR\",\"MZM\",\"MZN\",\"NAD\",\"ND3\",\"NGF\",\"NGI\",\"NGN\",\"NIC\",\"NLG\",\"NOK\",\"NOZ\",\"NPR\",\"NZD\",\"NZZ\",\"O08\",\"OMR\",\"PAB\",\"PEI\",\"PEN\",\"PEZ\",\"PGK\",\"PHP\",\"PKR\",\"PLN\",\"PLZ\",\"PSI\",\"PTE\",\"PYG\",\"QAR\",\"R2K\",\"ROL\",\"RON\",\"RSD\",\"RUB\",\"RUF\",\"RUR\",\"RWF\",\"SAR\",\"SBD\",\"SCR\",\"SDP\",\"SDR\",\"SEK\",\"SET\",\"SGD\",\"SGS\",\"SHP\",\"SKK\",\"SLL\",\"SRG\",\"SSI\",\"STD\",\"SUR\",\"SVC\",\"SVT\",\"SYP\",\"SZL\",\"T21\",\"T51\",\"T52\",\"T53\",\"T54\",\"T55\",\"T71\",\"TE0\",\"TED\",\"TF9\",\"THB\",\"THO\",\"TMM\",\"TND\",\"TNT\",\"TOP\",\"TPE\",\"TPX\",\"TRB\",\"TRL\",\"TRY\",\"TRZ\",\"TTD\",\"TWD\",\"TZS\",\"UAH\",\"UCB\",\"UDI\",\"UFC\",\"UFZ\",\"UGS\",\"UGX\",\"USB\",\"USD\",\"UVR\",\"UYP\",\"UYU\",\"VAC\",\"VEB\",\"VEF\",\"VES\",\"VND\",\"VUV\",\"WST\",\"XAF\",\"XAG\",\"XAU\",\"XPD\",\"XPT\",\"XCD\",\"XDR\",\"XEU\",\"XOF\",\"XPF\",\"YDD\",\"YER\",\"YUD\",\"YUN\",\"ZAL\",\"ZAR\",\"ZAZ\",\"ZMK\",\"ZMW\",\"ZRN\",\"ZRZ\",\"ZWD\",\"AUd\",\"BWp\",\"EUr\",\"GBp\",\"ILs\",\"KWd\",\"MWk\",\"SGd\",\"SZl\",\"USd\",\"ZAr\"]},\"g10Currency\":{\"type\":\"boolean\",\"description\":\"Is a G10 asset.\"},\"contractMonth\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\"},\"strategy\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"More specific descriptor of a fund's investment approach. Same view permissions as the asset\",\"enum\":[\"Active Extension\",\"Active Trading\",\"Activist\",\"Co-Invest / SPV\",\"Commodity\",\"Commodities\",\"Composite\",\"Conservative\",\"Convert Arb\",\"Convertible Arbitrage\",\"Credit Arbitrage\",\"Cross-Capital-Structure\",\"CTA / Managed Futures\",\"Currency\",\"Discretionary\",\"Discretionary Thematic\",\"Distressed\",\"Distressed Securities\",\"Distressed/Restructuring\",\"Diversified\",\"Equity Hedge\",\"Equity Market Neutral\",\"Equity Only\",\"Event-Driven\",\"Fixed Income Arb\",\"Fixed Income-Asset Backed\",\"Fixed Income-Corporate\",\"Fixed Income-Sovereign\",\"Fundamental Growth\",\"Fundamental Value\",\"General\",\"General Multi-Strategy\",\"Generalist\",\"Hybrid / Illiquid\",\"Long / Short\",\"Macro\",\"Market Defensive\",\"Merger Arb\",\"Merger Arbitrage\",\"Multi-Strategy\",\"Quantitative Directional\",\"Relative Value Arbitrage\",\"Risk Premia\",\"Sector - Energy/Basic Materials\",\"Sector - Healthcare\",\"Sector - Technology\",\"Sector - Technology/Healthcare\",\"Sector-Specific\",\"Short Bias\",\"Special Situations\",\"Stat Arb\",\"Statistical Arbitrage\",\"Strategic\",\"Structured\",\"Systematic\",\"Systematic Diversified\",\"Vol Arb / Options\",\"Volatility\",\"Volatility Target 10\",\"Volatility Target 12\",\"Volatility Target 15\",\"Yield Alternative\"]},\"constituents\":{\"maxItems\":50,\"type\":\"array\",\"description\":\"Target basket constituents, e.g. ids, weights\"}},\"additionalProperties\":false,\"description\":\"Parameters specific to the asset type\"},\"fields\":{\"maxItems\":1,\"type\":\"array\",\"items\":{\"anyOf\":[{\"maxLength\":50,\"minLength\":0,\"pattern\":\"[\\\\w(),]{1,80}\",\"type\":\"string\"},{\"required\":[\"field\"],\"type\":\"object\",\"properties\":{\"field\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"^[a-zA-Z0-9]*$\",\"type\":\"string\",\"description\":\"Field to be returned\",\"enum\":[\"investmentRate\",\"startingEmmaLegalEntityId\",\"mdapiClass\",\"bidUnadjusted\",\"aggressiveFillsPercentage\",\"vehicleType\",\"totalFatalitiesByState\",\"newActive\",\"dailyRisk\",\"energy\",\"sunshineDailyForecast\",\"sentimentScore\",\"0\",\"1\",\"2\",\"3\",\"correlation\",\"exposure\",\"size\",\"4\",\"5\",\"6\",\"7\",\"marketDataAsset\",\"8\",\"9\",\"buy75cents\",\"unadjustedHigh\",\"sourceImportance\",\"closingYield\",\"wind\",\"sc16\",\"sc15\",\"sc12\",\"sc11\",\"primaryVwapInLimitUnrealizedBps\",\"displayName\",\"minutesToTrade100Pct\",\"sc14\",\"cumulativeVolumeInShares\",\"sc13\",\"newFatalities\",\"buy50bps\",\"numStaffedBeds\",\"upfrontPayment\",\"arrivalMidRealizedCash\",\"sc10\",\"sc05\",\"a\",\"sc04\",\"b\",\"sc07\",\"c\",\"yieldToMaturity\",\"sc06\",\"address\",\"sc01\",\"leg2PaymentFrequency\",\"sc03\",\"sc02\",\"geographyName\",\"borrower\",\"settlePrice\",\"performanceContribution\",\"sc09\",\"mktClass\",\"sc08\",\"collateralization\",\"futureMonthU26\",\"futureMonthU25\",\"futureMonthU24\",\"futureMonthU23\",\"futureMonthU22\",\"statementId\",\"futureMonthU21\",\"modifiedDuration\",\"shortRatesContribution\",\"impliedNormalVolatility\",\"solarGeneration\",\"mtmPrice\",\"swapSpreadChange\",\"realizedArrivalPerformanceUSD\",\"portfolioAssets\",\"pricingdate\",\"tcmCostHorizon3Hour\",\"exchangeRate\",\"potentialBedCapInc\",\"numberCovered\",\"numberOfPositions\",\"openUnadjusted\",\"strikeTime\",\"askPrice\",\"eventId\",\"sectors\",\"additionalPriceNotationType\",\"grossInvestmentQtd\",\"annualizedRisk\",\"estimatedHoldingTimeShort\",\"midcurvePremium\",\"volumeComposite\",\"sharpeQtd\",\"estimatedHoldingTimeLong\",\"external\",\"trackerName\",\"sell50cents\",\"tradePrice\",\"cleared\",\"primeIdNumeric\",\"buy8bps\",\"cid\",\"totalConfirmedSeniorHome\",\"ctdFwdPrice\",\"sinkFactor\",\"temperatureForecast\",\"bidHigh\",\"pnlQtd\",\"buy50cents\",\"sell4bps\",\"receiverDayCountFraction\",\"auctionClosePercentage\",\"targetPrice\",\"bosInBpsDescription\",\"lowPrice\",\"adv22DayPct\",\"matchedMaturitySwapSpread12m\",\"priceRangeInTicksLabel\",\"ticker\",\"notionalUnit\",\"tcmCostHorizon1Day\",\"approval\",\"testMeasure\",\"optionLockOutPeriod\",\"sourceValueForecast\",\"leg2Spread\",\"shortConvictionLarge\",\"ccgName\",\"dollarExcessReturn\",\"gsn\",\"tradeEndDate\",\"receiverRateOption\",\"gss\",\"percentOfMediandv1m\",\"lendables\",\"sell75cents\",\"optionAdjustedSpread\",\"optionAdjustedSwapSpread\",\"bosInTicksLabel\",\"positionSourceId\",\"buy1bps\",\"buy3point5bps\",\"gsSustainRegion\",\"absoluteReturnWtd\",\"deploymentId\",\"assetParametersSeniority\",\"askSpread\",\"flow\",\"futureMonthH26\",\"loanRebate\",\"futureMonthH25\",\"period\",\"indexCreateSource\",\"futureMonthH24\",\"futureMonthH23\",\"futureMonthH22\",\"futureMonthH21\",\"nonUsdOis\",\"realTWIContribution\",\"mktAsset\",\"leg2IndexLocation\",\"twapUnrealizedBps\",\"lastUpdatedMessage\",\"loanValue\",\"optionAdjustedOISSpread\",\"totalReturnPrice\",\"weightedPercentInModel\",\"initLoanSpreadRequired\",\"electionPeriod\",\"fundingAskPrice\",\"historicalBeta\",\"bondRiskPremiumIndex\",\"hitRateYtd\",\"girGsdeerGsfeer\",\"numUnits\",\"assetParametersReceiverFrequency\",\"expenseRatioGrossBps\",\"relativePayoffWtd\",\"ctdPrice\",\"paceOfRollNow\",\"product\",\"leg2ReturnType\",\"agentLenderFee\",\"disseminationId\",\"optionStrikePrice\",\"precipitationType\",\"lowerBound\",\"arrivalMidNormalized\",\"underlyingAsset2\",\"underlyingAsset1\",\"legalEntity\",\"performanceFee\",\"orderState\",\"actualDataQuality\",\"indexRatio\",\"queueInLotsLabel\",\"adv10DayPct\",\"longConvictionMedium\",\"relativeHitRateWtd\",\"dailyTrackingError\",\"sell140cents\",\"sell10bps\",\"aggressiveOffsetFromLast\",\"longitude\",\"newIcu\",\"marketCap\",\"weightedAverageMid\",\"clusterRegion\",\"valoren\",\"averageExecutionPrice\",\"proceedsAssetOISSwapSpread1m\",\"payoffWtd\",\"basis\",\"investmentRateTrend\",\"grossInvestmentMtd\",\"200\",\"hedgeId\",\"201\",\"sharpeMtd\",\"202\",\"203\",\"tcmCostHorizon8Day\",\"204\",\"residualVariance\",\"205\",\"restrictInternalDerivedData\",\"206\",\"207\",\"208\",\"adv5DayPct\",\"209\",\"midpointFillsPercentage\",\"openInterest\",\"turnoverCompositeUnadjusted\",\"fwdPoints\",\"relativeReturnWtd\",\"units\",\"payerRateOption\",\"assetClassificationsRiskCountryName\",\"extMktPoint3\",\"210\",\"211\",\"matchedMaturitySwapSpread\",\"212\",\"cityName\",\"213\",\"hourlyBucket\",\"214\",\"averageImpliedVolatility\",\"totalHospitalizedWithSymptoms\",\"215\",\"216\",\"217\",\"daysOpenRealizedCash\",\"218\",\"219\",\"adjustedHighPrice\",\"proceedsAssetOISSwapSpread\",\"extMktPoint1\",\"direction\",\"extMktPoint2\",\"subRegionCode\",\"assetParametersFixedRate\",\"isEstimatedReturn\",\"valueForecast\",\"totalIcu\",\"positionSourceType\",\"previousCloseUnrealizedCash\",\"minimumDenomination\",\"futureValueNotional\",\"participationRate\",\"obfr\",\"220\",\"221\",\"222\",\"buy9point5bps\",\"223\",\"224\",\"225\",\"optionLockPeriod\",\"226\",\"esMomentumPercentile\",\"227\",\"228\",\"advPercentage\",\"229\",\"leg1AveragingMethod\",\"turnoverComposite\",\"forecastDate\",\"internalIndexCalcRegion\",\"positionType\",\"subAssetClass\",\"shortInterest\",\"referencePeriod\",\"adjustedVolume\",\"ctdFwdYield\",\"secDB\",\"memoryUsed\",\"bpeQualityStars\",\"230\",\"231\",\"232\",\"ctd\",\"233\",\"234\",\"235\",\"236\",\"237\",\"238\",\"239\",\"intendedParticipationRate\",\"leg1PaymentType\",\"tradingPnl\",\"collateralValueRequired\",\"buy45bps\",\"priceToEarningsPositive\",\"forecast\",\"forecastValue\",\"240\",\"pnl\",\"241\",\"242\",\"243\",\"volumeInLimit\",\"244\",\"isTerritory\",\"245\",\"leg2DeliveryPoint\",\"246\",\"247\",\"248\",\"249\",\"tcmCostHorizon4Day\",\"styles\",\"shortName\",\"resetFrequency1\",\"buy4bps\",\"resetFrequency2\",\"otherPriceTerm\",\"bidGspread\",\"openPrice\",\"psId\",\"hitRateMtd\",\"fairVolatility\",\"dollarCross\",\"portfolioType\",\"currency\",\"clusterClass\",\"sell50bps\",\"futureMonthM21\",\"bidSize\",\"arrivalMid\",\"assetParametersExchangeCurrency\",\"candidateName\",\"impliedLognormalVolatility\",\"vwapInLimitUnrealizedCash\",\"ratingMoodys\",\"futureMonthM26\",\"futureMonthM25\",\"futureMonthM24\",\"futureMonthM23\",\"futureMonthM22\",\"flowPct\",\"source\",\"assetClassificationsCountryCode\",\"settleDrop\",\"dataSetSubCategory\",\"sell9point5bps\",\"quantityBucket\",\"optionStyleSDR\",\"oeName\",\"given\",\"leg2DayCountConvention\",\"liquidityScoreSell\",\"delistingDate\",\"weight\",\"accruedInterest\",\"businessScope\",\"wtdDegreeDays\",\"absoluteWeight\",\"measure\",\"temperatureHourlyForecast\",\"icebergTipRateType\",\"sharpeYtd\",\"windSpeedForecast\",\"grossInvestmentYtd\",\"yieldPrice\",\"leg1TotalNotionalUnit\",\"issuePrice\",\"askHigh\",\"expectedDataQuality\",\"regionName\",\"valueRevised\",\"discretionUpperBound\",\"adjustedTradePrice\",\"forecastTime\",\"isoSubdivisionCodeAlpha2\",\"ctdConversionFactor\",\"proceedsAssetSwapSpread\",\"isADR\",\"issueDate\",\"serviceId\",\"yes\",\"gScore\",\"marketValue\",\"entityId\",\"notionalCurrency1\",\"netDebtToEbitda\",\"numUnitsUpper\",\"notionalCurrency2\",\"inLimitParticipationRate\",\"pressureForecast\",\"paid\",\"fixedRate\",\"short\",\"time\",\"buy4point5bps\",\"sell30cents\",\"eventEndDateTime\",\"leg1PaymentFrequency\",\"cmId\",\"taxonomy\",\"buy45cents\",\"measures\",\"seasonalAdjustment\",\"rankWtd\",\"underlyer\",\"createdTime\",\"identifier\",\"priceUnit\",\"tradeReportRefId\",\"subdivisionId\",\"unadjustedLow\",\"buy160cents\",\"portfolioId\",\"zSpread\",\"capFloorAtmFwdRate\",\"esPercentile\",\"tdapi\",\"locationCode\",\"rcic\",\"nameRaw\",\"simonAssetTags\",\"hitRateQtd\",\"primaryVolumeInLimit\",\"precipitationDailyForecastPercent\",\"aumEnd\",\"premium\",\"low\",\"crossGroup\",\"reportRunTime\",\"fiveDayPriceChangeBps\",\"holdings\",\"precipitationDailyForecast\",\"priceMethod\",\"assetParametersFixedRateFrequency\",\"oisXccy\",\"daysOpen\",\"buy110cents\",\"averageSpreadBps\",\"buy55cents\",\"futureMonthQ26\",\"issueSize\",\"futureMonthQ25\",\"futureMonthQ24\",\"futureMonthQ23\",\"futureMonthQ22\",\"pendingLoanCount\",\"futureMonthQ21\",\"priceSpotStopLossUnit\",\"priceRangeInTicksDescription\",\"tradeVolume\",\"primaryCountryRic\",\"optionExpirationFrequency\",\"isActive\",\"useMachineLearning\",\"growthScore\",\"bufferThreshold\",\"buy120cents\",\"matchedMaturitySwapRate\",\"primaryVwap\",\"exchangeTypeId\",\"basisSwapRate\",\"exchangeCode\",\"group\",\"assetParametersTerminationDate\",\"estimatedSpread\",\"yieldChangeOnDay\",\"created\",\"autoTags\",\"tcmCost\",\"sustainJapan\",\"historyStartDate\",\"bidSpread\",\"hedgeTrackingError\",\"windSpeedType\",\"strikePrice\",\"parAssetSwapSpread12m\",\"tradeReportId\",\"adjustedOpenPrice\",\"countryId\",\"point\",\"pnlMtd\",\"totalReturns\",\"lender\",\"annReturn1Year\",\"ctdFwdDv01\",\"effYield7Day\",\"meetingDate\",\"calendarSpreadMispricing\",\"buy140cents\",\"priceNotation2Type\",\"fundFocus\",\"relativeStrike\",\"flagship\",\"additionalPriceNotation\",\"factorCategory\",\"equityDelta\",\"grossWeight\",\"listed\",\"sell7bps\",\"earningsRecordType\",\"mean\",\"askYield\",\"shockStyle\",\"methodology\",\"buy25cents\",\"amountOutstanding\",\"marketPnl\",\"sustainAsiaExJapan\",\"sell6point5bps\",\"neighbourAssetId\",\"countIdeasYtd\",\"simonIntlAssetTags\",\"path\",\"vwapUnrealizedCash\",\"payoffMtd\",\"bosInBpsLabel\",\"bosInBps\",\"pointClass\",\"fxSpot\",\"restrictNamedIndividuals\",\"hedgeVolatility\",\"tags\",\"population\",\"underlyingAssetId\",\"realLongRatesContribution\",\"pctprices_return\",\"domain\",\"buy80cents\",\"forwardTenor\",\"averagePrice\",\"targetPriceRealizedBps\",\"leg2FixedRate\",\"shareClassAssets\",\"annuity\",\"totalCount\",\"quoteType\",\"corporateActionStatus\",\"peggedTipSize\",\"uid\",\"esPolicyPercentile\",\"usdOis\",\"term\",\"restrictInternalGsNtk\",\"tcmCostParticipationRate100Pct\",\"relativeUniverse\",\"measureIdx\",\"fredId\",\"twiContribution\",\"cloudCoverType\",\"delisted\",\"regionalFocus\",\"volumePrimary\",\"assetParametersPayerDesignatedMaturity\",\"buy30cents\",\"fundingBidPrice\",\"series\",\"sell3bps\",\"settlementPrice\",\"quarter\",\"sell18bps\",\"assetParametersFloatingRateOption\",\"realizedVwapPerformanceBps\",\"voteShare\",\"servicingCostShortPnl\",\"totalConfirmed\",\"economicForecast\",\"plotId\",\"clusterDescription\",\"concentrationLimit\",\"windSpeed\",\"observationHour\",\"signal\",\"borrowerId\",\"dataProduct\",\"buy7point5bps\",\"limitPrice\",\"bmPrimeId\",\"dataType\",\"count\",\"conviction\",\"rfqstate\",\"benchmarkMaturity\",\"grossFlowNormalized\",\"buy14bps\",\"factorId\",\"futureMonthV26\",\"stsFxCurrency\",\"futureMonthV25\",\"bidChange\",\"month\",\"futureMonthV24\",\"investmentWtd\",\"futureMonthV23\",\"futureMonthV22\",\"futureMonthV21\",\"expiration\",\"leg2ResetFrequency\",\"controversyScore\",\"proceedAssetSwapSpread\",\"concentrationLevel\",\"importance\",\"assetClassificationsGicsSector\",\"stsAssetName\",\"netExposureClassification\",\"settlementMethod\",\"receiverDesignatedMaturity\",\"title\",\"xRefTypeId\",\"duration\",\"load\",\"alpha\",\"company\",\"settlementFrequency\",\"distAvg7Day\",\"inRiskModel\",\"dailyNetShareholderFlowsPercent\",\"filledNotionalLocal\",\"everHospitalized\",\"meetingNumber\",\"midGspread\",\"daysOpenUnrealizedBps\",\"longLevel\",\"dataDescription\",\"temperatureType\",\"gsideid\",\"repoRate\",\"division\",\"cloudCoverDailyForecast\",\"windSpeedDailyForecast\",\"assetParametersFloatingRateDayCountFraction\",\"tradeAction\",\"action\",\"ctdYield\",\"arrivalHaircutVwapNormalized\",\"priceComponent\",\"queueClockTimeDescription\",\"assetParametersReceiverDayCountFraction\",\"percentMidExecutionQuantity\",\"deltaStrike\",\"cloudCover\",\"assetParametersNotionalCurrency\",\"buy18bps\",\"valueActual\",\"upi\",\"collateralCurrency\",\"originalCountry\",\"field\",\"geographicFocus\",\"daysOpenRealizedBps\",\"fxRiskPremiumIndex\",\"skew\",\"status\",\"notionalCurrency\",\"sustainEmergingMarkets\",\"eventDateTime\",\"leg1DesignatedMaturity\",\"totalPrice\",\"onBehalfOf\",\"testType\",\"accruedInterestStandard\",\"futureMonthZ26\",\"futureMonthZ25\",\"ccgCode\",\"shortExposure\",\"leg1FixedPaymentCurrency\",\"arrivalHaircutVwap\",\"executionDays\",\"recallDueDate\",\"forward\",\"strike\",\"spreadLimit\",\"productScope\",\"assetParametersIssuerType\",\"currency1\",\"currency2\",\"previousCloseRealizedBps\",\"daysSinceReported\",\"eventStatus\",\"vwapInLimit\",\"fwdDuration\",\"return\",\"isPairBasket\",\"notionalAmount\",\"payOrReceive\",\"totalSevere\",\"unexecutedNotionalUSD\",\"expectedResidualPercentage\",\"maturityDate\",\"traceAdvSell\",\"eventName\",\"addressLine2\",\"indicationOfOtherPriceAffectingTerm\",\"unadjustedBid\",\"backtestType\",\"gsdeer\",\"assetParametersIssuer\",\"gRegionalPercentile\",\"coverageChecked\",\"oisXccyExSpike\",\"totalRisk\",\"mnav\",\"marketVolume\",\"swapAnnuity\",\"parAssetSwapSpread\",\"currYield7Day\",\"pressure\",\"shortDescription\",\"futureMonthZ24\",\"feed\",\"futureMonthZ23\",\"mktPoint1\",\"futureMonthZ22\",\"futureMonthZ21\",\"futureMonthZ20\",\"assetParametersCommoditySector\",\"priceNotation2\",\"marketBufferThreshold\",\"priceNotation3\",\"mktPoint3\",\"mktPoint2\",\"leg2Type\",\"mktPoint4\",\"degreeDaysType\",\"investmentIncome\",\"groupType\",\"forwardPointImm\",\"twap\",\"clientShortName\",\"groupCategory\",\"bidPlusAsk\",\"foreignCcyRate\",\"electionOdds\",\"windDirectionForecast\",\"requireAnonClientName\",\"pricingLocation\",\"beta\",\"lastReturnsEndDate\",\"upfrontPaymentDate\",\"sell1point5bps\",\"longExposure\",\"sell4point5bps\",\"tcmCostParticipationRate20Pct\",\"venueType\",\"multiAssetClassSwap\",\"deltaChangeId\",\"implementationId\",\"leg1FixedPayment\",\"esNumericScore\",\"inBenchmark\",\"actionSDR\",\"countIdeasQtd\",\"knockOutPrice\",\"ctdAssetId\",\"buy10bps\",\"precipitation\",\"valueType\",\"betaAdjustedNetExposure\",\"estimatedRodVolume\",\"sell14bps\",\"10\",\"11\",\"12\",\"13\",\"excessReturnPrice\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\"fxPnl\",\"assetClassificationsGicsIndustryGroup\",\"lendingSecId\",\"dollarDuration\",\"equityTheta\",\"dv01\",\"startDate\",\"20\",\"21\",\"22\",\"23\",\"mixedSwap\",\"swaptionPremium\",\"24\",\"25\",\"26\",\"snowfall\",\"liquidityBucketBuy\",\"27\",\"mic\",\"28\",\"latitude\",\"29\",\"mid\",\"impliedRepo\",\"long\",\"firstExecutionTime\",\"coveredBond\",\"regionCode\",\"buy20cents\",\"longWeight\",\"calculationTime\",\"liquidityBucketSell\",\"daysOpenUnrealizedCash\",\"temperature\",\"averageRealizedVariance\",\"ratingFitch\",\"financialReturnsScore\",\"yearOrQuarter\",\"30\",\"31\",\"32\",\"nonSymbolDimensions\",\"33\",\"commoditiesForecast\",\"34\",\"35\",\"covid19ByState\",\"36\",\"37\",\"38\",\"39\",\"percentageExpectedResidual\",\"hospitalName\",\"buy90cents\",\"periodType\",\"assetClassificationsCountryName\",\"totalHospitalized\",\"peggedRefillInterval\",\"fatalitiesProbable\",\"40\",\"administrativeRegion\",\"41\",\"open\",\"42\",\"43\",\"44\",\"45\",\"cusip\",\"totalConfirmedByState\",\"46\",\"ideaActivityTime\",\"47\",\"48\",\"49\",\"windAttribute\",\"spreadOptionAtmFwdRate\",\"netExposure\",\"isLegacyPairBasket\",\"issuerType\",\"buy70cents\",\"strikeReference\",\"assetCount\",\"50\",\"51\",\"isOrderInLimit\",\"52\",\"53\",\"54\",\"fundamentalMetric\",\"55\",\"56\",\"quoteStatusId\",\"57\",\"absoluteValue\",\"closingReport\",\"58\",\"previousTotalConfirmed\",\"59\",\"longTenor\",\"multiplier\",\"buy40cents\",\"assetCountPriced\",\"voteDirection\",\"impliedRepoRate\",\"settlementCurrency\",\"wtdDegreeDaysForecast\",\"indicationOfCollateralization\",\"futureMonthN26\",\"60\",\"lendingPartnerFee\",\"futureMonthN25\",\"61\",\"futureMonthN24\",\"62\",\"primaryVwapRealizedBps\",\"futureMonthN23\",\"63\",\"futureMonthN22\",\"64\",\"futureMonthN21\",\"65\",\"66\",\"67\",\"68\",\"69\",\"breakEvenInflation\",\"pnlYtd\",\"leg1ReturnType\",\"tenor2\",\"resetFrequency\",\"assetParametersPayerFrequency\",\"degreeDaysForecast\",\"isManuallySilenced\",\"buy3bps\",\"lastUpdatedById\",\"legalEntityAcct\",\"targetShareholderMeetingDate\",\"70\",\"71\",\"72\",\"paceOfRollp0\",\"73\",\"74\",\"controversyPercentile\",\"leg1NotionalCurrency\",\"75\",\"complianceEffectiveTime\",\"expirationDate\",\"76\",\"77\",\"78\",\"79\",\"floatingRateDayCountFraction\",\"callLastDate\",\"factorReturn\",\"passiveFlowRatio\",\"composite5DayAdv\",\"marginalContributionToRisk\",\"closeDate\",\"temperatureHourForecast\",\"newIdeasWtd\",\"assetClassSDR\",\"yieldToWorst\",\"80\",\"closingPrice\",\"81\",\"turnoverCompositeAdjusted\",\"comment\",\"sourceSymbol\",\"82\",\"83\",\"84\",\"askUnadjusted\",\"85\",\"86\",\"restrictExternalDerivedData\",\"87\",\"88\",\"89\",\"askChange\",\"countIdeasMtd\",\"endDate\",\"sunshine\",\"contractType\",\"momentumType\",\"specificRisk\",\"mdapi\",\"payoffQtd\",\"loss\",\"midcurveVol\",\"sell6bps\",\"tradingCostPnl\",\"priceNotationType\",\"price\",\"paymentQuantity\",\"90\",\"91\",\"92\",\"93\",\"94\",\"95\",\"96\",\"97\",\"98\",\"redemptionDate\",\"99\",\"leg2NotionalCurrency\",\"subRegion\",\"benchmark\",\"tcmCostParticipationRate15Pct\",\"fiscalYear\",\"recallDate\",\"esgMetricValue\",\"internal\",\"gender\",\"assetClassificationsGicsIndustry\",\"adjustedBidPrice\",\"lowUnadjusted\",\"MACSSecondaryAssetClass\",\"confirmedPerMillion\",\"dataSourceId\",\"integratedScore\",\"buy7bps\",\"arrivalMidUnrealizedCash\",\"knockInPrice\",\"event\",\"isIntradayAuction\",\"locationName\",\"coupon\",\"percentageAuctionExecutedQuantity\",\"avgYield7Day\",\"originalDisseminationId\",\"totalOnVent\",\"twapUnrealizedCash\",\"stsCreditMarket\",\"onsCode\",\"passiveTouchFillsPercentage\",\"seniority\",\"leg1Index\",\"highUnadjusted\",\"submissionEvent\",\"TVProductMnemonic\",\"avgTradeRateLabel\",\"lastActivityDate\",\"disseminationTime\",\"priceToCash\",\"buy10cents\",\"navSpread\",\"venueMIC\",\"dollarTotalReturn\",\"blockUnit\",\"midSpread\",\"istatProvinceCode\",\"totalRecoveredByState\",\"repurchaseRate\",\"dataSource\",\"totalBeingTested\",\"clearedOrBilateral\",\"metricName\",\"askGspread\",\"forecastHour\",\"leg2PaymentType\",\"calSpreadMisPricing\",\"totalTestedNegative\",\"rate366\",\"platform\",\"rate365\",\"fixedRateFrequency\",\"rate360\",\"isContinuous\",\"value\",\"payerDesignatedMaturity\",\"productType\",\"mdv22Day\",\"twapRealizedBps\",\"testMeasureLabel\",\"quantity\",\"reportId\",\"indexWeight\",\"MACSPrimaryAssetClass\",\"trader\",\"leg2PriceType\",\"totalActive\",\"gsid2\",\"matchedMaturityOISSwapSpread\",\"valuationDate\",\"restrictGsFederation\",\"positionSource\",\"tcmCostHorizon6Hour\",\"buy200cents\",\"vwapUnrealizedBps\",\"priceToBook\",\"isin\",\"plId\",\"lastReturnsStartDate\",\"collateralValueVariance\",\"year\",\"forecastPeriod\",\"callFirstDate\",\"dataSetIds\",\"economicTermsHash\",\"numBeds\",\"sell20bps\",\"clientType\",\"percentageCloseExecutedQuantity\",\"macaulayDuration\",\"availableInventory\",\"est1DayCompletePct\",\"relativeHitRateYtd\",\"createdById\",\"marketDataType\",\"realShortRatesContribution\",\"metricCategory\",\"annualizedCarry\",\"valuePrevious\",\"transmissionClassification\",\"avgTradeRate\",\"shortLevel\",\"version\",\"categoryType\",\"policyRateExpectation\",\"uploadDate\",\"blockOffFacility\",\"unrealizedVwapPerformanceUSD\",\"paceOfRollp75\",\"earningsPerSharePositive\",\"numIcuBeds\",\"bucketVolumeInPercentage\",\"estimatedTradingCost\",\"eid\",\"relativeReturnQtd\",\"assessedTestMeasure\",\"mktQuotingStyle\",\"expirationTenor\",\"priceLimit\",\"marketModelId\",\"receiverFrequency\",\"realizedCorrelation\",\"issueStatus\",\"collateralValueActual\",\"atmFwdRate\",\"tcmCostParticipationRate75Pct\",\"close\",\"esProductImpactScore\",\"equityVega\",\"executedFillQuantity\",\"lenderPayment\",\"fiveDayMove\",\"valueFormat\",\"windChillForecast\",\"targetNotional\",\"fillLegId\",\"rationale\",\"realizedTwapPerformanceBps\",\"lastUpdatedSince\",\"totalTests\",\"equitiesContribution\",\"simonId\",\"congestion\",\"notes\",\"totalProbableSeniorHome\",\"eventCategory\",\"averageFillRate\",\"unadjustedOpen\",\"criticality\",\"bidAskSpread\",\"arrivalMidUnrealizedBps\",\"optionType\",\"terminationDate\",\"queriesPerSecond\",\"liquidityType\",\"creditLimit\",\"rankQtd\",\"combinedKey\",\"girFxForecast\",\"effectiveTenor\",\"girCommoditiesForecast\",\"relativeHumidityDailyForecast\",\"std30DaysSubsidizedYield\",\"annualizedTrackingError\",\"futureMonthF26\",\"futureMonthF25\",\"volSwap\",\"futureMonthF24\",\"heatIndexDailyForecast\",\"futureMonthF23\",\"realFCI\",\"blockTradesAndLargeNotionalOffFacilitySwaps\",\"futureMonthF22\",\"buy1point5bps\",\"futureMonthF21\",\"expirationSettlementDate\",\"absoluteReturnQtd\",\"grossExposure\",\"volume\",\"adv\",\"shortConvictionMedium\",\"completeTestMeasure\",\"exchange\",\"esPolicyScore\",\"rollVolumeStd\",\"temperatureDailyForecast\",\"relativePayoffQtd\",\"onLoanPercentage\",\"twapRemainingSlices\",\"fairVariance\",\"hitRateWtd\",\"previousCloseRealizedCash\",\"realizedVolatility\",\"unexecutedQuantity\",\"proceedsAssetSwapSpread1m\",\"cloneParentId\",\"windSpeedHourlyForecast\",\"etfFlowRatio\",\"assetParametersReceiverRateOption\",\"buy60cents\",\"securitySubTypeId\",\"message\",\"stsRatesCountry\",\"sell65cents\",\"horizon\",\"wouldIfGoodLevel\",\"bufferThresholdRequired\",\"faceValue\",\"rollVolumeHist\",\"counterPartyStatus\",\"composite22DayAdv\",\"percentageFarExecutedQuantity\",\"loanSpreadRequired\",\"assetClass\",\"sovereignSpreadContribution\",\"ric\",\"bucketEndTime\",\"rateType\",\"totalFatalitiesSeniorHome\",\"loanStatus\",\"shortWeight\",\"geographyId\",\"sell7point5bps\",\"nav\",\"fiscalQuarter\",\"versionString\",\"payoffYtd\",\"marketImpact\",\"eventType\",\"assetCountLong\",\"sell180cents\",\"spot\",\"applicationId\",\"indicativeClosePrice\",\"swapSpread\",\"tradingRestriction\",\"assetParametersPayOrReceive\",\"priceSpotEntryUnit\",\"unrealizedArrivalPerformanceBps\",\"city\",\"pnlWtd\",\"covariance\",\"bucketVolumeInShares\",\"commodityForecast\",\"valid\",\"stsCommodity\",\"initialPricingDate\",\"indicationOfEndUserException\",\"windDirectionHourlyForecast\",\"esScore\",\"yield\",\"fatalitiesUnderlyingConditionsPresent\",\"priceRangeInTicks\",\"paceOfRollp25\",\"dayCloseRealizedUSD\",\"pctChange\",\"brightnessType\",\"futureMonth3M\",\"numberOfRolls\",\"isoCountryCodeNumeric\",\"priceType\",\"realizedVwapPerformanceUSD\",\"fuelType\",\"bbid\",\"vegaNotionalAmount\",\"fatalitiesUnderlyingConditionsAbsent\",\"effectiveDate\",\"capped\",\"rating\",\"optionCurrency\",\"isCloseAuction\",\"volatility\",\"avgVentUtil\",\"underlyingAssetIds\",\"buy6point5bps\",\"vwapInLimitRealizedCash\",\"estimatedClosingAuctionVolume\",\"sell2bps\",\"annualRisk\",\"eti\",\"vwapInLimitRealizedBps\",\"rankMtd\",\"marketBuffer\",\"futureMonthJ24\",\"lastUploadedTime\",\"futureMonthJ23\",\"oeId\",\"futureMonthJ22\",\"futureMonthJ21\",\"bbidEquivalent\",\"initBufferThresholdRequired\",\"leg2DesignatedMaturity\",\"matchedMaturityOISSwapRate\",\"fairPrice\",\"participationRateInLimit\",\"extMktClass\",\"priceCurrency\",\"failedCount\",\"leg1IndexLocation\",\"supraStrategy\",\"dayCountConvention\",\"roundedNotionalAmount1\",\"roundedNotionalAmount2\",\"factorSource\",\"futureMonthJ26\",\"lendingSecType\",\"futureMonthJ25\",\"leverage\",\"forecastDay\",\"optionFamily\",\"generatorOutput\",\"priceSpotStopLossValue\",\"kpiId\",\"windGeneration\",\"percentageMidExecutedQuantity\",\"borrowCost\",\"knockOutDirection\",\"riskModel\",\"assetParametersVendor\",\"fairValue\",\"openTime\",\"pressureHourlyForecast\",\"localCcyRate\",\"endUserException\",\"sell90cents\",\"executionVenue\",\"primaryVwapInLimitRealizedBps\",\"approveRebalance\",\"adjustedClosePrice\",\"lmsId\",\"rebateRate\",\"sell130cents\",\"sell32bps\",\"paceOfRollp50\",\"priceMoveVsArrival\",\"strikeRelative\",\"pressureType\",\"buy40bps\",\"priceNotation\",\"strategy\",\"issueStatusDate\",\"lenderIncome\",\"pbClientId\",\"istatRegionCode\",\"sell9bps\",\"ownerId\",\"composite10DayAdv\",\"maxLoanBalance\",\"ideaActivityType\",\"sell60cents\",\"ideaSource\",\"everOnVent\",\"buy15cents\",\"unadjustedAsk\",\"contributionName\",\"givenPlusPaid\",\"lastFillPrice\",\"shortConvictionSmall\",\"upfrontPaymentCurrency\",\"spotSettlementDate\",\"matrixOrder\",\"dateIndex\",\"payerDayCountFraction\",\"assetClassificationsIsPrimary\",\"breakEvenInflationChange\",\"buy130cents\",\"dwiContribution\",\"asset2Id\",\"averageFillPrice\",\"depthSpreadScore\",\"sell10cents\",\"subAccount\",\"buy65cents\",\"bondCdsBasis\",\"vendor\",\"dataSet\",\"notionalAmount2\",\"notionalAmount1\",\"queueingTime\",\"annReturn5Year\",\"volumeStartOfDay\",\"priceNotation3Type\",\"assetParametersFloatingRateDesignatedMaturity\",\"executedNotionalLocal\",\"businessSponsor\",\"unexplained\",\"seasonalAdjustmentShort\",\"metric\",\"ask\",\"closePrice\",\"endTime\",\"sell100cents\",\"executionTimestamp\",\"buy180cents\",\"absoluteStrike\",\"sell3point5bps\",\"liquidityScoreBuy\",\"paymentFrequency\",\"expenseRatioNetBps\",\"metricType\",\"rankYtd\",\"leg1Spread\",\"coverageRegion\",\"absoluteReturnYtd\",\"dayCountConvention2\",\"fwdtier\",\"degreeDays\",\"turnoverAdjusted\",\"priceSpotTargetValue\",\"marketDataPoint\",\"numOfFunds\",\"tradeTime\",\"executionId\",\"turnoverUnadjusted\",\"leg1FloatingIndex\",\"hedgeAnnualizedVolatility\",\"benchmarkCurrency\",\"futuresContract\",\"name\",\"aum\",\"leg1DayCountConvention\",\"cbsCode\",\"folderName\",\"apiUsage\",\"twapInterval\",\"uniqueId\",\"optionExpirationDate\",\"swaptionAtmFwdRate\",\"liveDate\",\"corporateActionType\",\"primeId\",\"description\",\"assetClassificationsIsCountryPrimary\",\"rebateRateLimit\",\"factor\",\"daysOnLoan\",\"longConvictionSmall\",\"sell40cents\",\"relativePayoffYtd\",\"gsfeer\",\"relativeHitRateQtd\",\"wam\",\"wal\",\"quantityccy\",\"backtestId\",\"dirtyPrice\",\"corporateSpreadContribution\",\"relativeHumidityHourlyForecast\",\"multipleScore\",\"betaAdjustedExposure\",\"dividendPoints\",\"brightness\",\"assetParametersReceiverDesignatedMaturity\",\"bosInTicksDescription\",\"testId\",\"impliedCorrelation\",\"normalizedPerformance\",\"bytesConsumed\",\"swaptionVol\",\"estimatedClosingVolume\",\"issuer\",\"dividendYield\",\"marketType\",\"numUnitsLower\",\"sourceOrigin\",\"proceedsAssetSwapSpread3m\",\"totalQuantity\",\"internalUser\",\"sell40bps\",\"redemptionOption\",\"notionalUnit2\",\"notionalUnit1\",\"sedol\",\"roundingCostPnl\",\"midYield\",\"unexecutedNotionalLocal\",\"sustainGlobal\",\"endingDate\",\"proceedsAssetSwapSpread12m\",\"grossInvestmentWtd\",\"annReturn3Year\",\"sharpeWtd\",\"discountFactor\",\"relativeReturnMtd\",\"priceChangeOnDay\",\"buy100cents\",\"forwardPoint\",\"fci\",\"recallQuantity\",\"fxPositioning\",\"gsidEquivalent\",\"categories\",\"extMktAsset\",\"quotingStyle\",\"errorMessage\",\"midPrice\",\"proceedsAssetSwapSpread6m\",\"stsEmDm\",\"embeddedOption\",\"tcmCostHorizon2Day\",\"ageBand\",\"returnsEnabled\",\"runId\",\"queueInLots\",\"tenderOfferExpirationDate\",\"midcurveAnnuity\",\"lendingFundNavTrend\",\"cloudCoverForecast\",\"tcmCostParticipationRate5Pct\",\"defaultBackcast\",\"newsOnIntensity\",\"priceFormingContinuationData\",\"adjustedShortInterest\",\"newHospitalized\",\"assetParametersStrike\",\"buy35cents\",\"leg2TotalNotional\",\"assetParametersEffectiveDate\",\"annReturn10Year\",\"numAdultIcuBeds\",\"daysToExpiration\",\"continuationEvent\",\"wiId\",\"marketCapCategory\",\"historicalVolume\",\"buy5cents\",\"eventStartDate\",\"leg1FixedRate\",\"equityGamma\",\"rptId\",\"grossIncome\",\"emId\",\"assetCountInModel\",\"stsCreditRegion\",\"minTemperature\",\"bucketStartTime\",\"fillType\",\"closeTime\",\"failPct\",\"isoCountryCodeAlpha2\",\"isoCountryCodeAlpha3\",\"amount\",\"lendingFundAcct\",\"rebate\",\"electionType\",\"relativeHitRateMtd\",\"impliedVolatility\",\"spread\",\"variance\",\"wtdDegreeDaysDailyForecast\",\"swaptionAnnuity\",\"buy6bps\",\"g10Currency\",\"humidityForecast\",\"relativePeriod\",\"user\",\"customer\",\"leg1ResetFrequency\",\"queueClockTimeLabel\",\"paceOfRollp100\",\"assetClassificationsGicsSubIndustry\",\"dewPointHourlyForecast\",\"locationType\",\"facetDivisionalReportingGroupId\",\"realizedTwapPerformanceUSD\",\"swapRate\",\"algoExecutionStyle\",\"clientContact\",\"minTemperatureHour\",\"tradingCurrency\",\"totalByOnset\",\"agencySwapSpread\",\"rank\",\"mixedSwapOtherReportedSDR\",\"humidity\",\"dataSetCategory\",\"vwapRealizedBps\",\"buy9bps\",\"totalTested\",\"fatalitiesConfirmed\",\"universeId1\",\"assetParametersPayerDayCountFraction\",\"universeId2\",\"bidLow\",\"bucketizePrice\",\"fairVarianceVolatility\",\"covid19\",\"clientExposure\",\"leg2TotalNotionalUnit\",\"sell45cents\",\"gsSustainSubSector\",\"sinkable\",\"isReal\",\"maxTemperatureHour\",\"leg2AveragingMethod\",\"jsn\",\"sell160cents\",\"knockInDirection\",\"dayCloseUnrealizedUSD\",\"tenor\",\"pricingConvention\",\"popularity\",\"floatingRateOption\",\"hedgeValueType\",\"assetParametersClearingHouse\",\"disclaimer\",\"payerFrequency\",\"loanFee\",\"deploymentVersion\",\"buy16bps\",\"tradeDayCount\",\"priceToSales\",\"newIdeasQtd\",\"subdivisionName\",\"adjustedAskPrice\",\"factorUniverse\",\"arrivalRt\",\"internalIndexCalcAgent\",\"excessMarginValue\",\"transactionCost\",\"centralBankSwapRate\",\"previousNewConfirmed\",\"unrealizedVwapPerformanceBps\",\"degreeDaysDailyForecast\",\"positionAmount\",\"heatIndexHourlyForecast\",\"maRank\",\"fxPositioningSource\",\"eventStartDateTime\",\"impliedVolatilityByDeltaStrike\",\"mqSymbol\",\"numTotalUnits\",\"corporateAction\",\"leg1PriceType\",\"assetParametersPayerRateOption\",\"sell20cents\",\"leg2FixedPaymentCurrency\",\"gRegionalScore\",\"hardToBorrow\",\"sell5bps\",\"rollVwap\",\"wpk\",\"bespokeSwap\",\"assetParametersExpirationDate\",\"countryName\",\"carry\",\"startingDate\",\"loanId\",\"onboarded\",\"liquidityScore\",\"longRatesContribution\",\"sourceDateSpan\",\"annYield6Month\",\"underlyingDataSetId\",\"closeUnadjusted\",\"valueUnit\",\"quantityUnit\",\"adjustedLowPrice\",\"isMomentum\",\"longConvictionLarge\",\"oad\",\"rate\",\"couponType\",\"client\",\"convictionList\",\"passiveEtfRatio\",\"futureMonthG26\",\"futureMonthG25\",\"futureMonthG24\",\"futureMonthG23\",\"typeOfReturn\",\"futureMonthG22\",\"servicingCostLongPnl\",\"excessMarginPercentage\",\"futureMonthG21\",\"totalMild\",\"realizedArrivalPerformanceBps\",\"precipitationDailyForecastInches\",\"exchangeId\",\"leg2FixedPayment\",\"tcmCostHorizon20Day\",\"realm\",\"bid\",\"hedgeValue\",\"orderStartTime\",\"isAggressive\",\"floatingRateDesignatedMaturity\",\"percentageNearExecutedQuantity\",\"orderId\",\"hospitalType\",\"dayCloseRealizedBps\",\"precipitationHourlyForecast\",\"marketCapUSD\",\"auctionFillsPercentage\",\"highPrice\",\"absoluteShares\",\"fixedRateDayCountFraction\",\"model\",\"unrealizedTwapPerformanceUSD\",\"id\",\"maturity\",\"deltaChange\",\"index\",\"unrealizedArrivalPerformanceUSD\",\"icebergSlippage\",\"sell120cents\",\"futureMonthX26\",\"assetTypes\",\"futureMonthX25\",\"bcid\",\"mktPoint\",\"futureMonthX24\",\"restrictionStartDate\",\"touchLiquidityScore\",\"futureMonthX23\",\"futureMonthX22\",\"factorCategoryId\",\"securityTypeId\",\"futureMonthX21\",\"investmentYtd\",\"leg2Notional\",\"sell1bps\",\"sell200cents\",\"expectedCompletionDate\",\"spreadOptionVol\",\"sell80cents\",\"inflationSwapRate\",\"activeQueries\",\"sell45bps\",\"embededOption\",\"eventSource\",\"qisPermNo\",\"settlement\",\"shareclassId\",\"feature2\",\"feature3\",\"stsCommoditySector\",\"exceptionStatus\",\"salesCoverage\",\"feature1\",\"tcmCostParticipationRate10Pct\",\"eventTime\",\"positionSourceName\",\"deliveryDate\",\"interestRate\",\"side\",\"dynamicHybridAggressiveStyle\",\"complianceRestrictedStatus\",\"borrowFee\",\"everIcu\",\"noWorseThanLevel\",\"updateTime\",\"loanSpread\",\"tcmCostHorizon12Hour\",\"dewPoint\",\"researchCommission\",\"buy2bps\",\"assetClassificationsRiskCountryCode\",\"newIdeasMtd\",\"varSwapByExpiry\",\"sellDate\",\"aumStart\",\"assetParametersSettlement\",\"maxTemperature\",\"acquirerShareholderMeetingDate\",\"countIdeasWtd\",\"arrivalRtNormalized\",\"reportType\",\"sourceURL\",\"estimatedReturn\",\"high\",\"sourceLastUpdate\",\"sunshineForecast\",\"quantityMW\",\"sell70cents\",\"sell110cents\",\"pnodeId\",\"humidityType\",\"prevCloseAsk\",\"level\",\"impliedVolatilityByExpiration\",\"assetParametersFixedRateDayCountFraction\",\"esMomentumScore\",\"leg2Index\",\"netWeight\",\"portfolioManagers\",\"bosInTicks\",\"assetParametersCouponType\",\"expectedResidualQuantity\",\"rollDate\",\"dynamicHybridSpeed\",\"capFloorVol\",\"targetQuantity\",\"submitter\",\"no\",\"notional\",\"esDisclosurePercentage\",\"closeExecutedQuantityPercentage\",\"twapRealizedCash\",\"isOpenAuction\",\"leg1Type\",\"wetBulbTempHourlyForecast\",\"cleanupPrice\",\"total\",\"filledNotionalUSD\",\"assetId\",\"testStatus\",\"mktType\",\"lastUpdatedTime\",\"yield30Day\",\"buy28bps\",\"proportionOfRisk\",\"futureMonthK23\",\"futureMonthK22\",\"futureMonthK21\",\"primaryEntityId\",\"cross\",\"ideaStatus\",\"contractSubtype\",\"sri\",\"fxForecast\",\"fixingTimeLabel\",\"isETF\",\"100\",\"101\",\"102\",\"103\",\"104\",\"fillId\",\"excessReturns\",\"105\",\"106\",\"dollarReturn\",\"orderInLimit\",\"expiryTime\",\"107\",\"returnOnEquity\",\"108\",\"109\",\"futureMonthK26\",\"futureMonthK25\",\"futureMonthK24\",\"restrictionEndDate\",\"queueInLotsDescription\",\"volumeLimit\",\"objective\",\"navPrice\",\"leg1UnderlyingAsset\",\"110\",\"111\",\"112\",\"113\",\"privatePlacementType\",\"114\",\"hedgeNotional\",\"115\",\"116\",\"askLow\",\"intendedPRate\",\"117\",\"118\",\"119\",\"expiry\",\"avgMonthlyYield\",\"periodDirection\",\"prevRptId\",\"earningsPerShare\",\"strikePercentage\",\"esProductImpactPercentile\",\"vwapRealizedCash\",\"parAssetSwapSpread1m\",\"prevCloseBid\",\"minimumIncrement\",\"tcmCostHorizon16Day\",\"investmentMtd\",\"settlementDate\",\"weightedAverageMidNormalized\",\"120\",\"121\",\"122\",\"salesPerShare\",\"123\",\"124\",\"125\",\"unadjustedClose\",\"126\",\"127\",\"128\",\"129\",\"loanDate\",\"matchedMaturitySwapSpread1m\",\"collateralPercentageActual\",\"vwapInLimitUnrealizedBps\",\"metricValue\",\"autoExecState\",\"totalRecovered\",\"relativeReturnYtd\",\"130\",\"tickServer\",\"131\",\"132\",\"133\",\"134\",\"cumulativeVolumeInPercentage\",\"135\",\"136\",\"137\",\"138\",\"139\",\"realTimeRestrictionStatus\",\"tradeType\",\"settlementType\",\"netChange\",\"numberOfUnderliers\",\"swapType\",\"forecastType\",\"leg1Notional\",\"sellSettleDate\",\"140\",\"141\",\"142\",\"143\",\"144\",\"145\",\"146\",\"147\",\"newIdeasYtd\",\"managementFee\",\"148\",\"149\",\"parAssetSwapSpread3m\",\"sell36bps\",\"matchedMaturitySwapSpread3m\",\"sourceId\",\"country\",\"vwap\",\"touchSpreadScore\",\"ratingSecondHighest\",\"sell24bps\",\"150\",\"151\",\"152\",\"frequency\",\"153\",\"154\",\"activityId\",\"155\",\"estimatedImpact\",\"sell35cents\",\"156\",\"loanSpreadBucket\",\"157\",\"158\",\"coronavirusGlobalActivityTracker\",\"159\",\"underlyers\",\"assetParametersPricingLocation\",\"eventDescription\",\"icebergMaxSize\",\"assetParametersCoupon\",\"details\",\"sector\",\"avgBedUtilRate\",\"buy20bps\",\"epidemic\",\"mctr\",\"exchangeTime\",\"historicalClose\",\"fipsCode\",\"160\",\"161\",\"buy32bps\",\"162\",\"163\",\"ideaId\",\"commentStatus\",\"marginalCost\",\"164\",\"165\",\"166\",\"167\",\"168\",\"clientWeight\",\"169\",\"leg1DeliveryPoint\",\"sell5cents\",\"liqWkly\",\"unrealizedTwapPerformanceBps\",\"region\",\"temperatureHour\",\"upperBound\",\"sell55cents\",\"170\",\"171\",\"numPediIcuBeds\",\"172\",\"bidYield\",\"173\",\"174\",\"expectedResidual\",\"175\",\"176\",\"optionPremium\",\"177\",\"178\",\"179\",\"ownerName\",\"parAssetSwapSpread6m\",\"zScore\",\"sell12bps\",\"eventStartTime\",\"matchedMaturitySwapSpread6m\",\"turnover\",\"priceSpotTargetUnit\",\"coverage\",\"gPercentile\",\"180\",\"181\",\"182\",\"cloudCoverHourlyForecast\",\"183\",\"184\",\"lendingFundNav\",\"sourceOriginalCategory\",\"percentCloseExecutionQuantity\",\"185\",\"latestExecutionTime\",\"186\",\"187\",\"arrivalMidRealizedBps\",\"188\",\"189\",\"location\",\"scenarioId\",\"terminationTenor\",\"queueClockTime\",\"discretionLowerBound\",\"tcmCostParticipationRate50Pct\",\"ratingLinear\",\"previousCloseUnrealizedBps\",\"190\",\"191\",\"subAssetClassForOtherCommodity\",\"192\",\"forwardPrice\",\"193\",\"type\",\"194\",\"strikeRef\",\"195\",\"196\",\"197\",\"cumulativePnl\",\"198\",\"shortTenor\",\"sell28bps\",\"fundClass\",\"199\",\"unadjustedVolume\",\"buy36bps\",\"positionIdx\",\"windChillHourlyForecast\",\"secName\",\"impliedVolatilityByRelativeStrike\",\"percentADV\",\"leg1TotalNotional\",\"contract\",\"paymentFrequency1\",\"paymentFrequency2\",\"bespoke\",\"repoTenor\",\"sell15cents\",\"investmentQtd\",\"heatIndexForecast\",\"ratingStandardAndPoors\",\"qualityStars\",\"leg2FloatingIndex\",\"sourceTicker\",\"primaryVwapUnrealizedBps\",\"gsid\",\"lendingFund\",\"sensitivity\",\"dayCount\",\"sell16bps\",\"relativeBreakEvenInflationChange\",\"sell25cents\",\"varSwap\",\"buy5point5bps\",\"blockLargeNotional\",\"sell2point5bps\",\"capacity\",\"sectorsRaw\",\"primaryVwapInLimit\",\"shareclassPrice\",\"tradeSize\",\"priceSpotEntryValue\",\"buy8point5bps\",\"symbolDimensions\",\"buy24bps\",\"observation\",\"optionTypeSDR\",\"scenarioGroupId\",\"averageImpliedVariance\",\"avgTradeRateDescription\",\"fraction\",\"assetCountShort\",\"collateralPercentageRequired\",\"sell5point5bps\",\"date\",\"zipCode\",\"totalStdReturnSinceInception\",\"sourceCategory\",\"volumeUnadjusted\",\"passiveRatio\",\"priceToEarnings\",\"orderDepth\",\"annYield3Month\",\"netFlowStd\",\"encodedStats\",\"buy5bps\",\"runTime\",\"askSize\",\"absoluteReturnMtd\",\"std30DaysUnsubsidizedYield\",\"resource\",\"averageRealizedVolatility\",\"traceAdvBuy\",\"newConfirmed\",\"sell8bps\",\"bidPrice\",\"sell8point5bps\",\"targetPriceUnrealizedBps\",\"esNumericPercentile\",\"leg2UnderlyingAsset\",\"csaTerms\",\"relativePayoffMtd\",\"dailyNetShareholderFlows\",\"buy2point5bps\",\"cai\",\"executedNotionalUSD\",\"systemTime\",\"totalHomeIsolation\",\"stationName\",\"passPct\",\"openingReport\",\"midcurveAtmFwdRate\",\"precipitationForecast\",\"equityRiskPremiumIndex\",\"fatalitiesUnderlyingConditionsUnknown\",\"buy12bps\",\"clearingHouse\",\"dayCloseUnrealizedBps\",\"stsRatesMaturity\",\"liqDly\",\"contributorRole\",\"totalFatalities\",\"adjustedClose\",\"averageValue\",\"avgInterestRate\",\"basisDuration\",\"bestMonthDate\",\"bloombergTicker\",\"capexDepreciation\",\"capexSales\",\"cashConversion\",\"category\",\"convexity\",\"countryCode\",\"croci\",\"currentValue\",\"dacf\",\"dailyVolatility\",\"divYield\",\"dpsGrowth\",\"drawdownOverReturn\",\"ebitdaGrowth\",\"ebitdaMargin\",\"ebitGrowth\",\"ebitMargin\",\"evGci\",\"fcfConversion\",\"fcfYield\",\"gci\",\"grossProfTotAssets\",\"historicCPR\",\"incrementalMargin\",\"industry\",\"informationRatio\",\"interestCover\",\"lastChange\",\"lastChangePct\",\"lastDate\",\"lastValue\",\"liborMatchedMaturitySwap\",\"liborOAS\",\"liborProceedsASW\",\"liborzSpread\",\"manEarningGrowthMeas\",\"marginalRiskContribution\",\"maxDrawdown\",\"netDebtEbitda\",\"netDebtEquity\",\"niGrowth\",\"niMargin\",\"oisMatchedMaturitySwap\",\"oisProceedsASW\",\"oiszSpread\",\"optionStyle\",\"payup\",\"positionDate\",\"preTaxProfitGrowth\",\"riskPremiaStyles\",\"roce\",\"rolldown\",\"salesGrowth\",\"sharpeRatio\",\"totalDebtCapital\",\"totalDebtTotalAsset\",\"totalReturn\",\"unleveredFcfYield\",\"worstMonthDate\"]},\"function\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"\\\\w{1,20}\",\"type\":\"string\",\"description\":\"Aggregation function to be applied to a field\"},\"alias\":{\"maxLength\":50,\"minLength\":0,\"pattern\":\"\\\\w{1,20}\",\"type\":\"string\",\"description\":\"Alias for a given field on return\"}},\"additionalProperties\":false}]}},\"startDate\":{\"maxLength\":50,\"minLength\":0,\"type\":\"string\",\"description\":\"ISO 8601-formatted date\",\"format\":\"date\"}},\"additionalProperties\":false}"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/space-api.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/space-api.json
new file mode 100644
index 00000000..32ed92dd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/fixtures/space-api.json
@@ -0,0 +1,133 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Space API",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/planets": {
+ "get": {
+ "operationId": "GET /planets",
+ "summary": "Get a list of all planets",
+ "responses": {
+ "200": {
+ "description": "200 response for planet list",
+ "content": {
+ "application/json": {
+ "examples": {
+ "test": {
+ "value": {
+ "planets": ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/planets/random": {
+ "get": {
+ "operationId": "GET /planets/random",
+ "summary": "Get a random planet's info",
+ "responses": {
+ "200": {
+ "description": "200 response for planet info",
+ "content": {
+ "application/json": {
+ "examples": {
+ "earth": {
+ "value": {
+ "name": "Earth",
+ "diameter_mi": 7917.5,
+ "day_length_earth_days": 1
+ }
+ },
+ "venus": {
+ "value": {
+ "name": "Venus",
+ "diameter_mi": 7520.8,
+ "day_length_earth_days": 116.75
+ }
+ },
+ "neptune": {
+ "value": {
+ "name": "Neptune",
+ "diameter_mi": 30599,
+ "day_length_earth_days": 0.66
+ }
+ }
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "404 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "no_planets": {
+ "value": {
+ "error": "Your planets are in another universe!"
+ }
+ }
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "500 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "no_planets": {
+ "value": {
+ "error": "Bzzzzzt. We need to reboot the universe."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/moons": {
+ "get": {
+ "operationId": "GET /moons",
+ "summary": "Get info about moons",
+ "responses": {
+ "200": {
+ "description": "200 response for moons",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/MoonResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "MoonResponse": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "diameter_mi": {
+ "type": "integer"
+ },
+ "orbiting": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/makefile b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/makefile
new file mode 100644
index 00000000..9f4280d3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/makefile
@@ -0,0 +1,53 @@
+dockerCPImage=$(GW_IMAGE)
+kongCloudPullPassword=$(DOCKERHUB_KONGCLOUD_PULL_PSW)
+composeGeneratorFolderName=gateway-docker-compose-generator
+composeGeneratorFolderPath=${PWD}/${composeGeneratorFolderName}
+
+host=ec2-18-118-37-120.us-east-2.compute.amazonaws.com
+ip=18.118.37.120
+secret=$(shell [ -f ./testec2.pem ] && echo testec2.pem)
+ssh=ssh -i ${secret} ec2-user@${host}
+sshLight=ssh -o StrictHostKeyChecking=no -i ${secret} ec2-user@${host}
+
+clear_ec2_docker_volumes:
+ @echo "Removing EC2 Docker volumes"
+ ${ssh} 'docker volume rm $$(docker volume ls -q)'
+ @echo "Finished Removing EC2 Docker volumes"
+
+start_gw:
+ @echo "Starting Gateway"
+ cd ${composeGeneratorFolderPath}; make start
+ @echo "Started Gateway"
+
+stop_gw:
+ @echo "Stopping Gateway"
+ cd ${composeGeneratorFolderPath}; make stop; make clean
+ @echo "Gateway was successfully stopped"
+
+set_env_variables:
+ cd ${composeGeneratorFolderPath}; export ${envVars}; make generate
+
+custom_start_gw: stop_gw set_env_variables start_gw
+
+set_docker_cp_image:
+ @echo "Updating EC2 Docker Control Plane Image"
+ ${ssh} 'grep -qxF GW_IMAGE=${dockerCPImage} ./gw/.env || echo GW_IMAGE=${dockerCPImage} > ./gw/.env'
+ @echo "Finished updating EC2 Docker Control Plane Image"
+
+reset_docker_image_vars:
+ @echo "Deleting EC2 GW_IMAGE variable from .env file"
+ ${sshLight} "echo 'Resetting EC2 environment variables' && : > ./gw/.env"
+ @echo "Finished Deleting EC2 GW_IMAGE variable from .env file"
+
+login_with_kongcloudpull:
+ @echo "Logging in with kongcloud pull credentials"
+ ${ssh} 'echo "${kongCloudPullPassword}" | docker login -u "kongcloudpull" --password-stdin || true'
+
+update_kong_container_env_var:
+# usage: kongVars="-e KONG_PORTAL=on -e KONG_VITALS=on" command="kong reload" make gwContainerName=kong-cp update_kong_container_env_var
+ @echo "Updating Kong container environment variable"
+ docker exec ${kongVars} $$(docker ps -aqf name=$(gwContainerName)) ${command}
+
+get_kong_container_logs:
+ @echo "Reading kong container logs"
+ docker logs $$(docker ps -aqf name=$(gwContainerName)) --tail 4 &> logs.txt
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/package-lock.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/package-lock.json
new file mode 100644
index 00000000..7589f332
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/package-lock.json
@@ -0,0 +1,4696 @@
+{
+ "name": "@kong/kong-api-tests",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@kong/kong-api-tests",
+ "version": "0.0.1",
+ "dependencies": {
+ "@kong/kauth-test-utils": "^0.0.22",
+ "@kong/khcp-api-client": "^0.1.2002",
+ "axios": "^1.7.2",
+ "chai": "^4.4.1",
+ "chai-arrays": "^2.2.0",
+ "chai-as-promised": "^7.1.2",
+ "chai-bytes": "^0.1.2",
+ "chai-like": "^1.1.1",
+ "chai-string": "^1.5.0",
+ "dotenv": "^16.4.5",
+ "expect": "^29.7.0",
+ "influx": "^5.9.3",
+ "jsonwebtoken": "^9.0.2",
+ "jwt-decode": "^4.0.0",
+ "mocha": "^10.4.0",
+ "mocha-multi-reporters": "^1.5.1",
+ "promise-ws": "^1.0.0-1",
+ "redis": "^4.6.13",
+ "uuid": "^9.0.0"
+ },
+ "devDependencies": {
+ "@pollyjs/adapter-node-http": "^6.0.6",
+ "@pollyjs/core": "^6.0.6",
+ "@pollyjs/persister-fs": "^6.0.6",
+ "@tsconfig/recommended": "^1.0.6",
+ "@types/chai": "^4.3.9",
+ "@types/chai-arrays": "^2.0.2",
+ "@types/chai-as-promised": "^7.1.7",
+ "@types/chai-like": "^1.1.2",
+ "@types/chai-string": "^1.4.2",
+ "@types/mocha": "^10.0.5",
+ "@types/node": "^18.19.36",
+ "@types/uuid": "^9.0.8",
+ "@typescript-eslint/eslint-plugin": "^5.60.0",
+ "@typescript-eslint/parser": "^5.60.1",
+ "eslint": "^8.57.0",
+ "eslint-config-prettier": "^8.10.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "prettier": "^2.8.8",
+ "pretty-quick": "^3.3.1",
+ "ts-node": "^10.9.2",
+ "tsconfig-paths": "^4.1.2",
+ "typescript": "^4.9.5"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz",
+ "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==",
+ "dependencies": {
+ "@babel/highlight": "^7.24.6",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz",
+ "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz",
+ "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.24.6",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz",
+ "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "node_modules/@jest/expect-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
+ "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
+ "dependencies": {
+ "jest-get-type": "^29.6.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jest/types": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.8",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@kong/kauth-client-typescript-axios": {
+ "version": "1.142.0",
+ "resolved": "https://registry.npmjs.org/@kong/kauth-client-typescript-axios/-/kauth-client-typescript-axios-1.142.0.tgz",
+ "integrity": "sha512-NTPzVl0+j4n7Z04jyfawr/AIdHPwHhvaNAsJNzqXu0VQs1s+z4CTfh2a8w5TRMN6rKj8KFhDmCG0mSi0if/WOw=="
+ },
+ "node_modules/@kong/kauth-client-v3-axios": {
+ "version": "0.0.20",
+ "resolved": "https://registry.npmjs.org/@kong/kauth-client-v3-axios/-/kauth-client-v3-axios-0.0.20.tgz",
+ "integrity": "sha512-uS8WUHs5kAEKvqDOI9mrgd7/bsXR4pSzlXEUiZKcv/qmQibRtEOCAbtz/P9bLrCq7737OVTvjRPnUei7WDst+g=="
+ },
+ "node_modules/@kong/kauth-test-utils": {
+ "version": "0.0.22",
+ "resolved": "https://registry.npmjs.org/@kong/kauth-test-utils/-/kauth-test-utils-0.0.22.tgz",
+ "integrity": "sha512-20rJYRt8EAMJyXswiQ22B6ZXXCRqQChB2D0ClRzurh6qX5pNZTgfhReTsS6qqFoc9yGjEiAtdqSP3uOlsuWQRA==",
+ "dependencies": {
+ "@kong/kauth-client-typescript-axios": "^1.142.0",
+ "@kong/kauth-client-v3-axios": "^0.0.20",
+ "axios": "^0.27.2",
+ "chai": "^4.4.1",
+ "cookie": "^0.6.0"
+ }
+ },
+ "node_modules/@kong/kauth-test-utils/node_modules/axios": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "dependencies": {
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/@kong/khcp-api-client": {
+ "version": "0.1.2002",
+ "resolved": "https://registry.npmjs.org/@kong/khcp-api-client/-/khcp-api-client-0.1.2002.tgz",
+ "integrity": "sha512-3Y6kuM8ecOitNUXt4wLqnjMq/zKRKuKSOYrDPE/Ajat/pfSZJy7NcJSWRMssOOH1Yc2agcO/HJGzzGeRatL7Kw==",
+ "engines": {
+ "node": ">=14.21.3",
+ "yarn": "1.x"
+ },
+ "peerDependencies": {
+ "axios": "0.27.x",
+ "url": "0.x"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pollyjs/adapter": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@pollyjs/adapter/-/adapter-6.0.6.tgz",
+ "integrity": "sha512-szhys0NiFQqCJDMC0kpDyjhLqSI7aWc6m6iATCRKgcMcN/7QN85pb3GmRzvnNV8+/Bi2AUSCwxZljcsKhbYVWQ==",
+ "dev": true,
+ "dependencies": {
+ "@pollyjs/utils": "^6.0.6"
+ }
+ },
+ "node_modules/@pollyjs/adapter-node-http": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@pollyjs/adapter-node-http/-/adapter-node-http-6.0.6.tgz",
+ "integrity": "sha512-jdJG7oncmSHZAtVMmRgOxh5A56b7G8H9ULlk/ZaVJ+jNrlFXhLmPpx8OQoSF4Cuq2ugdiWmwmAjFXHStcpY3Mw==",
+ "dev": true,
+ "dependencies": {
+ "@pollyjs/adapter": "^6.0.6",
+ "@pollyjs/utils": "^6.0.6",
+ "lodash-es": "^4.17.21",
+ "nock": "^13.2.1"
+ }
+ },
+ "node_modules/@pollyjs/core": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@pollyjs/core/-/core-6.0.6.tgz",
+ "integrity": "sha512-1ZZcmojW8iSFmvHGeLlvuudM3WiDV842FsVvtPAo3HoAYE6jCNveLHJ+X4qvonL4enj1SyTF3hXA107UkQFQrA==",
+ "dev": true,
+ "dependencies": {
+ "@pollyjs/utils": "^6.0.6",
+ "@sindresorhus/fnv1a": "^2.0.1",
+ "blueimp-md5": "^2.19.0",
+ "fast-json-stable-stringify": "^2.1.0",
+ "is-absolute-url": "^3.0.3",
+ "lodash-es": "^4.17.21",
+ "loglevel": "^1.8.0",
+ "route-recognizer": "^0.3.4",
+ "slugify": "^1.6.3"
+ }
+ },
+ "node_modules/@pollyjs/node-server": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@pollyjs/node-server/-/node-server-6.0.6.tgz",
+ "integrity": "sha512-nkP1+hdNoVOlrRz9R84haXVsaSmo8Xmq7uYK9GeUMSLQy4Fs55ZZ9o2KI6vRA8F6ZqJSbC31xxwwIoTkjyP7Vg==",
+ "dev": true,
+ "dependencies": {
+ "@pollyjs/utils": "^6.0.6",
+ "body-parser": "^1.19.0",
+ "cors": "^2.8.5",
+ "express": "^4.17.1",
+ "fs-extra": "^10.0.0",
+ "http-graceful-shutdown": "^3.1.5",
+ "morgan": "^1.10.0",
+ "nocache": "^3.0.1"
+ }
+ },
+ "node_modules/@pollyjs/persister": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@pollyjs/persister/-/persister-6.0.6.tgz",
+ "integrity": "sha512-9KB1p+frvYvFGur4ifzLnFKFLXAMXrhAhCnVhTnkG2WIqqQPT7y+mKBV/DKCmYFx8GPA9FiNGqt2pB53uJpIdw==",
+ "dev": true,
+ "dependencies": {
+ "@pollyjs/utils": "^6.0.6",
+ "@types/set-cookie-parser": "^2.4.1",
+ "bowser": "^2.4.0",
+ "fast-json-stable-stringify": "^2.1.0",
+ "lodash-es": "^4.17.21",
+ "set-cookie-parser": "^2.4.8",
+ "utf8-byte-length": "^1.0.4"
+ }
+ },
+ "node_modules/@pollyjs/persister-fs": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@pollyjs/persister-fs/-/persister-fs-6.0.6.tgz",
+ "integrity": "sha512-/ALVgZiH2zGqwLkW0Mntc0Oq1v7tR8LS8JD2SAyIsHpnSXeBUnfPWwjAuYw0vqORHFVEbwned6MBRFfvU/3qng==",
+ "dev": true,
+ "dependencies": {
+ "@pollyjs/node-server": "^6.0.6",
+ "@pollyjs/persister": "^6.0.6"
+ }
+ },
+ "node_modules/@pollyjs/utils": {
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/@pollyjs/utils/-/utils-6.0.6.tgz",
+ "integrity": "sha512-nhVJoI3nRgRimE0V2DVSvsXXNROUH6iyJbroDu4IdsOIOFC1Ds0w+ANMB4NMwFaqE+AisWOmXFzwAGdAfyiQVg==",
+ "dev": true,
+ "dependencies": {
+ "qs": "^6.10.1",
+ "url-parse": "^1.5.3"
+ }
+ },
+ "node_modules/@redis/bloom": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
+ "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
+ "peerDependencies": {
+ "@redis/client": "^1.0.0"
+ }
+ },
+ "node_modules/@redis/client": {
+ "version": "1.5.16",
+ "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.16.tgz",
+ "integrity": "sha512-X1a3xQ5kEMvTib5fBrHKh6Y+pXbeKXqziYuxOUo1ojQNECg4M5Etd1qqyhMap+lFUOAh8S7UYevgJHOm4A+NOg==",
+ "dependencies": {
+ "cluster-key-slot": "1.1.2",
+ "generic-pool": "3.9.0",
+ "yallist": "4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@redis/graph": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
+ "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
+ "peerDependencies": {
+ "@redis/client": "^1.0.0"
+ }
+ },
+ "node_modules/@redis/json": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz",
+ "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==",
+ "peerDependencies": {
+ "@redis/client": "^1.0.0"
+ }
+ },
+ "node_modules/@redis/search": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz",
+ "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==",
+ "peerDependencies": {
+ "@redis/client": "^1.0.0"
+ }
+ },
+ "node_modules/@redis/time-series": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz",
+ "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==",
+ "peerDependencies": {
+ "@redis/client": "^1.0.0"
+ }
+ },
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="
+ },
+ "node_modules/@sindresorhus/fnv1a": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/fnv1a/-/fnv1a-2.0.1.tgz",
+ "integrity": "sha512-suq9tRQ6bkpMukTG5K5z0sPWB7t0zExMzZCdmYm6xTSSIm/yCKNm7VCL36wVeyTsFr597/UhU1OAYdHGMDiHrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/recommended": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.6.tgz",
+ "integrity": "sha512-0IKu9GHYF1NGTJiYgfWwqnOQSlnE9V9R7YohHNNf0/fj/SyOZWzdd06JFr0fLpg1Mqw0kGbYg8w5xdkSqLKM9g==",
+ "dev": true
+ },
+ "node_modules/@types/chai": {
+ "version": "4.3.16",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz",
+ "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==",
+ "dev": true
+ },
+ "node_modules/@types/chai-arrays": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/chai-arrays/-/chai-arrays-2.0.3.tgz",
+ "integrity": "sha512-6Sn2OTvsv8kvdcwDoVdwYut2/PMQh3AIp4sB+uDpn7SFLRF123SWOlmC/WQHZmTFapaMvroh+MbLNpSoU+rlLQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/chai": "*"
+ }
+ },
+ "node_modules/@types/chai-as-promised": {
+ "version": "7.1.8",
+ "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz",
+ "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==",
+ "dev": true,
+ "dependencies": {
+ "@types/chai": "*"
+ }
+ },
+ "node_modules/@types/chai-like": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@types/chai-like/-/chai-like-1.1.3.tgz",
+ "integrity": "sha512-AEGBQz8wcPhvytKR5EP3HiQrmUeg6HP/ZgNnGWnLaQA4fyZ7kDS1/wbSBLN4CBTMobK4wM2SpksVWzTXWQ8r3w==",
+ "dev": true,
+ "dependencies": {
+ "@types/chai": "*"
+ }
+ },
+ "node_modules/@types/chai-string": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.5.tgz",
+ "integrity": "sha512-IecXRMSnpUvRnTztdpSdjcmcW7EdNme65bfDCQMi7XrSEPGmyDYYTEfc5fcactWDA6ioSm8o7NUqg9QxjBCCEw==",
+ "dev": true,
+ "dependencies": {
+ "@types/chai": "*"
+ }
+ },
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="
+ },
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+ "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+ "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
+ "node_modules/@types/mocha": {
+ "version": "10.0.6",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz",
+ "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "18.19.36",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.36.tgz",
+ "integrity": "sha512-tX1BNmYSWEvViftB26VLNxT6mEr37M7+ldUtq7rlKnv4/2fKYsJIOmqJAjT6h1DNuwQjIKgw3VJ/Dtw3yiTIQw==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/semver": {
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
+ "dev": true
+ },
+ "node_modules/@types/set-cookie-parser": {
+ "version": "2.4.7",
+ "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.7.tgz",
+ "integrity": "sha512-+ge/loa0oTozxip6zmhRIk8Z/boU51wl9Q6QdLZcokIGMzY5lFXYy/x7Htj2HTC6/KZP1hUbZ1ekx8DYXICvWg==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
+ },
+ "node_modules/@types/uuid": {
+ "version": "9.0.8",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
+ "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
+ "dev": true
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.32",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
+ "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
+ "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.4.0",
+ "@typescript-eslint/scope-manager": "5.62.0",
+ "@typescript-eslint/type-utils": "5.62.0",
+ "@typescript-eslint/utils": "5.62.0",
+ "debug": "^4.3.4",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "natural-compare-lite": "^1.4.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^5.0.0",
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
+ "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.62.0",
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/typescript-estree": "5.62.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
+ "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/visitor-keys": "5.62.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
+ "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "5.62.0",
+ "@typescript-eslint/utils": "5.62.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
+ "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
+ "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/visitor-keys": "5.62.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
+ "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.62.0",
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/typescript-estree": "5.62.0",
+ "eslint-scope": "^5.1.1",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
+ "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
+ "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "dev": true
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/axios": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
+ "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/basic-auth/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/blueimp-md5": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
+ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==",
+ "dev": true
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
+ "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+ "dev": true,
+ "dependencies": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.2",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/body-parser/node_modules/qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dev": true,
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/bowser": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
+ "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/chai": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz",
+ "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==",
+ "dependencies": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.3",
+ "deep-eql": "^4.1.3",
+ "get-func-name": "^2.0.2",
+ "loupe": "^2.3.6",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chai-arrays": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/chai-arrays/-/chai-arrays-2.2.0.tgz",
+ "integrity": "sha512-4awrdGI2EH8owJ9I58PXwG4N56/FiM8bsn4CVSNEgr4GKAM6Kq5JPVApUbhUBjDakbZNuRvV7quRSC38PWq/tg==",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/chai-as-promised": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz",
+ "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==",
+ "dependencies": {
+ "check-error": "^1.0.2"
+ },
+ "peerDependencies": {
+ "chai": ">= 2.1.2 < 6"
+ }
+ },
+ "node_modules/chai-bytes": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/chai-bytes/-/chai-bytes-0.1.2.tgz",
+ "integrity": "sha512-0ol6oJS0y1ozj6AZK8n1pyv1/G+l44nqUJygAkK1UrYl+IOGie5vcrEdrAlwmLYGIA9NVvtHWosPYwWWIXf/XA==",
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "chai": ">=2 <5"
+ }
+ },
+ "node_modules/chai-like": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/chai-like/-/chai-like-1.1.1.tgz",
+ "integrity": "sha512-VKa9z/SnhXhkT1zIjtPACFWSoWsqVoaz1Vg+ecrKo5DCKVlgL30F/pEyEvXPBOVwCgLZcWUleCM/C1okaKdTTA==",
+ "peerDependencies": {
+ "chai": "2 - 4"
+ }
+ },
+ "node_modules/chai-string": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz",
+ "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==",
+ "peerDependencies": {
+ "chai": "^4.1.2"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/check-error": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
+ "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
+ "dependencies": {
+ "get-func-name": "^2.0.2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+ "dev": true
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
+ "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-eql": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+ "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+ "dependencies": {
+ "type-detect": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz",
+ "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+ "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.28.0",
+ "prettier": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/execa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/expect": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
+ "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
+ "dependencies": {
+ "@jest/expect-utils": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "jest-matcher-utils": "^29.7.0",
+ "jest-message-util": "^29.7.0",
+ "jest-util": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.19.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
+ "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.2",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.6.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.2.0",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.11.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/express/node_modules/qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dev": true,
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generic-pool": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
+ "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-func-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-graceful-shutdown": {
+ "version": "3.1.13",
+ "resolved": "https://registry.npmjs.org/http-graceful-shutdown/-/http-graceful-shutdown-3.1.13.tgz",
+ "integrity": "sha512-Ci5LRufQ8AtrQ1U26AevS8QoMXDOhnAHCJI3eZu1com7mZGHxREmw3dNj85ftpQokQCvak8nI2pnFS8zyM1M+Q==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.12.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/influx": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/influx/-/influx-5.9.3.tgz",
+ "integrity": "sha512-QQU9CgwnaEV6zMrK8+vhVItsdoKFqDioXJrjJhRQaff9utvT3N0jcrQJT9qnxFLktqgJ5ngbDY68Zh4eo4uD/w=="
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-absolute-url": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+ "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/jest-diff": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
+ "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^29.6.3",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-get-type": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
+ "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
+ "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "jest-diff": "^29.7.0",
+ "jest-get-type": "^29.6.3",
+ "pretty-format": "^29.7.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-message-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
+ "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
+ "dependencies": {
+ "@babel/code-frame": "^7.12.13",
+ "@jest/types": "^29.6.3",
+ "@types/stack-utils": "^2.0.0",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.9",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^29.7.0",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-util": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+ "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
+ "dependencies": {
+ "@jest/types": "^29.6.3",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "ci-info": "^3.2.0",
+ "graceful-fs": "^4.2.9",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+ "dev": true
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/loglevel": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz",
+ "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.0"
+ },
+ "funding": {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/loglevel"
+ }
+ },
+ "node_modules/loupe": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
+ "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
+ "dependencies": {
+ "get-func-name": "^2.0.1"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
+ "dev": true
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
+ "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mocha": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz",
+ "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==",
+ "dependencies": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.5.3",
+ "debug": "4.3.4",
+ "diff": "5.0.0",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "8.1.0",
+ "he": "1.2.0",
+ "js-yaml": "4.1.0",
+ "log-symbols": "4.1.0",
+ "minimatch": "5.0.1",
+ "ms": "2.1.3",
+ "serialize-javascript": "6.0.0",
+ "strip-json-comments": "3.1.1",
+ "supports-color": "8.1.1",
+ "workerpool": "6.2.1",
+ "yargs": "16.2.0",
+ "yargs-parser": "20.2.4",
+ "yargs-unparser": "2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/mocha-multi-reporters": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz",
+ "integrity": "sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg==",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "lodash": "^4.17.15"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "peerDependencies": {
+ "mocha": ">=3.1.2"
+ }
+ },
+ "node_modules/mocha/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/mocha/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mocha/node_modules/debug/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mocha/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "node_modules/mocha/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/morgan": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
+ "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
+ "dev": true,
+ "dependencies": {
+ "basic-auth": "~2.0.1",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/morgan/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/morgan/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/morgan/node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/mri": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
+ "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/natural-compare-lite": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+ "dev": true
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nocache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz",
+ "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/nock": {
+ "version": "13.5.4",
+ "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz",
+ "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.0",
+ "json-stringify-safe": "^5.0.1",
+ "propagate": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
+ "dev": true
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/pretty-quick": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz",
+ "integrity": "sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==",
+ "dev": true,
+ "dependencies": {
+ "execa": "^4.1.0",
+ "find-up": "^4.1.0",
+ "ignore": "^5.3.0",
+ "mri": "^1.2.0",
+ "picocolors": "^1.0.0",
+ "picomatch": "^3.0.1",
+ "tslib": "^2.6.2"
+ },
+ "bin": {
+ "pretty-quick": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=10.13"
+ },
+ "peerDependencies": {
+ "prettier": "^2.0.0"
+ }
+ },
+ "node_modules/pretty-quick/node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pretty-quick/node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pretty-quick/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pretty-quick/node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pretty-quick/node_modules/picomatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz",
+ "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/promise-ws": {
+ "version": "1.0.0-1",
+ "resolved": "https://registry.npmjs.org/promise-ws/-/promise-ws-1.0.0-1.tgz",
+ "integrity": "sha512-VtDegdCu9VKKNVv/4H3qj0+dG1Qrg1wGb7ZLY0qUYfEVkEw4c2B+2i1hIFlB31KEg36YNg3i62yMr3JIdmbu9Q==",
+ "dependencies": {
+ "pify": "^3.0.0",
+ "ws": "^6.2.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/propagate": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
+ "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.12.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
+ "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
+ "dependencies": {
+ "side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "dev": true,
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/redis": {
+ "version": "4.6.14",
+ "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.14.tgz",
+ "integrity": "sha512-GrNg/e33HtsQwNXL7kJT+iNFPSwE1IPmd7wzV3j4f2z0EYxZfZE7FVTmUysgAtqQQtg5NXF5SNLR9OdO/UHOfw==",
+ "dependencies": {
+ "@redis/bloom": "1.2.0",
+ "@redis/client": "1.5.16",
+ "@redis/graph": "1.1.1",
+ "@redis/json": "1.0.6",
+ "@redis/search": "1.1.6",
+ "@redis/time-series": "1.0.5"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/route-recognizer": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/route-recognizer/-/route-recognizer-0.3.4.tgz",
+ "integrity": "sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==",
+ "dev": true
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/semver": {
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "dev": true,
+ "dependencies": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.18.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
+ "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==",
+ "dev": true
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slugify": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
+ "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+ "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/stack-utils/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dev": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-node/node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
+ "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
+ "dev": true,
+ "dependencies": {
+ "json5": "^2.2.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "node_modules/tsutils/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz",
+ "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==",
+ "peer": true,
+ "dependencies": {
+ "punycode": "^1.4.1",
+ "qs": "^6.11.2"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/url/node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
+ "peer": true
+ },
+ "node_modules/utf8-byte-length": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
+ "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
+ "dev": true
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/workerpool": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
+ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw=="
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "node_modules/ws": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
+ "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
+ "dependencies": {
+ "async-limiter": "~1.0.0"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.2.4",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/package.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/package.json
new file mode 100644
index 00000000..8a8bb8ba
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/package.json
@@ -0,0 +1,65 @@
+{
+ "name": "@kong/kong-api-tests",
+ "version": "0.0.1",
+ "private": true,
+ "author": "Kong Inc.",
+ "scripts": {
+ "format": "prettier --write \"**/*.{js,ts}\"",
+ "lint": "eslint \"**/*.{js,ts}\"",
+ "test-spec": "mocha \"test/gateway/**/${npm_config_spec}.spec.ts\"",
+ "test-gateway": "TEST_APP=gateway mocha \"test/gateway/**/*.spec.ts\"",
+ "test-multiple": "mocha ${npm_config_spec}",
+ "test-smoke": "TEST_APP=gateway mocha -g @smoke --recursive",
+ "test-non-smoke": "TEST_APP=gateway mocha --invert -g @smoke --recursive",
+ "test-koko": "TEST_APP=koko TEST_ENV=dev mocha -g @koko --recursive",
+ "test-gke": "TEST_APP=gateway mocha -g @gke --recursive"
+ },
+ "dependencies": {
+ "@kong/kauth-test-utils": "^0.0.22",
+ "axios": "^1.7.2",
+ "chai": "^4.4.1",
+ "@kong/khcp-api-client": "^0.1.2002",
+ "chai-arrays": "^2.2.0",
+ "chai-as-promised": "^7.1.2",
+ "chai-bytes": "^0.1.2",
+ "chai-like": "^1.1.1",
+ "chai-string": "^1.5.0",
+ "dotenv": "^16.4.5",
+ "expect": "^29.7.0",
+ "influx": "^5.9.3",
+ "jsonwebtoken": "^9.0.2",
+ "jwt-decode": "^4.0.0",
+ "mocha": "^10.4.0",
+ "mocha-multi-reporters": "^1.5.1",
+ "promise-ws": "^1.0.0-1",
+ "redis": "^4.6.13",
+ "uuid": "^9.0.0"
+ },
+ "devDependencies": {
+ "@pollyjs/adapter-node-http": "^6.0.6",
+ "@pollyjs/core": "^6.0.6",
+ "@pollyjs/persister-fs": "^6.0.6",
+ "@tsconfig/recommended": "^1.0.6",
+ "@types/chai": "^4.3.9",
+ "@types/chai-arrays": "^2.0.2",
+ "@types/chai-as-promised": "^7.1.7",
+ "@types/chai-like": "^1.1.2",
+ "@types/chai-string": "^1.4.2",
+ "@types/mocha": "^10.0.5",
+ "@types/node": "^18.19.36",
+ "@types/uuid": "^9.0.8",
+ "@typescript-eslint/eslint-plugin": "^5.60.0",
+ "@typescript-eslint/parser": "^5.60.1",
+ "eslint": "^8.57.0",
+ "eslint-config-prettier": "^8.10.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "prettier": "^2.8.8",
+ "pretty-quick": "^3.3.1",
+ "ts-node": "^10.9.2",
+ "tsconfig-paths": "^4.1.2",
+ "typescript": "^4.9.5"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/spec-failed-reporter.js b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/spec-failed-reporter.js
new file mode 100644
index 00000000..973280e6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/spec-failed-reporter.js
@@ -0,0 +1,47 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+const { execSync } = require('child_process');
+const { Spec } = require('mocha/lib/reporters');
+const fs = require('fs');
+const path = require('path');
+const { isCI } = require('./support/config/environment');
+
+const fileName = 'failed-tests.txt';
+const reportFile = path.resolve(process.cwd(), fileName);
+const failedSpecs = [];
+
+/**
+ * Extending Mocha's custom spec reporter to write failed spec file names to a failed-tests.txt file
+ */
+class CustomSpecReporter extends Spec {
+ constructor(runner, options) {
+ super(runner, options);
+
+ if (isCI() || process.env.GKE === 'true') {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ runner.on('fail', (test, err) => {
+ // create the report file it doesn't exist
+ if (!fs.existsSync(reportFile)) {
+ fs.writeFileSync(reportFile, '');
+ }
+
+ let specFile = test.file.split('kong-api-tests/').pop();
+
+ if (!failedSpecs.includes(specFile)) {
+ console.log(`This failed test will be rerun: ${specFile}`);
+ failedSpecs.push(specFile);
+ try {
+ return execSync(`echo ${specFile} >> failed-tests.txt`, {
+ stdio: 'inherit',
+ });
+ } catch (error) {
+ console.log(
+ `Something went wrong while writing failed test filenames to a file: ${error}`
+ );
+ }
+ }
+ });
+ }
+ }
+}
+
+module.exports = CustomSpecReporter;
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/assert/chai-expect.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/assert/chai-expect.ts
new file mode 100644
index 00000000..652fe7b0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/assert/chai-expect.ts
@@ -0,0 +1,25 @@
+/**
+ * This file helps to configure Assertion handles for automated tests.
+ * Different plugin is used to enhance the capability of chai.
+ * Chai as Promised extends Chai with a fluent language for asserting facts about promises.
+ * chai-string extends the capability of chai to help with common string comparison assertions.
+ *
+ * Read more about :
+ * chai as promised: https://www.chaijs.com/plugins/chai-as-promised/
+ * chai-string: https://www.chaijs.com/plugins/chai-string/
+ * chai-Arrays: https://www.chaijs.com/plugins/chai-arrays/
+ */
+
+import * as chai from 'chai';
+import chaiArrays from 'chai-arrays';
+import chaiAsPromised from 'chai-as-promised';
+import chaiLike from 'chai-like';
+import chaiString from 'chai-string';
+import chaiBytes from 'chai-bytes';
+
+chai.use(chaiAsPromised);
+chai.use(chaiString);
+chai.use(chaiArrays);
+chai.use(chaiLike);
+chai.use(chaiBytes);
+export const expect = chai.expect;
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/assert/jest-expect.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/assert/jest-expect.ts
new file mode 100644
index 00000000..95af9840
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/assert/jest-expect.ts
@@ -0,0 +1,35 @@
+import { expect } from 'expect';
+
+// https://jestjs.io/docs/expect#expectextendmatchers
+expect.extend({
+ toBeTypeOrNull: (actual, classType) => {
+ try {
+ expect(actual).toEqual(expect.any(classType));
+ return {
+ message: () => `Ok`,
+ pass: true,
+ };
+ } catch (error) {
+ return actual === null
+ ? {
+ message: () => `Ok`,
+ pass: true,
+ }
+ : {
+ message: () => `expected ${actual} to be ${classType} type or null`,
+ pass: false,
+ };
+ }
+ },
+});
+
+declare module 'expect' {
+ interface AsymmetricMatchers {
+ toBeTypeOrNull(classType: any): void;
+ }
+ interface Matchers {
+ toBeTypeOrNull(classType: any): R;
+ }
+}
+
+export const jestExpect = expect;
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/auth/kauth-tokens.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/auth/kauth-tokens.ts
new file mode 100644
index 00000000..25a0b4d7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/auth/kauth-tokens.ts
@@ -0,0 +1,93 @@
+import { teams } from '@fixtures';
+import {
+ getAuthOptions as getAuthOptionsUtil,
+ config as configUtil,
+ parseKAuthCookies as parseKAuthCookiesUtil,
+ setAdminAuthTokens
+} from '@kong/kauth-test-utils';
+import { AxiosResponseHeaders } from 'axios';
+
+const ACCESS_TOKENS: { [key: string]: string } = {};
+const REFRESH_TOKENS: { [key: string]: string } = {};
+
+
+const getKAuthCookies = (
+ teamName: string
+): {
+ access: string;
+ refresh: string;
+} => {
+ const access = ACCESS_TOKENS[teamName];
+ const refresh = REFRESH_TOKENS[teamName];
+ if (access == null) {
+ throw new Error(`No auth access token found for the team: ${teamName}`);
+ }
+ if (refresh == null) {
+ throw new Error(`No auth refresh token found for the team: ${teamName}`);
+ }
+ return {
+ access,
+ refresh,
+ };
+};
+
+/**
+ * Set the KAuth token cookies from the given access/refresh tokens for the target team
+ * @param {string} access access token
+ * @param {string} refresh refresh token
+ * @param {string} teamName organization team name
+ * @param {boolean} isPortal option to use portal naming
+ */
+export const setKAuthCookies = (
+ access: string,
+ refresh: string,
+ teamName: string,
+ isPortal = false
+) => {
+ if (isPortal) {
+ access = `${configUtil.constants.PORTAL_ACCESS_TOKEN}=${access}`;
+ refresh = `${configUtil.constants.PORTAL_REFRESH_TOKEN}=${refresh}`;
+ } else {
+ // set admin tokens in utils for functions that use them. TODO -> migration in progress...
+ if (teamName === teams.DefaultTeamNames.ORGANIZATION_ADMIN) {
+ setAdminAuthTokens(access, refresh);
+ }
+ access = `${configUtil.constants.KONNECT_ACCESS_TOKEN}=${access}`;
+ refresh = `${configUtil.constants.KONNECT_REFRESH_TOKEN}=${refresh}`;
+ }
+ ACCESS_TOKENS[teamName] = access;
+ REFRESH_TOKENS[teamName] = refresh;
+};
+
+/**
+ * Get the request options with the auth cookie headers set for the target team
+ * @param {string} teamName organization team name
+ * @returns {any} request auth options
+ */
+export const getAuthOptions = (
+ teamName: string = teams.DefaultTeamNames.ORGANIZATION_ADMIN
+): any => {
+ const { access, refresh } = getKAuthCookies(teamName);
+ return getAuthOptionsUtil(access, refresh);
+};
+
+
+/**
+ * Set the KAuth token cookies from the authenticate response headers for the target team
+ * @param {AxiosResponseHeaders} responseHeaders authenticate response headers
+ * @param {string} teamName organization team name
+ * @param {boolean} isPortal option to use portal naming
+ * @returns {{ [key: string]: string }} access/refresh token cookies
+ */
+export const parseKAuthCookies = (
+ responseHeaders: AxiosResponseHeaders,
+ teamName: string,
+ isPortal = false
+): {
+ access: string;
+ refresh: string;
+} => {
+ const { access, refresh } = parseKAuthCookiesUtil(responseHeaders, isPortal);
+ setKAuthCookies(access, refresh, teamName, isPortal);
+ return getKAuthCookies(teamName);
+};
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/auth/openid-connect.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/auth/openid-connect.ts
new file mode 100644
index 00000000..ab52dc39
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/auth/openid-connect.ts
@@ -0,0 +1,33 @@
+
+import jwt from 'jsonwebtoken'
+import crypto from 'crypto'
+import { authDetails } from '@fixtures'
+
+const generateDpopProof = async ({time: timestamp, nonce: nonce, token: token, url: url, jti: jti}) => {
+ const header = {
+ alg: 'RS256',
+ typ: 'dpop+jwt',
+ jwk: authDetails.dpop.public_jwk,
+ }
+ const payload = {
+ htu: url,
+ htm: 'POST',
+ jti: jti,
+ iat: timestamp,
+ }
+ if (nonce) {
+ payload['nonce'] = nonce
+ }
+ if (token) {
+ // access token -> s256 hash -> base64 encode -> remove padding -> url encode -> ath claim
+ payload['ath'] = await crypto.createHash('sha256')
+ .update(token)
+ .digest('base64')
+ .split('=')[0]
+ .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
+ }
+ const webtoken = jwt.sign(payload, authDetails.dpop.private_pem, {header: header})
+ return webtoken
+}
+
+export { generateDpopProof }
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/api-config.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/api-config.ts
new file mode 100644
index 00000000..cce27efb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/api-config.ts
@@ -0,0 +1,33 @@
+import { Configuration as KAuthConfiguration } from '@kong/kauth-client-typescript-axios';
+import { Configuration as KonnectConfiguration } from '@kong/khcp-api-client';
+
+import {
+ App,
+ getApp,
+ getBasePath,
+ isPreview,
+} from './environment';
+
+const API_CONFIG = {
+ konnect: (): KonnectConfiguration => {
+ const basePath = getBasePath({ app: App.konnect });
+ return new KonnectConfiguration({
+ basePath,
+ baseOptions: { validateStatus: false },
+ });
+ },
+};
+
+/**
+ * Get the API config for the target app
+ * @param {string} app optional target app (default from env)
+ * @returns app API config
+ */
+export const getApiConfig = (
+ app: string = getApp(),
+ preview: boolean = isPreview()
+):
+ | KAuthConfiguration
+ | KonnectConfiguration => {
+ return API_CONFIG[app]({ preview });
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/constants.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/constants.ts
new file mode 100644
index 00000000..90ffd911
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/constants.ts
@@ -0,0 +1,21 @@
+/**
+ * Enum of available constants
+ */
+export const constants = Object.freeze({
+ gateway: {
+ ADMIN_AUTH_HEADER: 'Kong-Admin-Token',
+ ADMIN_PASSWORD: 'handyshake',
+ },
+ kauth: {
+ BASE_USER: {
+ email: 'quality@konghq.com',
+ fullName:
+ 'Quality Quality Quality Quality Quality Engineering Engineering Engineering Engineering',
+ organization:
+ 'Kong Quality Engineering Kong Quality Engineering Kong Quality Engineering Kong Quality Engineering Kong Quality Engineering Kong Quality Engineering Kong Quality Engineering Kong Quality Engineering',
+ },
+ GATEWAY_USER: {
+ email: 'quality+gatewaykonnect@konghq.com'
+ }
+ }
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/environment.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/environment.ts
new file mode 100644
index 00000000..4337ad4d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/environment.ts
@@ -0,0 +1,304 @@
+import { getGatewayHost } from './gateway-vars';
+import { getApiGeo } from './geos';
+import { getRuntimeGroupId } from '../entities/runtimes';
+
+/**
+ * Enum of available envs
+ */
+export enum Env {
+ dev = 'dev',
+ prod = 'prod',
+}
+
+/**
+ * Enum of available apps
+ */
+export enum App {
+ gateway = 'gateway',
+ koko = 'koko',
+ kauth = 'kauth',
+ kauth_v2 = 'kauth_v2',
+ kauth_v3 = 'kauth_v3',
+ konnect = 'konnect',
+ konnect_v2 = 'konnect_v2',
+ servicehub = 'servicehub',
+ kadmin = 'kadmin'
+}
+
+/**
+ * Enum of available protocols
+ */
+export enum Protocol {
+ rest = 'rest',
+ grpc = 'grpc',
+}
+
+/**
+ * Enum of available environments
+ */
+export const Environment = Object.freeze({
+ gateway: {
+ admin: 'admin',
+ adminSec: 'adminSec',
+ proxy: 'proxy',
+ proxy2: 'proxy2',
+ proxySec: 'proxySec',
+ ec2host: 'ec2host',
+ hostName: 'hostName',
+ wsProxy: 'wsProxy',
+ wssProxy: 'wssProxy',
+ keycloak: 'keycloak',
+ keycloakSec: 'keycloakSec',
+ ec2TestServer: 'ec2TestServer',
+ },
+ koko: {
+ dev: 'dev',
+ prod: 'prod',
+ },
+ kauth: {
+ local: 'local',
+ dev: 'dev',
+ prod: 'prod',
+ },
+ konnect: {
+ local: 'local',
+ dev: 'dev',
+ prod: 'prod',
+ },
+ konnect_v2: {
+ dev: 'dev',
+ prod: 'prod',
+ },
+ servicehub: {
+ dev: 'dev',
+ prod: 'prod',
+ },
+ kadmin: {
+ dev: 'dev',
+ prod: 'prod',
+ },
+});
+
+/**
+ * Object of available base paths
+ */
+const getPaths = (geo = getApiGeo()) => {
+ return {
+ gateway: {
+ admin: `http://${getGatewayHost()}:8001`,
+ adminSec: `https://${getGatewayHost()}:8444`,
+ status: `https://${getGatewayHost()}:8100`,
+ statusDP: `https://${getGatewayHost()}:8101`,
+ proxy: `http://${getGatewayHost()}:8000`,
+ proxySec: `https://${getGatewayHost()}:8443`,
+ proxy2: `http://${getGatewayHost()}:8010`,
+ proxySec2: `https://${getGatewayHost()}:8453`,
+ wsProxy: `ws://${getGatewayHost()}:8000`,
+ wssProxy: `wss://${getGatewayHost()}:8443`,
+ keycloak: `http://${getGatewayHost()}:8080`,
+ keycloakSec: `https://${getGatewayHost()}:8543`,
+ ec2host: 'ec2-18-117-8-125.us-east-2.compue.amazonaws.com',
+ ec2TestServer: '18.117.9.215',
+ hostName: getGatewayHost(),
+ },
+ koko: {
+ dev: `https://${geo}.api.konghq.tech/konnect-api/api/runtime_groups/${getRuntimeGroupId()}`,
+ prod: `https://${geo}.api.konghq.com/konnect-api/api/runtime_groups/${getRuntimeGroupId()}`,
+ },
+ kauth: {
+ dev: 'https://global.api.konghq.tech/kauth',
+ prod: 'https://global.api.konghq.com/kauth',
+ dev_preview: 'https://global.api.konghq.tech/kauth-preview',
+ prod_preview: 'https://global.api.konghq.com/kauth-preview',
+ },
+ kauth_v2: {
+ dev: 'https://global.api.konghq.tech/v2',
+ prod: 'https://global.api.konghq.com/v2',
+ dev_preview: 'https://global.api.konghq.tech/kauth-preview/v2',
+ prod_preview: 'https://global.api.konghq.com/kauth-preview/v2',
+ },
+ kauth_v3: {
+ dev: 'https://global.api.konghq.tech/v3',
+ prod: 'https://global.api.konghq.com/v3',
+ dev_preview: 'https://global.api.konghq.tech/kauth-preview/v3',
+ prod_preview: 'https://global.api.konghq.com/kauth-preview/v3',
+ },
+ konnect: {
+ dev: `https://${geo}.api.konghq.tech/konnect-api`,
+ prod: `https://${geo}.api.konghq.com/konnect-api`,
+ },
+ konnect_v2: {
+ dev: `https://${geo}.api.konghq.tech/v2`,
+ prod: `https://${geo}.api.konghq.com/v2`,
+ },
+ servicehub: {
+ dev: `https://${geo}.api.konghq.tech/servicehub/v1`,
+ prod: `https://${geo}.api.konghq.com/servicehub/v1`,
+ },
+ kadmin: {
+ dev: `https://${geo}.kadmin.admin.konghq.tech`,
+ prod: `https://${geo}.kadmin.admin.konghq.com`,
+ },
+ };
+};
+
+/**
+ * Get the current app under test (if configured)
+ * @param {string | undefined} app current app to check for
+ * @returns {string} current app
+ */
+export const getApp = (app: string | undefined = process.env.TEST_APP): string => {
+ if (!app || !(app in App)) {
+ throw new Error(
+ `App '${app}' does not exist or was not provided. Use 'export TEST_APP='`
+ );
+ }
+ return app;
+};
+
+/**
+ * Get the current primary protocol under test (if supported)
+ * @returns {string} current primary protocol
+ */
+export const getProtocol = (): string => {
+ let protocol = process.env.TEST_PROTOCOL || '';
+ if (!protocol) {
+ protocol = Protocol.rest;
+ }
+ if (!(protocol in Protocol)) {
+ throw new Error(`Protocol '${protocol}' is not currently supported`);
+ }
+ return protocol;
+};
+
+/**
+ * Get the current app environment (if configured)
+ * @param {string | undefined} app current app to use
+ * @param {string | undefined} environment current environment to check for
+ * @returns {string} app environment
+ */
+export const getEnvironment = (
+ app: string | undefined = getApp(),
+ environment: string | undefined = process.env.TEST_ENV
+): string => {
+ if (
+ !environment ||
+ !(app in Environment) ||
+ !(environment in Environment[app])
+ ) {
+ throw new Error(
+ `Environment '${environment}' does not exist or was not provided. Use 'export TEST_ENV='`
+ );
+ }
+ return environment;
+};
+
+/**
+ * Get the base path for the current environment of app under test
+ * @param {string | undefined} options.app current app
+ * @param {string | undefined} options.environment current environment
+ */
+export const getBasePath = (
+ options: { app?: string | undefined; environment?: string | undefined } = {}
+): string => {
+ const app = getApp(options.app);
+ const environment = getEnvironment(app, options.environment);
+ return getPaths()[app][environment];
+};
+
+/**
+ * Get the base path for a certain endpoint in the gateway test envronment
+ */
+
+export const getGatewayBasePath = (key: string): string =>
+ getPaths()['gateway'][key];
+
+/**
+ * Check if the current test run environment is CI
+ * @returns {boolean}- true or false
+ */
+export const isCI = (): boolean => {
+ return process.env.CI === 'true' ? true : false;
+};
+
+/**
+ * Check if the current app environment matches the target
+ * @param {string} environment target to match
+ * @returns {boolean} if matched - true; else - false
+ */
+export const isEnvironment = (environment: string): boolean => {
+ return getEnvironment() === environment;
+};
+
+/**
+ * Check if the current app environment is localhost
+ * @param {string} app current app to use
+ * @returns {boolean} if localhost - true; else - false
+ */
+export const isLocal = (app: string = getApp()): boolean => {
+ return isEnvironment(Environment[app].local);
+};
+
+/**
+ * Check if the current app is Gateway
+ * @returns {boolean} if Gateway - true; else - false
+ */
+export const isGateway = (): boolean => {
+ return getApp() === App.gateway;
+};
+
+/**
+ * Check if the current app is Koko
+ * @returns {boolean} if Koko - true; else - false
+ */
+export const isKoko = (): boolean => {
+ return getApp() === App.koko;
+};
+
+/**
+ * Check if the current app is KAuth
+ * @returns {boolean} if KAuth - true; else - false
+ */
+export const isKAuth = (): boolean => {
+ return getApp() === App.kauth;
+};
+
+/**
+ * Check if the current app is KAuth v2
+ * @returns {boolean} if KAuth v2 - true; else - false
+ */
+export const isKAuthV2 = (): boolean => {
+ return getApp() === App.kauth_v2;
+};
+
+/**
+ * Check if the current app is KAuth v3
+ * @returns {boolean} if KAuth v3 - true; else - false
+ */
+export const isKAuthV3 = (): boolean => {
+ return getApp() === App.kauth_v3;
+};
+
+/**
+ * Use preview endpoints (if configured)
+ * @returns {boolean} preview
+ */
+export const isPreview = (): boolean => {
+ return process.env.TEST_PREVIEW === 'true';
+};
+
+/**
+ * Check if the current protocol is gRPC
+ * @returns {boolean} if gRPC - true; else - false
+ */
+export const isGRPC = (): boolean => {
+ return getProtocol() === Protocol.grpc;
+};
+
+/**
+ * Check if the current protocol is REST
+ * @returns {boolean} if REST - true; else - false
+ */
+export const isREST = (): boolean => {
+ return getProtocol() === Protocol.rest;
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/gateway-vars.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/gateway-vars.ts
new file mode 100644
index 00000000..91644789
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/gateway-vars.ts
@@ -0,0 +1,133 @@
+import { constants } from './constants';
+import { execSync } from 'child_process';
+
+export const vars = {
+ aws: {
+ AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
+ AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
+ },
+ azure: {
+ AZURE_FUNCTION_KEY: process.env.AZURE_FUNCTION_KEY
+ },
+ app_dynamics: {
+ APPD_PASSWORD: process.env.APPD_PASSWORD,
+ },
+};
+/**
+ * Check that all necessary environment variables are defined before test execution
+ * @param {string} scope - narrow down the scope to a specific set of variables e.g. azure or aws
+ */
+export const checkGwVars = (scope) => {
+ const missingVars: string[] = [];
+ for (const envVar in vars[scope]) {
+ if (!vars[scope][envVar]) {
+ missingVars.push(envVar);
+ }
+ }
+ if (missingVars.length > 0) {
+ throw new Error(
+ `required gateway environment secrets not found: ${missingVars.join(
+ ', '
+ )}`
+ );
+ }
+};
+
+/**
+ * Get current gateway host
+ * @returns {string} - current gateway host
+ */
+export const getGatewayHost = (): string => {
+ return process.env.GW_HOST || 'localhost';
+};
+
+/**
+ * Check if current database is running in local mode
+ * @returns {boolean} - true if the database is running in local mode else false
+ */
+export const isLocalDatabase = (): boolean => {
+ return process.env.PG_IAM_AUTH == 'true' ? false : true;
+};
+
+/**
+ * Get current gateway mode
+ * @returns {string} - current gateway mode
+ */
+export const getGatewayMode = (): string => {
+ return process.env.GW_MODE || 'classic';
+};
+
+/**
+ * Check if current gateway mode is hybrid
+ * @returns {boolean} - true if gateway runs in hybrid mode else false
+ */
+export const isGwHybrid = (): boolean => {
+ return getGatewayMode() === 'hybrid' ? true : false;
+};
+
+/**
+ * Check if gateway is installed natively (package tests)
+ * @returns {boolean} - true if gateway is installed using a package
+ */
+export const isGwNative = (): boolean => {
+ return process.env.KONG_PACKAGE ? true : false;
+};
+
+/**
+ * Check if tests are runing for custom plugins
+ * @returns {string}
+ */
+export const isCustomPlugin = (): string => {
+ return process.env.CUSTOM_PLUGIN ? process.env.CUSTOM_PLUGIN : 'false'
+}
+
+/**
+ * Get running kong container name based on which test suite is running
+ * @returns {string} - the name of the container
+ */
+export const getKongContainerName = (): string => {
+ return process.env.KONG_PACKAGE ? process.env.KONG_PACKAGE : 'kong-cp';
+};
+
+/**
+ * Get kong version
+ * @returns {string} - the name of the container
+ */
+export const getKongVersion = (): string | undefined => {
+ return process.env.KONG_VERSION;
+};
+
+/**
+ * Get the target docker image for Konnect data plane, default is konnect-dp1
+ * @returns {string}
+ */
+export const getDataPlaneDockerImage = (): string | undefined => {
+ return process.env.KONNECT_DP_IMAGE ? process.env.KONNECT_DP_IMAGE : 'kong/kong-gateway-dev:nightly-ubuntu'
+}
+
+/**
+ * Get the Control Pane Docker image name from GW_IMAGE
+ * @returns {string}
+ */
+export const getControlPlaneDockerImage = (): string => {
+ if(process.env.GW_IMAGE) {
+ return process.env.GW_IMAGE
+ } else {
+ try{
+ return execSync(`docker ps --filter "name=kong-cp" --format '{{.Image}}'`).toString().trim();
+ } catch(e) {
+ console.error(`Error getting control plane docker image: ${e}`)
+ return ''
+ }
+ }
+}
+
+/**
+ * Checks if GATEWAY_PASSWORD env var is set to return respective Auth header key:value
+ */
+export const gatewayAuthHeader = () => {
+ return {
+ authHeaderKey: constants.gateway.ADMIN_AUTH_HEADER,
+ authHeaderValue: constants.gateway.ADMIN_PASSWORD,
+ };
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/geos.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/geos.ts
new file mode 100644
index 00000000..73c9dd8c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/config/geos.ts
@@ -0,0 +1,17 @@
+/**
+ * Enum of available geos
+ */
+export enum Geo {
+ us = 'us',
+ eu = 'eu',
+ au = 'au',
+ global = 'global',
+}
+
+/**
+ * Get the API Geo (us/eu/au) env var (Konnect Apps)
+ * @returns {string} us/eu
+ */
+export const getApiGeo = (): Geo => {
+ return (process.env.TEST_API_GEO || Geo.us) as Geo;
+};
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/konnect-cp.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/konnect-cp.ts
new file mode 100644
index 00000000..4574fff5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/konnect-cp.ts
@@ -0,0 +1,17 @@
+let KONNECT_CP_ID = ''
+
+/**
+ * Set the Konnect Control Plane ID for the current run
+ * @param {string} konnectCpId - konnect control Plane id
+ */
+export const setKonnectControlPlaneId = (konnectCpId: string | undefined): void => {
+ KONNECT_CP_ID = konnectCpId || '';
+};
+
+/**
+ * Get the Konnect Control Plane ID for the current run
+ * @returns {string} - konnect control plane id
+ */
+export const getKonnectControlPlaneId = (): string => {
+ return KONNECT_CP_ID;
+};
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/organization.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/organization.ts
new file mode 100644
index 00000000..67f3a382
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/organization.ts
@@ -0,0 +1,57 @@
+import { constants } from "../config/constants";
+
+let ORG_NAME = ''
+let ORG_ID = ''
+let ORG_CREATED = false;
+
+/**
+ * Set the organization name for the current run
+ * @param {string} orgName organization name
+ */
+export const setOrgName = (orgName: string): void => {
+ ORG_NAME = orgName;
+};
+
+/**
+ * Get the organization name for the current run
+ * @param {boolean} isKonnect option to use short name for konnect
+ * @returns {string} organization name
+ */
+export const getOrgName = (isKonnect = false): string => {
+ return (
+ ORG_NAME ||
+ (isKonnect ? 'Quality Engineering' : constants.kauth.BASE_USER.organization)
+ );
+};
+
+/**
+ * Set the organization ID for the current run
+ * @param {string} orgId organization id
+ */
+export const setOrgId = (orgId: string | undefined): void => {
+ ORG_ID = orgId || '';
+};
+
+/**
+ * Get the organization ID for the current run
+ * @returns {string} organization id
+ */
+export const getOrgId = (): string => {
+ return ORG_ID;
+};
+
+/**
+ * Set the organization created flag for cleanup
+ * @param {string} wasCreated whether the set org was created
+ */
+export const setOrgCreated = (wasCreated: boolean): void => {
+ ORG_CREATED = wasCreated;
+};
+
+/**
+ * Get whether the organization was created for cleanup
+ * @returns {boolean} whether the set org was created
+ */
+export const wasOrgCreated = (): boolean => {
+ return ORG_CREATED;
+};
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/runtimes.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/runtimes.ts
new file mode 100644
index 00000000..de5c8138
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/runtimes.ts
@@ -0,0 +1,25 @@
+const RUNTIME_GROUP_IDS: { [key: string]: string } = {};
+
+/**
+ * Set the runtime group ID for the current run
+ * @param {string} runtimeGroupId runtime group id
+ * @param {string} runtimeGroupName runtime group name (default: default)
+ */
+export const setRuntimeGroupId = (
+ runtimeGroupId: string,
+ runtimeGroupName = 'default'
+): void => {
+ RUNTIME_GROUP_IDS[runtimeGroupName] = runtimeGroupId;
+};
+
+/**
+ * Get the runtime group ID for the current run
+ * @param {string} runtimeGroupName runtime group name (default: default)
+ * @returns {string} runtime group id
+ */
+export const getRuntimeGroupId = (runtimeGroupName = 'default'): string => {
+ if (runtimeGroupName in RUNTIME_GROUP_IDS) {
+ return RUNTIME_GROUP_IDS[runtimeGroupName];
+ }
+ return '';
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/user.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/user.ts
new file mode 100644
index 00000000..71341040
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/entities/user.ts
@@ -0,0 +1,110 @@
+import { v4 as uuidv4, validate as uuidValidate } from 'uuid';
+import { teams } from '@fixtures';
+import { constants } from '@support';
+
+let EMAIL = '';
+let PASSWORD = '';
+let EXISTING = false;
+const FULL_NAMES: { [key: string]: string } = {};
+
+/**
+ * Add a UUID as an alias to the base email and return both
+ * @param {string} baseEmail current base email
+ * @returns {{ email: string; password: string }} email alias and password
+ */
+export const createUuidEmail = (
+ baseEmail: string
+): {
+ email: string;
+ password: string;
+} => {
+ const uuid = uuidv4().toUpperCase();
+ const emailParts = baseEmail.split('@');
+ const email = `${emailParts[0]}+${uuid}@${emailParts[1]}`;
+ EMAIL = email;
+ PASSWORD = uuid;
+ return { email, password: uuid };
+};
+
+/**
+ * Set the optional quality base user email and password
+ * @param {string} email base quality user email
+ */
+export const setQualityBaseUser = (email: string) => {
+ const emailParts = email.split('@');
+ const password = emailParts[0].split('+')[1];
+ if (uuidValidate(password)) {
+ EMAIL = email;
+ PASSWORD = password;
+ EXISTING = true;
+ } else {
+ throw new Error(
+ 'Quality Base User does not match the quality+@konghq.com pattern'
+ );
+ }
+};
+
+/**
+ * Get the base user email and password
+ * @returns {{ email: string; password: string; existing: boolean }} base user email and password
+ */
+export const getBaseUserCredentials = (): {
+ email: string;
+ password: string;
+ existing: boolean;
+} => {
+ return { email: EMAIL, password: PASSWORD, existing: EXISTING };
+};
+
+/**
+ * Get the aliased email for the target default team
+ * @param {string} team target default team
+ * @returns {{ teamEmail: string; password: string }} email alias and password
+ */
+export const getTeamUser = (
+ team: string
+): {
+ teamEmail: string;
+ password: string;
+} => {
+ const emailParts = EMAIL.split('@');
+ let teamEmail = `${emailParts[0]}+${team}`.substring(0, 64);
+ teamEmail = `${teamEmail}@${emailParts[1]}`;
+ return { teamEmail, password: PASSWORD };
+};
+
+/**
+ * Set the user full name for the given default team
+ * @param {string} team target default team
+ * @param {string} fullName team user full name
+ */
+export const setTeamFullName = (team: string, fullName: string): void => {
+ FULL_NAMES[team] = fullName;
+};
+
+/**
+ * Get the user full name of the given default team
+ * @param {string} team target default team
+ * @returns {string} team user full name
+ */
+export const getTeamFullName = (team: string): string => {
+ return FULL_NAMES[team] || `Quality ${team}`;
+};
+
+/**
+ * Get the AUTH0 konnect admin credentials from the env vars
+ * @returns {Credentials} username, password
+ */
+export const getAuth0UserCreds = () => {
+ const username = constants.kauth.GATEWAY_USER.email;
+ const password = process.env.KONNECT_USER_PASSWORD;
+
+ if (!password) {
+ throw new Error('No KONNECT_USER_PASSWORD env var found, please set the variable to authenticate with Konnect');
+ }
+ EMAIL = username;
+ PASSWORD = password;
+ // Auth0 full name for a new org is set to the username
+ FULL_NAMES[teams.DefaultTeamNames.ORGANIZATION_ADMIN] = username;
+ return { username, password };
+};
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/certificates.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/certificates.ts
new file mode 100644
index 00000000..6fbe2192
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/certificates.ts
@@ -0,0 +1,52 @@
+import { execSync } from 'child_process';
+import * as fs from 'fs';
+import * as path from 'path';
+
+/**
+ * Generate public and private certificates
+ */
+export const generatePublicPrivateCertificates = async () => {
+ try {
+ execSync(`openssl genrsa -out private.pem 2048`, { stdio: 'inherit' });
+ execSync(`openssl rsa -in private.pem -outform PEM -pubout -out public.pem`, { stdio: 'inherit' });
+ execSync(`openssl req -new -key private.pem -out certificate.csr -subj "/C=US/ST=State/L=Toronto/O=Kong/OU=Gateway/CN=Kong"`, { stdio: 'inherit' });
+ execSync(`openssl x509 -req -days 3650 -in certificate.csr -signkey private.pem -out certificate.crt`, { stdio: 'inherit' });
+ } catch (error) {
+ console.error('Something went wrong while generating ssl certificates', error);
+ }
+};
+
+export const removeCertficatesAndKeys = () => {
+ const privateKey = path.resolve(process.cwd(), 'private.pem');
+ const publicKey = path.resolve(process.cwd(), 'public.pem');
+ const csrCert = path.resolve(process.cwd(), 'certificate.csr');
+ const cert = path.resolve(process.cwd(), 'certificate.crt');
+
+ const files = [privateKey, publicKey, csrCert, cert];
+
+ files.forEach(file => {
+ try{
+ if (fs.existsSync(file)) {
+ fs.unlinkSync(file);
+ console.log(`\nSuccessfully removed target file: ${file.split('/').pop()}`);
+ }
+ } catch (error) {
+ console.error('Something went wrong while removing certificate files', error);
+ }
+ })
+}
+
+/**
+ * Reads the target file and returns its contents
+ * @returns {string}
+ */
+export const getTargetFileContent = (filename: string) => {
+ const file = path.resolve(process.cwd(), filename);
+
+ if (fs.existsSync(file)) {
+ console.log("exists")
+ return fs.readFileSync(file, 'utf8');
+ } else {
+ console.error(`Couldn't read the given file at ${file}`);
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/gateway-container.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/gateway-container.ts
new file mode 100644
index 00000000..77fa55b2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/gateway-container.ts
@@ -0,0 +1,255 @@
+import { execSync } from 'child_process';
+import * as fs from 'fs';
+import * as path from 'path';
+import { getKongContainerName, isGwNative } from 'support/config/gateway-vars';
+import axios from 'axios';
+import { expect } from 'chai';
+import { eventually, getGatewayBasePath } from 'support';
+import { wait } from 'support/utilities/random';
+
+/**
+ * Sets Kong Gateway target container variables
+ * @param {object} targetEnvironmentVariables - {KONG_PORTAL: 'on', KONG_VITALS: 'off'}
+ * @param {string} containerName - target docker kong container name, default is 'kong-cp'
+ */
+export const resetGatewayContainerEnvVariable = async (
+ targetEnvironmentVariables: object,
+ containerName: string
+) => {
+ const isKongNative = isGwNative();
+ const newVars: any = [];
+ let restartCommand;
+
+ for (const envVar in targetEnvironmentVariables) {
+ const modifiedVar = `-e ${envVar}=${targetEnvironmentVariables[envVar]}`;
+ newVars.push(modifiedVar);
+ }
+
+ const finalVars = newVars.join(' ');
+
+ if (isKongNative) {
+ restartCommand =
+ containerName === 'kong-dp1'
+ ? `kong restart -c kong-dp.conf`
+ : `kong restart -c kong.conf`;
+ } else {
+ restartCommand = 'kong reload';
+ }
+
+ try {
+ execSync(
+ `kongVars="${finalVars}" command="${restartCommand}" make gwContainerName=${containerName} update_kong_container_env_var`,
+ { stdio: 'inherit' }
+ );
+ } catch (error) {
+ throw new Error(
+ `Something went wrong during updating the container environment variable: ${error}`
+ );
+ }
+
+ // wait before test to prevent the case where we are checking before any worker exits
+ await wait(1000) // eslint-disable-line no-restricted-syntax
+
+ await eventually(async () => {
+ const base_url = containerName === 'kong-dp1' ? getGatewayBasePath('statusDP') : getGatewayBasePath('status')
+ const resp = await axios.get(`${base_url}/status/ready`)
+
+ expect(resp.status, 'Kong Gateway timed out, restarting').to.equal(200)
+ expect(resp.data.message, 'Message should be ready').to.equal('ready')
+ });
+};
+
+/**
+ * Reload gateway
+ * @param {string} containerName - target docker kong container name, default is 'kong-cp'
+ */
+export const reloadGateway = (
+ containerName: string = getKongContainerName()
+) => {
+ const command = 'kong reload'
+
+ try {
+ return execSync(
+ `docker exec $(docker ps -aqf name=${containerName}) ${command}`,
+ { stdio: 'inherit' }
+ );
+ } catch (error) {
+ console.log(
+ `Something went wrong during reloading the gateway: ${error}`
+ );
+ }
+};
+
+/**
+ * Stops Gateway in CI and starts it with a custom env variable
+ * @param {string} targetEnvironmentVariables - space separated env variables e.g. `GW_ENTERPRISE=false PG_SSL=true`
+ */
+export const startGwWithCustomEnvVars = (
+ targetEnvironmentVariables: string
+) => {
+ try {
+ return execSync(
+ `make envVars="${targetEnvironmentVariables}" custom_start_gw`,
+ { stdio: 'inherit' }
+ );
+ } catch (error) {
+ console.log(
+ `Something went wrong while restarting gateway with custom environment variables: ${error}`
+ );
+ }
+};
+
+/**
+ * Reads given kong container logs
+ * @param {string} containerName - target docker kong container name
+ * @param {number} numberOfLinesToRead - the number of lines to read from logs
+ */
+export const getGatewayContainerLogs = (
+ containerName,
+ numberOfLinesToRead = 4
+) => {
+ const isKongNative = isGwNative();
+ const logFile = path.resolve(process.cwd(), 'error.log');
+
+ // using | cat as simple redirection like &> or >& doesn't work in CI Ubuntu
+ const command = isKongNative
+ ? `docker cp "${containerName}":/var/error.log ${logFile}`
+ : `docker logs $(docker ps -aqf name="${containerName}") --tail ${numberOfLinesToRead} 2>&1 | cat > error.log`;
+
+ try {
+ execSync(command);
+ const logs = execSync(`tail -n ${numberOfLinesToRead} ${logFile}`).toString();
+ console.log(`Printing current log slice of kong container: \n${logs}`);
+
+ // remove logs file
+ if (fs.existsSync(logFile)) {
+ fs.unlinkSync(logFile);
+ console.log(`\nSuccessfully removed target file: 'error.log'`);
+ }
+
+ return logs;
+ } catch (error) {
+ console.log('Something went wrong while reading the container logs');
+ }
+};
+
+/**
+ * Get the kong version from running docker container
+ * @param {string} containerName
+ * @returns {string}
+ */
+export const getKongVersionFromContainer = (containerName = 'kong-cp') => {
+ const containers = execSync(`docker ps --format '{{.Names}}'`).toString();
+ if (!containers.includes(containerName)) {
+ throw new Error(
+ `The docker container with name ${containerName} was not found`
+ );
+ }
+
+ try {
+ const version = execSync(
+ `docker exec ${containerName} /bin/bash -c "kong version"`,
+ { stdio: ['inherit', 'pipe', 'pipe'] }
+ );
+
+ return version.toString().trim();
+ } catch (error) {
+ throw new Error(
+ `Something went wrong while getting kong container version: ${error}`
+ );
+ }
+};
+
+/**
+ * Run docker start on db container
+ * @param {string} containerName - name of the container to start
+ * @param {string} command - command to run, can be either stop or start
+ */
+export const runDockerContainerCommand = async (containerName, command) => {
+ // stop docker postgres database container
+ const result = await execSync(`docker ${command} ${containerName}`);
+ return result.toString('utf-8');
+};
+
+/**
+ * Generates code snippet and deploys a Konnect Data Plane via Docker in the same network as other test 3rd party services
+ * @param {string} controlPlaneEndpoint - Konnect control_plane_endpoint
+ * @param {string} telemetryEndpoint - Konnect telemetry_endpoint
+ * @param {string} cert - the generated certificate file
+ * @param {string} privateKey - the generated private key file
+ * @param {string} gatewayDpImage - target gateway image for the data plane
+ * @param {string} targetOS- Options are: 'docker' - default, macosintel, macosarm
+ * @param {number} dataPlaneCount - number of data planes to deploy, default is 1
+ */
+export const deployKonnectDataPlane = (controlPlaneEndpoint, telemetryEndpoint, cert, privateKey, gatewayDpImage, targetOS = 'docker', dataPlaneCount = 1) => {
+ let osConfig: string
+ let dockerNetwork: string
+
+ // Define Platform as in Konnect Platform dropdown menu
+ if (targetOS === 'macosintel') {
+ osConfig = 'macOsIntelOS'
+ } else if (targetOS === 'macosarm') {
+ osConfig = 'macOsArmOS'
+ } else {
+ osConfig = 'linuxdockerOS'
+ }
+
+ const staticInstructions = `-e "KONG_ROLE=data_plane" \
+ -e "KONG_DATABASE=off" \
+ -e "KONG_VITALS=off" \
+ -e "KONG_CLUSTER_MTLS=pki" \
+ -e "KONG_CLUSTER_CONTROL_PLANE=${controlPlaneEndpoint}:443" \
+ -e "KONG_CLUSTER_SERVER_NAME=${controlPlaneEndpoint}" \
+ -e "KONG_CLUSTER_TELEMETRY_ENDPOINT=${telemetryEndpoint}:443" \
+ -e "KONG_CLUSTER_TELEMETRY_SERVER_NAME=${telemetryEndpoint}" \
+ -e "KONG_CLUSTER_CERT=${cert}" \
+ -e "KONG_CLUSTER_CERT_KEY=${privateKey}" \
+ -e "KONG_LUA_SSL_TRUSTED_CERTIFICATE=system" \
+ -e "KONG_KONNECT_MODE=on" \
+ -e "KONG_CLUSTER_DP_LABELS=created-by:quickstart,type:docker-${osConfig}"`
+
+ // if the target test network exists, create cdp container in that network
+ try {
+ execSync(`docker network ls | grep 'gateway-docker-compose-generator_kong-ee-net'`);
+ dockerNetwork = '--net gateway-docker-compose-generator_kong-ee-net'
+ } catch (error) {
+ dockerNetwork = ''
+ }
+
+ for(let i = 1; i <= dataPlaneCount; i++) {
+ const port1 = 8000 + (i-1) * 10
+ const port2 = 8443 + (i-1) * 10
+
+ const dpCodeSnippet = `docker run --name konnect-dp${i} ${dockerNetwork} -d \
+ ${staticInstructions} \
+ -p ${port1}:8000 \
+ -p ${port2}:8443 \
+ ${gatewayDpImage}`
+
+ try {
+ execSync(dpCodeSnippet, { stdio: 'inherit' });
+ console.info(`Successfully deployed the Konnect data plane named: konnect-dp${i} \n`)
+ } catch (error) {
+ console.error('Something went wrong while deploying the Konnect data plane', error);
+ }
+ }
+}
+
+/**
+ * Stops and removes the target container
+ * @param {string} containerName
+ */
+export const stopAndRemoveTargetContainer = (containerName) => {
+ // if konnect-dp1 container exists
+ const doesContainerExists = execSync(`docker ps -a -q -f name=${containerName}`).toString().trim()
+ if (doesContainerExists) {
+ try {
+ execSync(`docker stop ${containerName}; docker rm ${containerName} -f`, { stdio: 'inherit' });
+ console.info(`Successfully removed the ${containerName} docker container`)
+ } catch (error) {
+ console.error(`Something went wrong while removing the ${containerName} docker container`, error);
+ }
+ } else {
+ console.info(`Konnect container ${containerName} doesn't exist, moving on with the rest of the test setup`)
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/gw-ec2.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/gw-ec2.ts
new file mode 100644
index 00000000..a0e3f32d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/exec/gw-ec2.ts
@@ -0,0 +1,41 @@
+import { execSync } from 'child_process';
+import * as fs from 'fs';
+import * as path from 'path';
+
+/**
+ * Stop all active docker services in Gateway EC2 instance
+ */
+export const safeStopGateway = () => {
+ try {
+ return execSync(`make safe_stop_gw`, { stdio: 'inherit' });
+ } catch (error) {
+ console.log('All EC2 Docker services were shut down');
+ }
+};
+
+/**
+ * Pull the nightly master image and Start the Gateway
+ */
+export const startGateway = () => {
+ try {
+ execSync(`make pull_nightly_master`, { stdio: 'inherit' });
+ return execSync(`make start_gw_classic`, { stdio: 'inherit' });
+ } catch (error) {
+ console.log('Classic Mode Gateway was started!');
+ }
+};
+
+/**
+ * Remove secret pem file if that was created during test execution
+ * @param {string} fileName
+ */
+export const removeSecretFile = (fileName) => {
+ const createdSecretFile = path.resolve(process.cwd(), fileName);
+
+ if (fs.existsSync(createdSecretFile)) {
+ fs.unlinkSync(createdSecretFile);
+ console.log(`\nSuccessfully removed target file: ${fileName}`);
+ }
+
+ return;
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/index.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/index.ts
new file mode 100644
index 00000000..9c1bd37a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/index.ts
@@ -0,0 +1,117 @@
+export { expect } from './assert/chai-expect';
+export { jestExpect } from './assert/jest-expect';
+export { constants } from './config/constants';
+export {
+ App,
+ Environment,
+ getApp,
+ getBasePath,
+ getGatewayBasePath,
+ getEnvironment,
+ getProtocol,
+ isCI,
+ isEnvironment,
+ isGateway,
+ isGRPC,
+ isLocal,
+ isREST,
+ Protocol,
+ isKoko,
+ isKAuth,
+ isKAuthV2,
+ isKAuthV3,
+ isPreview,
+ Env
+} from './config/environment';
+export {
+ checkGwVars,
+ gatewayAuthHeader,
+ getGatewayHost,
+ getGatewayMode,
+ isGwHybrid,
+ isLocalDatabase,
+ vars,
+ isGwNative,
+ getKongContainerName,
+ getKongVersion,
+ getDataPlaneDockerImage,
+ isCustomPlugin,
+ getControlPlaneDockerImage
+} from './config/gateway-vars';
+export {
+ createUuidEmail,
+ getBaseUserCredentials,
+ getTeamFullName,
+ getTeamUser,
+ setQualityBaseUser,
+ setTeamFullName,
+ getAuth0UserCreds
+} from './entities/user';
+export {
+ getGatewayContainerLogs,
+ resetGatewayContainerEnvVariable,
+ startGwWithCustomEnvVars,
+ getKongVersionFromContainer,
+ runDockerContainerCommand,
+ deployKonnectDataPlane,
+ stopAndRemoveTargetContainer,
+ reloadGateway
+} from './exec/gateway-container';
+export { removeSecretFile, safeStopGateway, startGateway } from './exec/gw-ec2';
+export {
+ Credentials,
+ ErrorData,
+ GatewayRoute,
+ GatewayService,
+ GrpcConfig,
+ KokoAuthHeaders,
+ Consumer
+} from './interfaces';
+export { constructDeckCommand, read_deck_config } from './utilities/deck';
+export * from './utilities/entities-gateway';
+export * from './utilities/entities-rbac-gateway';
+export * from './utilities/gw-vaults';
+export * from './utilities/influxdb';
+export * from './utilities/jwe-keys';
+export { logDebug, logResponse } from './utilities/logging';
+export { getHttpLogServerLogs, deleteHttpLogServerLogs } from './utilities/http-log-server';
+export { getNegative, postNegative } from './utilities/negative-axios';
+export { execCustomCommand, checkForArm64 } from './utilities/prog';
+export { findRegex, randomString, wait } from './utilities/random';
+export {
+ client,
+ createRedisClient,
+ getAllKeys,
+ getDbSize,
+ getTargetKeyData,
+ resetRedisDB,
+ shutDownRedis,
+ expectRedisFieldsInPlugins
+} from './utilities/redis';
+export { retryRequest } from './utilities/retry-axios';
+export { isValidDate, isValidUrl } from './utilities/validate';
+export {
+ expectStatusReadyEndpointOk,
+ expectStatusReadyEndpoint503,
+ waitForTargetStatus,
+ getClusteringDataPlanes
+} from './utilities/status-endpoint';
+export {
+ getMetric,
+ getSharedDictValue,
+ waitForConfigHashUpdate,
+ waitForDictUpdate,
+ queryPrometheusMetrics,
+ queryAppdynamicsMetrics,
+ getAllMetrics
+} from './utilities/metrics';
+export { eventually } from './utilities/eventually';
+export * from './config/geos';
+export { getRuntimeGroupId, setRuntimeGroupId } from './entities/runtimes'
+export { setKonnectControlPlaneId, getKonnectControlPlaneId } from './entities/konnect-cp'
+export { generateDpopProof } from './auth/openid-connect'
+export { getAuthOptions, setKAuthCookies } from './auth/kauth-tokens'
+export * from './entities/organization'
+export { getApiConfig } from './config/api-config';
+export { generatePublicPrivateCertificates, getTargetFileContent, removeCertficatesAndKeys } from './exec/certificates'
+export { createPolly } from './mocking/polly'
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/consumers.d.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/consumers.d.ts
new file mode 100644
index 00000000..fc21bb35
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/consumers.d.ts
@@ -0,0 +1,7 @@
+export interface Consumer {
+ username: string;
+ custom_id?: string;
+ id?: string;
+ tags?: string[];
+ username_lower?: string;
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/credentials.d.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/credentials.d.ts
new file mode 100644
index 00000000..279aabec
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/credentials.d.ts
@@ -0,0 +1,4 @@
+export interface Credentials {
+ username: string;
+ password: string;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/error-data.d.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/error-data.d.ts
new file mode 100644
index 00000000..03d8b1dd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/error-data.d.ts
@@ -0,0 +1,3 @@
+export interface ErrorData {
+ errors: Array<{ status: string; title: string }>;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/grpc-config.d.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/grpc-config.d.ts
new file mode 100644
index 00000000..09514d05
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/grpc-config.d.ts
@@ -0,0 +1,7 @@
+import { ChannelCredentials, Metadata } from '@grpc/grpc-js';
+
+export interface GrpcConfig {
+ address: string;
+ channelCredentials: ChannelCredentials;
+ meta: Metadata;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/index.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/index.ts
new file mode 100644
index 00000000..22014fe4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/index.ts
@@ -0,0 +1,6 @@
+export { Credentials } from './credentials';
+export { ErrorData } from './error-data';
+export { GrpcConfig } from './grpc-config';
+export { Consumer } from './consumers';
+export { GatewayRoute, GatewayService, KokoAuthHeaders } from './konnect-gateway';
+export { PollyConfig } from './polly'
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/konnect-gateway.d.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/konnect-gateway.d.ts
new file mode 100644
index 00000000..bd8e355d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/konnect-gateway.d.ts
@@ -0,0 +1,37 @@
+export interface GatewayService {
+ name: string;
+ client_certificate?: string | null;
+ connect_timeout?: number;
+ enabled?: boolean;
+ host?: string;
+ path?: string;
+ port?: number;
+ protocol?: string;
+ read_timeout?: number;
+ retries?: number;
+ tags?: Array;
+ write_timeout?: number;
+}
+
+export interface GatewayRoute {
+ service: { id: string };
+ destinations?: string | null;
+ headers?: string | null;
+ hosts?: string | null;
+ https_redirect_status_code?: number;
+ methods?: Array;
+ name?: string;
+ paths?: Array;
+ path_handling?: string;
+ preserve_host?: boolean;
+ protocols?: Array;
+ regex_priority?: number;
+ snis?: string | null;
+ sources?: string | null;
+ strip_path?: boolean;
+ tags?: Array;
+}
+
+export interface KokoAuthHeaders {
+ [key: string]: AxiosHeaderValue;
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/polly.d.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/polly.d.ts
new file mode 100644
index 00000000..8a3feb21
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/interfaces/polly.d.ts
@@ -0,0 +1,48 @@
+export interface PollyConfig {
+ adapters?: string[];
+ adapterOptions?: {
+ fetch?: {
+ context?: any;
+ };
+ xhr?: {
+ context?: any;
+ };
+ [key: string]: any;
+ };
+ persister?: string;
+ persisterOptions?: {
+ fs?: {
+ recordingsDir?: string;
+ };
+ [key: string]: any;
+ };
+ logging?: boolean;
+ logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent';
+ mode?: 'record' | 'replay' | 'passthrough';
+ expiresIn?: string;
+ expiryStrategy?: 'warn' | 'error' | 'record';
+ timing?: {
+ connect?: number | (() => number);
+ response?: number | (() => number);
+ };
+ matchRequestsBy?: {
+ method?: boolean;
+ url?: {
+ protocol?: boolean;
+ username?: boolean;
+ password?: boolean;
+ host?: boolean;
+ hostname?: boolean;
+ port?: boolean;
+ pathname?: boolean;
+ query?: boolean | string[];
+ hash?: boolean;
+ };
+ headers?: boolean | string[];
+ body?: boolean;
+ };
+ recordIfMissing?: boolean;
+ flushRequestsOnStop?: boolean;
+ requestDelay?: number;
+ recordFailedRequests?: boolean;
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/mocking/polly.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/mocking/polly.ts
new file mode 100644
index 00000000..6379a42d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/mocking/polly.ts
@@ -0,0 +1,39 @@
+import { Polly } from '@pollyjs/core';
+import NodeHttpAdapter from '@pollyjs/adapter-node-http';
+import FSPersister from '@pollyjs/persister-fs';
+import * as path from 'path';
+import { PollyConfig } from '../interfaces/polly'
+
+Polly.register(NodeHttpAdapter);
+Polly.register(FSPersister);
+
+// pollyjs configuration options can be found here https://netflix.github.io/pollyjs/#/configuration
+
+/**
+ * Returns new polly insance with some predefined configurations
+ * @param {string} name - name of the recording
+ * @param {object} PollyConfig- pollyjs configuration, all configurations except name are optional
+ * @returns {object} - new polly instance
+ */
+export const createPolly = (name, config?: PollyConfig): Polly => {
+ return new Polly(name, {
+ // configuring polly for http requests and filesystem storage for the recordings
+ adapters: ['node-http'],
+ persister: 'fs',
+ persisterOptions: {
+ fs: {
+ recordingsDir: path.resolve(process.cwd(), `./support/mocking/${name}`),
+ }
+ },
+ // The Polly mode
+ mode: config?.mode || 'replay',
+ // Set the log level for the polly instance
+ logLevel: config?.logLevel || 'info',
+ // If a request's recording is not found, pass-through to the server and record the response
+ recordIfMissing: true,
+ // After how long the recorded request will be considered expired from the time it was persisted
+ expiresIn: '30d',
+ // What should occur when Polly tries to use an expired recording in replay mode
+ expiryStrategy: 'warn'
+ });
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/mocking/prometheus/prometheus_1819985369/recording.har b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/mocking/prometheus/prometheus_1819985369/recording.har
new file mode 100644
index 00000000..29439375
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/mocking/prometheus/prometheus_1819985369/recording.har
@@ -0,0 +1,99 @@
+{
+ "log": {
+ "_recordingName": "prometheus",
+ "creator": {
+ "comment": "persister:fs",
+ "name": "Polly.JS",
+ "version": "6.0.6"
+ },
+ "entries": [
+ {
+ "_id": "31ac4a1509371e6aee1074ec24b18085",
+ "_order": 0,
+ "cache": {},
+ "request": {
+ "bodySize": 0,
+ "cookies": [],
+ "headers": [
+ {
+ "name": "accept",
+ "value": "application/json, text/plain, */*"
+ },
+ {
+ "name": "kong-admin-token",
+ "value": "handyshake"
+ },
+ {
+ "name": "user-agent",
+ "value": "axios/1.7.2"
+ },
+ {
+ "name": "accept-encoding",
+ "value": "gzip, compress, deflate, br"
+ },
+ {
+ "name": "host",
+ "value": "localhost:9090"
+ }
+ ],
+ "headersSize": 251,
+ "httpVersion": "HTTP/1.1",
+ "method": "GET",
+ "queryString": [
+ {
+ "name": "query",
+ "value": "kong_upstream_target_health"
+ }
+ ],
+ "url": "http://localhost:9090/api/v1/query?query=kong_upstream_target_health"
+ },
+ "response": {
+ "bodySize": 259,
+ "content": {
+ "encoding": "base64",
+ "mimeType": "application/json",
+ "size": 259,
+ "text": "[\"H4sIAAAAAAAA/+SRXW7CMBCE7zLPbhRDBdTnaJ8QsoyzEEriRN41UhTl7pVJECfoj9RHj0cz82lHsDhJDANO3hMzFConDmZEJE6NvA89weBGXroItagw+xEtSbz4bLU2uJashcG1C2ebepZIrrXi4pnE1uQaqaHgqirmFgO9XRWrTVEWa7MroXAJLC54WiJefG92usw/n93xKULdJ2dbFdhSjPdVnI48sFALg1ok2+bq5X28hLnmseypfzyUSeHmmkQwe73V29f1WutN8bbZKZQ4TOrXgedQX5O/su1Op//FPXw/rv4TuCn8GHC+72GavgAAAP//AQAA//8z6P9GBwQAAA==\"]"
+ },
+ "cookies": [],
+ "headers": [
+ {
+ "name": "content-encoding",
+ "value": "gzip"
+ },
+ {
+ "name": "content-type",
+ "value": "application/json"
+ },
+ {
+ "name": "date",
+ "value": "Mon, 03 Jun 2024 16:45:16 GMT"
+ },
+ {
+ "name": "content-length",
+ "value": "259"
+ }
+ ],
+ "headersSize": 116,
+ "httpVersion": "HTTP/1.1",
+ "redirectURL": "",
+ "status": 200,
+ "statusText": "OK"
+ },
+ "startedDateTime": "2024-06-03T16:45:16.964Z",
+ "time": 5,
+ "timings": {
+ "blocked": -1,
+ "connect": -1,
+ "dns": -1,
+ "receive": 0,
+ "send": 0,
+ "ssl": -1,
+ "wait": 5
+ }
+ }
+ ],
+ "pages": [],
+ "version": "1.2"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/deck.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/deck.ts
new file mode 100644
index 00000000..b4441986
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/deck.ts
@@ -0,0 +1,19 @@
+import { gatewayAuthHeader } from '../config/gateway-vars';
+import { logDebug } from './logging';
+import * as fs from 'fs';
+const { authHeaderKey, authHeaderValue } = gatewayAuthHeader();
+
+/**
+ * Construct a decK command with the given cmd component
+ * @param {object} string - the actual deck command
+ */
+export const constructDeckCommand = (cmd) => {
+ const finalCommand = `deck gateway ${cmd} --headers "${authHeaderKey}: ${authHeaderValue}"`;
+
+ logDebug(`built deck cmd -> ${finalCommand}`);
+ return finalCommand;
+};
+
+export const read_deck_config = (deckFileName) => {
+ return JSON.parse(fs.readFileSync(`./${deckFileName}`, 'utf8'));
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/entities-gateway.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/entities-gateway.ts
new file mode 100644
index 00000000..94eaeb28
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/entities-gateway.ts
@@ -0,0 +1,996 @@
+import axios, { AxiosPromise, AxiosResponse } from 'axios';
+import { expect } from '../assert/chai-expect';
+import { Environment, getBasePath, isGateway, isKoko } from '../config/environment';
+import { logResponse } from './logging';
+import { randomString, wait } from './random';
+import { retryRequest } from './retry-axios';
+import { getNegative } from './negative-axios';
+
+export const getUrl = (endpoint: string) => {
+ const basePath = getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ });
+ if (endpoint && endpoint.startsWith('/')) {
+ return `${basePath}${endpoint}`;
+ }
+ return `${basePath}/${endpoint}`;
+};
+
+const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+})}`;
+
+/**
+ * Request to create GW Service
+ * @param {string} name - service name
+ * @param {object} payload - request payload
+ * @param {string} workspace - name of the worksapce
+ * @returns {AxiosResponse}
+ */
+export const createGatewayService = async (
+ name: string,
+ payload?: object,
+ workspace?: string
+) => {
+ payload ? (payload = { name, ...payload }) : null;
+ const endpoint = `${workspace}/services`;
+
+ const url = workspace ? `${getUrl(endpoint)}` : getUrl('services');
+
+ const requestPayload = payload || {
+ name,
+ url: 'http://httpbin/anything',
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: requestPayload,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(resp.data.name, 'Should have correct service name').equal(name);
+ return resp.data;
+};
+
+/**
+ * Request to update GW Service
+ * @param {string} serviceIdOrName
+ * @param {object} payload - request payload
+ * @param {string} workspace - name of the worksapce
+ * @returns {AxiosResponse}
+ */
+export const updateGatewayService = async (
+ serviceIdOrName: string,
+ payload?: object,
+ workspace?: string
+) => {
+ payload ? (payload = { ...payload }) : null;
+ const endpoint = `${workspace}/services/`;
+
+ const url = workspace
+ ? `${getUrl(endpoint)}`
+ : getUrl(`services/${serviceIdOrName}`);
+
+ const resp = await axios({
+ method: 'patch',
+ url,
+ data: payload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+
+ return resp.data;
+};
+
+/**
+ * Reusable request to delete GW Service
+ * @param {string} serviceIdOrName
+ * @returns {AxiosResponse}
+ */
+export const deleteGatewayService = async (serviceIdOrName: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('services')}/${serviceIdOrName}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ return resp;
+};
+
+/**
+ * Adds Route to an existing Gateway Service
+ * @param {string} serviceIdOrName
+ * @param {string[]} paths - paths of the route
+ * @param {object} payload - optional request body for the route
+ * @param {string} workspace - name of the worksapce
+ * @returns {AxiosResponse}
+ */
+export const createRouteForService = async (
+ serviceIdOrName: string,
+ paths?: string[],
+ payload?: object,
+ workspace?: string
+) => {
+ const endpoint = `${workspace}/services`;
+ const url =`${getUrl(workspace ? endpoint : 'services')}/${serviceIdOrName}/routes`;
+
+ payload ? (payload = { name: serviceIdOrName, paths, ...payload }) : null;
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: payload || {
+ name: randomString(),
+ paths: paths ? paths : ['/apitest'],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ return resp.data;
+};
+
+/**
+ * Adds Expression Route to an existing Gateway Service
+ * @param {string} serviceIdOrName
+ * @param {string} expression - expression to use for route
+ * @param {string} workspace - name of the worksapce
+ * @returns {AxiosResponse}
+ */
+export const createExpressionRouteForService = async (
+ serviceIdOrName: string,
+ expression?: string,
+ workspace?: string
+) => {
+ const endpoint = `${workspace}/services`;
+ const url =`${getUrl(workspace ? endpoint : 'services')}/${serviceIdOrName}/routes`;
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: `expression=${expression || '(http.path=="/apitest")'}`,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ return resp.data;
+};
+
+/**
+ * Creates a route
+ * @param {string[]} paths - paths of the route
+ * @param {object} payload - optional request body for the route
+ * @param {string} workspace - name of the worksapce
+ * @returns {AxiosResponse}
+ */
+export const createRoute = async (
+ paths: string[],
+ payload?: object,
+ workspace?: string
+) => {
+
+ const endpoint = `${workspace}/routes`;
+ const url = workspace ? `${getUrl(endpoint)}` : getUrl('routes');
+ payload ? (payload = { paths, ...payload }) : null;
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: payload || {
+ name: randomString(),
+ paths: paths ? paths : [`/${randomString()}`],
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ return resp.data;
+};
+
+/**
+ * Patch a route
+ * @param {string} routeIdOrName
+ * @param {object} payload - optional request body for the route
+ * @param {string} workspace - name of the worksapce
+ * @returns {AxiosResponse}
+ */
+export const patchRoute = async (
+ routeIdOrName: string,
+ payload: object,
+ workspace?: string
+) => {
+
+ const endpoint = `${workspace}/routes/${routeIdOrName}`;
+ const url = workspace ? `${getUrl(endpoint)}` : getUrl(`routes/${routeIdOrName}`);
+ payload ? (payload = { ...payload }) : null;
+
+ const resp = await axios({
+ method: 'patch',
+ validateStatus: null,
+ url,
+ data: payload,
+ });
+ logResponse(resp);
+
+ return resp;
+};
+
+/**
+ * Delete the target route
+ * @param {string} routeIdOrName route id or name
+ * @param {object} headers optional headers
+ * @returns {AxiosResponse}
+ */
+export const deleteGatewayRoute = async (
+ routeIdOrName: string
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('routes')}/${routeIdOrName}`
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Create a consumer
+ * @param {string} username - optional username
+ * @param {object} payload - optional payload
+ * @returns {AxiosResponse}
+ */
+export const createConsumer = async (username?: string, payload?: object) => {
+ const resp = await axios({
+ method: 'post',
+ url: getUrl('consumers'),
+ data: payload || {
+ username: username ? username : randomString(),
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * PATCH a consumer
+ * @param {string} usernameOrId
+ * @param {object} payload
+ * @returns {AxiosResponse}
+ */
+export const patchConsumer = async (usernameOrId: string, payload: object) => {
+ const resp = await axios({
+ method: 'patch',
+ url: `${getUrl('consumers')}/${usernameOrId}`,
+ data: payload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ return resp.data;
+};
+
+/**
+ * Create a consumer
+ * @param {string} usernameOrId
+ * @returns {AxiosResponse}
+ */
+export const deleteConsumer = async (usernameOrId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('consumers')}/${usernameOrId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Create a consumer group
+ * @param {string} consumerGroupName - optional consumer group name
+ * @param {object} payload - optional payload
+ * @returns {AxiosResponse}
+ */
+export const createConsumerGroup = async (
+ consumerGroupName?: string,
+ payload?: object
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: getUrl('consumer_groups'),
+ data: payload || {
+ name: consumerGroupName ? consumerGroupName : randomString(),
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a consumer group
+ * @param {string} consumerGroupName - consumer group name
+ * @returns {AxiosResponse}
+ */
+export const deleteConsumerGroup = async (consumerGroupNameOrId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('consumer_groups')}/${consumerGroupNameOrId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Create a consumer group scoped plugin
+ * @param {string} consumerGroupNameOrId - consumer group name or id
+ * @param {object} payload - payload
+ * @returns {AxiosResponse}
+ */
+export const createConsumerGroupScopedPlugin = async (
+ consumerGroupNameOrId: string,
+ payload: object
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('consumer_groups')}/${consumerGroupNameOrId}/plugins`,
+ data: payload,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Add the given consumer to the given consumer group
+ * @param {object} consumerNameOrId - consumer name or id
+ * @param {string} consumerGroupNameOrId - consumer group name or id
+ * @returns {AxiosResponse}
+ */
+export const addConsumerToConsumerGroup = async (
+ consumerNameOrId: object,
+ consumerGroupNameOrId: string
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('consumer_groups')}/${consumerGroupNameOrId}/consumers`,
+ data: {
+ consumer: consumerNameOrId,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp;
+};
+
+/**
+ * Remove the given consumer from the given consumer group
+ * @param {object} consumerNameOrId - consumer name or id
+ * @param {string} consumerGroupNameOrId - consumer group name or id
+ * @returns {AxiosResponse}
+ */
+export const removeConsumerFromConsumerGroup = async (
+ consumerNameOrId: object,
+ consumerGroupNameOrId: string
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl(
+ 'consumer_groups'
+ )}/${consumerGroupNameOrId}/consumers/${consumerNameOrId}`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp.data;
+};
+
+/**
+ * Create a consumer group setting
+ * @param {string} consumerGroupNameOrId - consumer group name or id
+ * @param {string} pluginName - plugin name
+ * @param {string} pluginId - id
+ * @param {object} settings - settings
+ * @returns {AxiosResponse}
+ */
+export const createConsumerGroupSettings = async (
+ consumerGroupNameOrId: string,
+ pluginName: string,
+ pluginId: string,
+ settings: object
+) => {
+ const resp = await axios({
+ method: 'put',
+ url: `${getUrl(
+ 'consumer_groups'
+ )}/${consumerGroupNameOrId}/plugins/${pluginId}`,
+ data: {
+ name: pluginName,
+ config: settings,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ return resp.data;
+};
+
+/**
+ * Create key-auth credentials for a consumer
+ * @param {string} consumerNameOrId- consumer name or id
+ * @returns {AxiosResponse}
+ */
+export const createKeyAuthCredentialsForConsumer = async (
+ consumerNameOrId: string
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('consumers')}/${consumerNameOrId}/key-auth-enc`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Create basic-auth plugin credentials for a given consumer
+ * @param consumerNameOrId name or id of the target consumer
+ * @param username optional basic-auth username
+ * @param password optional basic-auth password
+ * @returns {AxiosResponse}
+ */
+export const createBasicAuthCredentialForConsumer = async (
+ consumerNameOrId: string,
+ username?: string,
+ password?: string
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('consumers')}/${consumerNameOrId}/basic-auth`,
+ data: {
+ username: username ? username : randomString(),
+ password: password ? password : randomString(),
+ },
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ return resp;
+};
+
+/**
+ * Upload a root CA Certificate
+ * @param {string} cert - the root certificate
+ * @returns {AxiosResponse}
+ */
+export const uploadCaCertificate = async (
+ cert: string
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('ca_certificates')}`,
+ data: {
+ cert
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ return resp.data;
+};
+
+/**
+ * Delete CA Certificate
+ * @param {string} certNameOrId
+ * @returns {AxiosResponse}
+ */
+export const deleteCaCertificate = async (
+ certNameOrId: string
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('ca_certificates')}/${certNameOrId}`,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp
+};
+
+/**
+ * Create a key for a consumer
+ * @param {string} consumerNameorId
+ * @param {string} pluginName - either key-auth or key-auth-enc
+ * @returns {AxiosResponse}
+ */
+export const createKeyCredentialForConsumer = async (
+ consumerNameorId: string,
+ pluginName = 'key-auth'
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('consumers')}/${consumerNameorId}/${pluginName}`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ return resp.data;
+};
+
+/**
+ * Get all existing workspaces
+ * @returns {AxiosResponse}
+ */
+export const getWorkspaces = async () => {
+ const resp = await axios(`${getUrl('workspaces')}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ return resp.data;
+};
+
+/**
+ * Create a workspace
+ * @param {string} workspaceName
+ * @returns {AxiosResponse}
+ */
+export const createWorkspace = async (workspaceName: string) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('workspaces')}`,
+ data: {
+ name: workspaceName,
+ },
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a workspace
+ * @param {string} workspaceNameOrId
+ * @returns {AxiosResponse}
+ */
+export const deleteWorkspace = async (workspaceNameOrId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('workspaces')}/${workspaceNameOrId}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Create a Plugin
+ * @param {object} pluginPayload - request body for plugin creation
+ * @param {string} workspace - optional name of the workspace to create plugin
+ * @returns {AxiosResponse}
+ */
+export const createPlugin = async (
+ pluginPayload: object,
+ workspace?: string
+) => {
+
+ // In Koko we don't set 'default' workspace name in URL
+ if (isGateway()) {
+ workspace = workspace ? workspace : 'default'
+ } else if (isKoko()) {
+ workspace = ''
+ }
+
+ const endpoint = `${workspace}/plugins`;
+
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl(endpoint)}`,
+ data: pluginPayload,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a plugin
+ * @param {string} pluginId
+ * @returns {AxiosResponse}
+ */
+export const deletePlugin = async (pluginId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('plugins')}/${pluginId}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
+
+/**
+ * Create upstream
+ * @param {string} upstreamName - name of the upstream
+ * @param {object} payload- request payload
+ */
+export const createUpstream = async (upstreamName = randomString(), payload?: object) => {
+ payload ? (payload = { ...payload, name: upstreamName, }) : null;
+
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('upstreams')}`,
+ data: payload || {
+ name: upstreamName,
+ healthchecks: {
+ active: {
+ healthy: {
+ interval: 2,
+ successes: 2,
+ http_statuses: [200, 302]
+ },
+ unhealthy: {
+ interval: 2,
+ http_failures: 1,
+ http_statuses: [429, 404, 500, 501, 502, 503, 504, 505]
+ }
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ return resp.data
+}
+
+/**
+ * Delete an upstream
+ * @param {string} upstreamId
+ * @returns {AxiosResponse}
+ */
+export const deleteUpstream = async (upstreamId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('upstreams')}/${upstreamId}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
+
+/**
+ * Create a target for an existing upstream
+ * @param {string} upstreamId - upstream id to create target for
+ * @param {string} target - target url
+ * @returns {AxiosResponse}
+ */
+export const addTargetToUpstream = async (upstreamId: string, target: string) => {
+ const resp = await axios({
+ url: `${getUrl('upstreams')}/${upstreamId}/targets`,
+ method: 'post',
+ data: {
+ target,
+ weight: 1000
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ return resp.data
+}
+
+
+/**
+ * Create a filter chain on a service
+ * @param {object} filterChainPayload - request body for filter chain creation
+ * @param {string} serviceNameOrId - service name or id to attach the filter chain
+ * @param {string} workspace - optional name of the workspace to create filter chain
+ * @returns {AxiosResponse}
+ */
+export const createFilterChainForService = async (
+ filterChainPayload: object,
+ serviceNameOrId: string,
+ workspace?: string
+) => {
+ workspace = workspace ? workspace : 'default';
+ const endpoint = `${workspace}/services/${serviceNameOrId}/filter-chains`;
+
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl(endpoint)}`,
+ data: filterChainPayload,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Create a filter chain on a route
+ * @param {object} filterChainPayload - request body for filter chain creation
+ * @param {string} routeNameOrId - route name or id to attach the filter chain
+ * @param {string} workspace - optional name of the workspace to create filter chain
+ * @returns {AxiosResponse}
+ */
+export const createFilterChainForRoute = async (
+ filterChainPayload: object,
+ routeNameOrId: string,
+ workspace?: string
+) => {
+ workspace = workspace ? workspace : 'default';
+ const endpoint = `${workspace}/routes/${routeNameOrId}/filter-chains`;
+
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl(endpoint)}`,
+ data: filterChainPayload,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a filter chain
+ * @param {string} filterChainNameOrId
+ * @returns {AxiosResponse}
+ */
+export const deleteFilterChain = async (filterChainNameOrId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('filter-chains')}/${filterChainNameOrId}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
+
+
+
+/**
+ * Delete kong cache
+ */
+export const deleteCache = async () => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('cache')}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
+
+/**
+ *
+ */
+export const getRouterFlavor = async () => {
+ return (await axios(getUrl(''))).data.configuration.router_flavor
+};
+
+/**
+ * Create a service and a route, send request to route until it is 200
+ * after getting 200, delete the service/route, send request again to the route until it is 404
+ * This triggers router rebuild making sure all configuration updates have been propagated in kong
+ * @param {object} options
+ * @property {number} timeout - retryRequest timeout
+ * @property {number} interval - retryRequest interval
+ * @property {object} proxyReqHeader - custom proxy request header e.g. key-auth key
+ */
+export const waitForConfigRebuild = async (options: any = {}) => {
+ // create a service
+ const service = await createGatewayService(`routerRebuild-${randomString()}`);
+ const serviceId = service.id;
+
+ // create a route for a service
+ const routePath = `/routerRebuild-${randomString()}`;
+ const router_flavor = isGateway() ? await getRouterFlavor() : 'traditional_compatible'
+ const route = router_flavor == 'expressions' ? await createExpressionRouteForService(serviceId, `http.path == "${routePath}"`) : await createRouteForService(serviceId, [routePath]);
+ const routeId = route.id;
+
+ // create a key-auth plugin for the route
+ const plugin = await createPlugin({
+ name: 'key-auth',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ config: {},
+ });
+ const pluginId = plugin.id;
+
+ // send request to route until response is 401
+ const reqSuccess = () =>
+ getNegative(`${proxyUrl}${routePath}`, options?.proxyReqHeader);
+ const assertionsSuccess = (resp) => {
+ expect(
+ resp.status,
+ 'waitForConfigRebuild - route should return 401'
+ ).to.equal(401);
+ };
+
+ await retryRequest(
+ reqSuccess,
+ assertionsSuccess,
+ options?.timeout,
+ options?.interval,
+ options?.verbose,
+ );
+
+ // removing the entities
+ await deletePlugin(pluginId);
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+
+ // send request to route until response is 404
+ const reqFail = () =>
+ getNegative(`${proxyUrl}${routePath}`, options?.proxyReqHeader);
+ const assertionsFail = (resp) => {
+ expect(
+ resp.status,
+ 'waitForConfigRebuild - route should return 404'
+ ).to.equal(404);
+ };
+
+ await retryRequest(
+ reqFail,
+ assertionsFail,
+ options?.timeout,
+ options?.interval
+ );
+
+ return true
+};
+
+interface ItemProps {
+ id?: string;
+ username?: string;
+ name?: string;
+}
+
+interface ResponseProps {
+ data: Array;
+}
+
+export const clearAllKongResources = async () => {
+ await clearKongResource('consumers');
+ await clearKongResource('consumer_groups');
+ await clearKongResource('plugins');
+ await clearKongResource('certificates');
+ await clearKongResource('ca_certificates');
+ await clearKongResource('snis');
+ await clearKongResource('vaults');
+ await clearKongResource('routes');
+ await clearKongResource('services');
+ await clearKongResource('upstreams');
+};
+
+export const clearKongResource = async (endpoint: string) => {
+ const tasks: (() => AxiosPromise)[] = [];
+ const url = getUrl(endpoint);
+ try {
+ const items: ItemProps[] = [];
+ let next = url;
+
+ for (;;) {
+ const res: AxiosResponse = await axios({
+ method: 'get',
+ url: next,
+ });
+
+ if (Array.isArray(res.data.data)) {
+ items.push(...res.data.data);
+ }
+
+ if ((res.data as any)?.next) {
+ next = getUrl((res.data as any)?.next);
+ } else {
+ break;
+ }
+ }
+
+ if (items.length === 0) {
+ return;
+ }
+
+ items.forEach((resource: ItemProps) => {
+ const clearAllOptions = {
+ method: 'DELETE',
+ url: `${getUrl(endpoint)}/${resource.id || resource.name}`,
+ };
+
+ tasks.push(async () => axios(clearAllOptions));
+ });
+ } catch (err) {
+ if (axios.isAxiosError(err) && err.response?.status === 404) return;
+ }
+
+ try {
+ await Promise.all(
+ Array.from({ length: 10 }).map(async () => {
+ let task: (() => AxiosPromise) | undefined;
+
+ while ((task = tasks.shift())) {
+ try {
+ await task();
+ } catch (e) {
+ if (axios.isAxiosError(e) && e.response?.status === 404) {
+ continue;
+ }
+
+ throw e;
+ }
+ }
+ })
+ );
+ } catch (err) {
+ console.log(err);
+ }
+};
+
+
+/**
+ * Wait for /status/ready to return given status
+ * @param {string} cacheKey - cache key to wait for
+ * @param {number} timeout - timeout in ms
+ */
+export const waitForCacheInvalidation = async (
+ cacheKey,
+ timeout,
+) => {
+ let response;
+ let wantedTimeout
+ while (timeout > 0) {
+ const response = await getNegative(`${getUrl('cache')}/${cacheKey}`);
+ if (response.status === 404) {
+ // log final response
+ logResponse(response);
+ return true;
+ }
+ await wait(1000); // eslint-disable-line no-restricted-syntax
+ timeout -= 1000;
+ }
+ // log last response received
+ logResponse(response);
+
+ // throw
+ expect(false, `${wantedTimeout}ms exceeded waiting for "${cacheKey}" to invalidate from cache`).to.equal(true);
+ return false;
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/entities-rbac-gateway.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/entities-rbac-gateway.ts
new file mode 100644
index 00000000..f4abd638
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/entities-rbac-gateway.ts
@@ -0,0 +1,248 @@
+import axios from 'axios';
+import { expect } from '../assert/chai-expect';
+import { App, Environment, getBasePath } from '../config/environment';
+
+const getUrl = (endpoint: string) => {
+ const basePath = getBasePath({
+ app: App.gateway,
+ environment: Environment.gateway.admin,
+ });
+ return `${basePath}/${endpoint}`;
+};
+
+/**
+ * Create a user
+ * @param {string} name
+ * @param {string} token - user token used in API request headers to auth the user
+ * @param {boolean} enabled - enabled by-deafult
+ * @param {object} payload - optional request body
+ * @returns {AxiosResponse}
+ */
+export const createUser = async (
+ name: string,
+ token: string,
+ enabled?: boolean,
+ payload?: object
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('rbac/users')}`,
+ data: payload || {
+ name,
+ user_token: token,
+ enabled,
+ },
+ });
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a user
+ * @param {string} userNameOrId
+ * @returns {AxiosResponse}
+ */
+export const deleteUser = async (userNameOrId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('rbac/users')}/${userNameOrId}`,
+ });
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Create a Role
+ * @param {string} name - role name
+ * @param {string} comment - optional role comment
+ * @returns {AxiosResponse}
+ */
+export const createRole = async (name: string, comment?: string) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('rbac/roles')}`,
+ data: {
+ name,
+ comment,
+ },
+ });
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a Role
+ * @param {string} roleNameOrId
+ * @returns {AxiosResponse}
+ */
+export const deleteRole = async (roleNameOrId: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('rbac/roles')}/${roleNameOrId}`,
+ });
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Create a Role Endpoint Permission
+ * @param {string} roleNameOrId - role name or id
+ * @param {string} endpoint - target endpoint for permission
+ * @param {string} actions - comma separated actions, default all of ('create,update,read,delete')
+ * @param {boolean} negative - optional negative boolean, false is default
+ * @param {string} workspace - optional workspace name, 'default' is default
+ * @returns {AxiosResponse}
+ */
+export const createRoleEndpointPermission = async (
+ roleNameOrId: string,
+ endpoint: string,
+ actions?: string,
+ negative?: boolean,
+ workspace?: string
+) => {
+ actions = actions ? actions : '*';
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('rbac/roles')}/${roleNameOrId}/endpoints`,
+ data: {
+ workspace,
+ endpoint,
+ negative,
+ actions,
+ },
+ });
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a Role Endpoint Permission
+ * @param {string} roleNameOrId
+ * @param {string} endpoint - target permission endpoint
+ * @param {string} workspace - optional workspace name, 'default' is default
+ * @returns {AxiosResponse}
+ */
+export const deleteRoleEndpointPermission = async (
+ roleNameOrId: string,
+ endpoint: string,
+ workspace?: string
+) => {
+ workspace = workspace ? workspace : 'default';
+
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl(
+ 'rbac/roles'
+ )}/${roleNameOrId}/endpoints/${workspace}${endpoint}`,
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Add Role to a User
+ * @param {string} userNameOrId - user name or id
+ * @param {string|Array} roleNames - comma separated list of role names or a single string
+ * @returns {AxiosResponse}
+ */
+export const addRoleToUser = async (
+ userNameOrId: string,
+ roleNames: string | string[]
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('rbac/users')}/${userNameOrId}/roles`,
+ data: {
+ roles: roleNames,
+ },
+ });
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete User role
+ * @param {string} userNameOrId - user name or id
+ * @param {string|Array} roleNames - comma separated list of role names or a single string
+ * @returns {AxiosResponse}
+ */
+export const deleteUserRole = async (
+ userNameOrId: string,
+ roleNames: string | string[]
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('rbac/users')}/${userNameOrId}/roles`,
+ data: {
+ roles: roleNames,
+ },
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
+
+/**
+ * Create a Role Entity Permission
+ * @param {string} roleNameOrId - role name or id
+ * @param {string} entity_id - id of the target entity
+ * @param {string} entity_type - type of the target entity e.g. 'services'
+ * @param {string|string[]} actions - optional actions, 'create,read,update,delete' is default
+ * @param {boolean} negative - optional negative boolean, false is default
+ * @returns {AxiosResponse}
+ */
+export const createRoleEntityPermission = async (
+ roleNameOrId: string,
+ entity_id: string,
+ entity_type: string,
+ actions?: string,
+ negative?: boolean
+) => {
+ actions = actions ? actions : '*';
+
+ const resp = await axios({
+ method: 'post',
+ url: `${getUrl('rbac/roles')}/${roleNameOrId}/entities`,
+ data: {
+ entity_id,
+ entity_type,
+ actions,
+ negative,
+ },
+ });
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Delete a Role Entity Permission
+ * @param {string} roleNameOrId
+ * @param {string} entity_id - id of the target entity
+ * @returns {AxiosResponse}
+ */
+export const deleteRoleEntityPermission = async (
+ roleNameOrId: string,
+ entity_id: string
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('rbac/roles')}/${roleNameOrId}/entities/${entity_id}`,
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ return resp;
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/eventually.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/eventually.ts
new file mode 100644
index 00000000..442981e1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/eventually.ts
@@ -0,0 +1,40 @@
+import { isCI } from '@support';
+
+/**
+ * Wait for the `assertions` does not throw any exceptions.
+ * @param assertions - The assertions to be executed.
+ * @param timeout - The timeout in milliseconds.
+ * @param interval - The interval in milliseconds.
+ * @param verbose - Verbose logs in case of error
+ * @returns {Promise} - Asnyc void promise.
+ */
+export const eventually = async (
+ assertions: () => Promise,
+ timeout = 30000,
+ interval = 3000,
+ verbose = false,
+): Promise => {
+ let errorMsg = '';
+ // enable verbose logs in GH Actions for debugability
+ verbose = isCI() ? true : verbose
+
+ while (timeout >= 0) {
+ const start = Date.now();
+ try {
+ await assertions();
+ return;
+ } catch (error: any) {
+ const end = Date.now();
+ if (verbose) {
+ errorMsg = error.message;
+ console.log(errorMsg);
+ console.log(
+ `** Assertion(s) Failed -- Retrying in ${interval / 1000} seconds **`
+ );
+ }
+ await new Promise((resolve) => setTimeout(resolve, interval));
+ timeout -= interval + (end - start);
+ }
+ }
+ throw new Error(errorMsg);
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/gw-vaults.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/gw-vaults.ts
new file mode 100644
index 00000000..8cfb8987
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/gw-vaults.ts
@@ -0,0 +1,553 @@
+import axios from 'axios';
+import { expect } from '../assert/chai-expect';
+import { Environment, getBasePath } from '../config/environment';
+import { postNegative } from './negative-axios';
+import { logResponse } from './logging';
+
+const getUrl = (endpoint = '') => {
+ const basePath = getBasePath({
+ environment: Environment.gateway.admin,
+ });
+
+ return `${basePath}/${endpoint}`;
+};
+
+const getHost = () => {
+ const hostName = getBasePath({
+ environment: Environment.gateway.hostName,
+ });
+
+ return hostName;
+};
+
+const hcvToken = 'vault-plaintext-root-token';
+
+/**
+ * Get target hcv mount kv engine version
+ * @param {string} targetMount - name of the target hcv mount
+ * @returns {string} - hashicorp vault kv version of target mount
+ */
+export const getHcvKvVersion = async (targetMount = 'secret') => {
+ const resp = await axios({
+ url: `http://${getHost()}:8200/v1/sys/mounts/${targetMount}`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ });
+
+ expect(resp.data.options.version).to.be.a('string');
+ const kvVersion = resp.data.options.version;
+
+ return kvVersion;
+};
+
+/**
+ * Enable Approle auth method in HCV
+ * @param {string} path - path of the enabled approle auth method, default is 'approle'
+ */
+export const enableHcvApproleAuth = async (path = "approle") => {
+ const resp = await axios({
+ method: 'POST',
+ url: `http://${getHost()}:8200/v1/sys/auth/${path}`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ data: {
+ type: 'approle',
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ return resp.data;
+};
+
+/**
+ * Create an HCV policy for reading all secrets
+ * @param {string} policy_name - name of the policy
+ */
+export const createHcvSecretReadPolicy = async (policy_name = "hcv-secret-read") => {
+ const resp = await axios({
+ method: 'POST',
+ url: `http://${getHost()}:8200/v1/sys/policies/acl/${policy_name}`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ data: {
+ policy: `path "secret/*" {
+ capabilities = ["read"]
+ }`,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ return resp.data;
+}
+
+/**
+ * Create a new Approle in HCV
+ * @param {string} path - path of the enabled approle auth method, default is 'approle'
+ * @param {string} role_name - name of the approle to create, default is 'test-role'
+ */
+export const createHcvAppRole = async (path = "approle", role_name = "test-role") => {
+
+ const policy_name = "hcv-secret-read";
+ await createHcvSecretReadPolicy(policy_name);
+
+ const resp = await axios({
+ method: 'POST',
+ url: `http://${getHost()}:8200/v1/auth/${path}/role/${role_name}`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ data: {
+ secret_id_ttl: 0,
+ token_ttl: 0,
+ token_max_ttl: 0,
+ token_policies: [policy_name],
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ return resp.data;
+};
+
+/**
+ * Fetch Approle ID from HCV
+ * @param {string} role_name - name of the approle to fetch, default is 'test-role'
+ * @param {string} path - path of the enabled approle auth method, default is 'approle'
+ * @returns {string} - approle id
+ */
+export const getHcvApproleID = async (path = "approle", role_name = "test-role") => {
+ const resp = await axios({
+ method: 'GET',
+ url: `http://${getHost()}:8200/v1/auth/${path}/role/${role_name}/role-id`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ return resp.data.data.role_id;
+};
+
+/**
+ * Create a new Approle Secret ID in HCV
+ * @param {string} path - path of the enabled approle auth method, default is 'approle'
+ * @param {string} role_name - name of the approle to fetch, default is 'test-role'
+ * @returns {string} - approle secret id
+ */
+export const createHcvApproleSecretID = async (path = "approle", role_name = "test-role") => {
+ const resp = await axios({
+ method: 'POST',
+ url: `http://${getHost()}:8200/v1/auth/${path}/role/${role_name}/secret-id`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ data: {
+ ttl: 0,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ return resp.data.data.secret_id;
+}
+
+/**
+ * Create a new Approle Wrapped Secret ID in HCV
+ * @param {string} path - path of the enabled approle auth method, default is 'approle'
+ * @param {string} role_name - name of the approle to fetch, default is 'test-role'
+ * @returns {string} - wrapped approle secret id
+ */
+export const createHcvApproleWrappedSecretId = async (path = "approle", role_name = "test-role") => {
+ const resp = await axios({
+ method: 'POST',
+ url: `http://${getHost()}:8200/v1/auth/${path}/role/${role_name}/secret-id`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ 'X-Vault-Wrap-TTL': '60m',
+ },
+ data: {
+ ttl: 0,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ return resp.data.wrap_info.token;
+}
+
+/**
+ * Constracts hcv secret url
+ * @param {string} targetMount - name of the target hcv mount
+ * @param {string} pathName - path of the secret in HCV vault
+ * @returns {string} - hashicorp secret api
+ */
+export const getHcvSecretUrl = async (
+ targetMount = 'secret',
+ pathName = targetMount
+) => {
+ const kvVersion = await getHcvKvVersion(targetMount);
+
+ const hcvSecretUrl = `http://${getHost()}:8200/v1/${targetMount}${
+ kvVersion === '2' ? '/data/' : '/'
+ }${pathName}`;
+
+ return hcvSecretUrl;
+};
+
+/**
+ * Creates hcv backend vault in Kong
+ * @param {string} targetMount - name of the target hcv mount
+ * @param {string} targetHcvToken - hcv root token
+ * @param {string} vaultPrefix - hcv vault prefix (not config prefix for variables), default is 'my-hcv'
+ */
+export const createHcvVaultInKong = async (
+ targetMount = 'secret',
+ targetHcvToken = hcvToken,
+ vaultPrefix = 'my-hcv'
+) => {
+ const kvVersion = await getHcvKvVersion(targetMount);
+
+ const resp = await axios({
+ method: 'put',
+ url: `${getUrl('vaults')}/${vaultPrefix}`,
+ data: {
+ name: 'hcv',
+ config: {
+ protocol: 'http',
+ host: 'host.docker.internal',
+ port: 8200,
+ mount: targetMount,
+ kv: kvVersion === '2' ? 'v2' : 'v1',
+ token: targetHcvToken,
+ },
+ },
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ return resp.data;
+};
+
+/**
+ * Creates hcv backend vault using Approle auth method in Kong
+ * @param {string} targetMount - name of the target hcv mount
+ * @param {string} vaultPrefix - hcv vault prefix (not config prefix for variables), default is 'my-hcv'
+ * @param {string} targetApproleRoleID - ID of the Approle
+ * @param {string} targetApproleSecretID - Secret ID of the Approle
+ * @param {boolean} approleResponseWrapping - whether the secret id is a response wrapping token
+ */
+export const createHcvVaultWithApproleInKong = async (
+ targetMount = 'secret',
+ vaultPrefix = 'my-hcv',
+ targetApproleRoleID: string,
+ targetApproleSecretID: string,
+ approleResponseWrapping = false,
+) => {
+ const kvVersion = await getHcvKvVersion(targetMount);
+
+ const resp = await axios({
+ method: 'put',
+ url: `${getUrl('vaults')}/${vaultPrefix}`,
+ data: {
+ name: 'hcv',
+ config: {
+ protocol: 'http',
+ host: 'host.docker.internal',
+ port: 8200,
+ mount: targetMount,
+ kv: kvVersion === '2' ? 'v2' : 'v1',
+ auth_method: 'approle',
+ approle_role_id: targetApproleRoleID,
+ approle_secret_id: targetApproleSecretID,
+ approle_response_wrapping: approleResponseWrapping,
+ },
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ return resp.data;
+};
+
+/**
+ * Create HCV vault secrets
+ * @param {object} payload- request payload
+ * @param {string} targetMount - name of the target hcv mount, default is 'secret'
+ * @param {string} pathName - optional path of the secret in HCV vault
+ */
+export const createHcvVaultSecrets = async (
+ payload: object,
+ targetMount = 'secret',
+ pathName = targetMount
+) => {
+ const hcvSecretUrl = await getHcvSecretUrl(targetMount, pathName);
+ // kv v2 requires 'data' to be present in request payload
+ if (hcvSecretUrl.includes('/data/')) {
+ payload = { data: { ...payload } };
+ }
+
+ const resp = await axios({
+ method: 'post',
+ url: hcvSecretUrl,
+ data: payload,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ 'Content-Type': 'application/json',
+ },
+ });
+
+ expect(resp.status, 'Status should be 200|204').to.equal(
+ resp.data ? 200 : 204
+ );
+
+ return resp.data;
+};
+
+/**
+ * Delete target HCV secret mount entierly
+ * @param {string} mountName - target mount to delete
+ */
+export const deleteHcvSecretMount = async (mountName: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `http://${getHost()}:8200/v1/sys/mounts/${mountName}`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
+
+/**
+ * Delete target HCV secret path together with all secrets
+ * @param {string} mountName - target mount to delete
+ * @param {String} secretPathName - the path to delete
+ */
+export const deleteHcvSecret = async (
+ mountName: string,
+ secretPathName: string
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `http://${getHost()}:8200/v1/${mountName}/metadata/${secretPathName}`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
+
+/**
+ * Get HCV vault secrets
+ * @param {string} targetMount - name of the target hcv mount
+ * @param {string} pathName - optional path of the secret in HCV vault
+ */
+export const getHcvVaultSecret = async (
+ targetMount = 'secret',
+ pathName = targetMount
+) => {
+ const hcvSecretUrl = await getHcvSecretUrl(targetMount, pathName);
+
+ const resp = await axios({
+ url: `${hcvSecretUrl}?version=1`,
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ return resp.data.data;
+};
+
+/**
+ * Create AWS backend vault entity
+ * @param {string} vaultPrefix - the backend vault prefix, default is 'my-aws'
+ */
+export const createAwsVaultEntity = async (vaultPrefix = 'my-aws') => {
+ const resp = await axios({
+ method: 'put',
+ url: `${getUrl('vaults')}/${vaultPrefix}`,
+ data: {
+ name: 'aws',
+ config: {
+ region: 'us-east-2',
+ },
+ },
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+};
+
+/**
+ * Create Azure backend vault entity
+ * @param {string} vaultPrefix - the backend vault prefix, default is 'my-azure'
+ * @param {object} ttls - to specify ttl, neg_ttl and resurrect_ttl
+ */
+export const createAzureVaultEntity = async (vaultPrefix = 'my-azure', ttls?: object) => {
+ const resp = await axios({
+ method: 'put',
+ url: `${getUrl('vaults')}/${vaultPrefix}`,
+ data: {
+ name: 'azure',
+ config: {
+ location: 'us-east',
+ vault_uri: 'https://sdet-test.vault.azure.net/',
+ ...ttls
+ },
+ },
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+};
+
+/**
+ * Create ENV backend vault entity
+ * @param {string} vaultPrefix - the backend vault prefix, default is 'my-env'
+ * @param {object} configPayload - the vault config object payload e.g. {prefix: 'myvar_'}
+ */
+export const createEnvVaultEntity = async (
+ vaultPrefix = 'my-env',
+ configPayload: { prefix: string }
+) => {
+ const resp = await axios({
+ method: 'put',
+ url: `${getUrl('vaults')}/${vaultPrefix}`,
+ data: {
+ name: 'env',
+ config: configPayload,
+ },
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+};
+
+/**
+ * Creates GCP backend vault
+ * @param {string} projectId - gcp project_id, defaults to 'gcp-sdet-test'
+ * @param {string} vaultPrefix - gcp vault prefix (not config prefix for variables), default is 'my-gcp'
+ */
+export const createGcpVaultEntity = async (
+ vaultPrefix = 'my-gcp',
+ projectId = 'gcp-sdet-test'
+) => {
+ const resp = await axios({
+ method: 'put',
+ url: `${getUrl('vaults')}/${vaultPrefix}`,
+ data: {
+ name: 'gcp',
+ config: {
+ project_id: projectId,
+ },
+ },
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ return resp.data;
+};
+
+/**
+ * Delete target vault entity
+ * @param {string} vaultPrefix
+ */
+export const deleteVaultEntity = async (vaultPrefix: string) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('vaults')}/${vaultPrefix}`,
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
+
+/**
+ * Create a mount wiht kv engine 1
+ * @param {string} mountName - target maount name
+ * @param {number} kvEngineVersion - version of the kv engine
+ */
+export const createHcvMountWithTargetKvEngine = async (
+ mountName: string,
+ kvEngineVersion: number
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: `http://${getHost()}:8200/v1/sys/mounts/${mountName}`,
+ data: {
+ type: 'kv',
+ options: {
+ version: kvEngineVersion,
+ },
+ },
+ headers: {
+ 'X-Vault-Token': hcvToken,
+ },
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ return resp.data;
+};
+
+/**
+ * Create vault-auth vault entity
+ * @param {string} vaultEntityName - the name of vault entity
+ * @param {string} mountName - target mount name for the entity
+ * @param {string} kvEngineVersion - target version of kv engine
+ */
+export const createVaultAuthVaultEntity = async (
+ vaultEntityName = 'kong-auth',
+ mountName = 'kong-auth',
+ kvEngineVersion = 'v1'
+) => {
+ const resp = await postNegative(getUrl('vault-auth'), {
+ name: vaultEntityName,
+ mount: mountName,
+ protocol: 'http',
+ host: 'host.docker.internal',
+ port: 8200,
+ vault_token: hcvToken,
+ kv: kvEngineVersion,
+ });
+
+ return resp;
+};
+
+/**
+ * Get vault-auth vaults
+ */
+export const getVaultAuthVaults = async () => {
+ const vaultUrl = getUrl('vault-auth');
+ const resp = await axios(vaultUrl);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ return resp.data;
+};
+
+/**
+ * Delete target vault-auth plugin vault entity
+ * @param {string} vaultName - name of the target vault
+ */
+export const deleteVaultAuthPluginVaultEntity = async (
+ vaultName = 'kong-auth'
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('vault-auth')}/${vaultName}`,
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/http-log-server.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/http-log-server.ts
new file mode 100644
index 00000000..893e29bf
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/http-log-server.ts
@@ -0,0 +1,22 @@
+import axios from 'axios';
+import { expect } from '../assert/chai-expect';
+import { logResponse } from './logging';
+
+export const getHttpLogServerLogs = async () => {
+ const resp = await axios('http://localhost:9300/logs');
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+
+ return resp.data;
+};
+
+export const deleteHttpLogServerLogs = async () => {
+ const resp = await axios({
+ method: 'delete',
+ url: 'http://localhost:9300/logs'
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').equal(204);
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/influxdb.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/influxdb.ts
new file mode 100644
index 00000000..4724c74c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/influxdb.ts
@@ -0,0 +1,112 @@
+// const Influx = require('influx');
+import { InfluxDB } from 'influx';
+import { Environment, getBasePath, isGateway } from '../config/environment';
+
+export let influx: any;
+
+// for debugging you can enter the influxdb CLI via the below command from influx container
+// influx -precision rfc3339
+
+// for npm influx documentation
+// https://node-influx.github.io/class/src/index.js~InfluxDB.html#instance-method-query
+
+// Measurements in InfluxDB for Kong
+const SERIES = {
+ KONG_REQUEST: 'kong_request',
+ KONG_DATASTORE_CACHE: 'kong_datastore_cache',
+};
+
+/**
+ * Initialize new influxDB connection
+ * @returns {object}
+ */
+export const createInfluxDBConnection = () => {
+ const host = getBasePath({ environment: isGateway() ? Environment.gateway.hostName : undefined });
+
+ const influxDBUrl = `http://${host}:8086/kong`;
+
+ influx = new InfluxDB(influxDBUrl);
+ return influx;
+};
+
+/**
+ * Retrieves all entries from the kong_request mesurement
+ * @param {number} expectedEntryCount - total expected entry count to wait for
+ * @param {number} retries - how many times to retry
+ * @param {number} interval - interval to check again
+ * @returns {Array}
+ */
+export const getAllEntriesFromKongRequest = async (
+ expectedEntryCount?: number,
+ retries = 10,
+ interval = 2000
+): Promise => {
+ let entries = await influx.query(`select * from ${SERIES.KONG_REQUEST}`);
+ let actualCount = 0;
+
+ // wait for expected entry count to appear in influxdb
+ if (expectedEntryCount && entries.length !== expectedEntryCount) {
+ while (entries.length !== expectedEntryCount) {
+ await new Promise((resolve) => setTimeout(resolve, interval));
+ entries = await influx.query(`select * from ${SERIES.KONG_REQUEST}`);
+
+ if (retries === actualCount) break;
+ actualCount++;
+ }
+ }
+
+ return entries;
+};
+
+/**
+ * Retrieves all entries from the kong_request mesurement
+ * @returns {Array}
+ */
+export const getAllEntriesFromKongDatastoreCache =
+ async (): Promise => {
+ const entries = await influx.query(
+ `select * from ${SERIES.KONG_DATASTORE_CACHE}`
+ );
+
+ return entries;
+ };
+
+/**
+ * Get all existing data from a particular TAG or field
+ * @param {number} indexOfEntry
+ * @returns {object}
+ */
+export const getAllDataFromTargetTagOrField = async (
+ indexOfEntry: number
+): Promise => {
+ const entries = await influx.query(`select * from ${indexOfEntry}`);
+ return entries;
+};
+
+/**
+ * Execute custom query in influxDB
+ * @param {string} customQuery
+ * @returns {object}
+ */
+export const executeCustomQuery = async (
+ customQuery: string
+): Promise => {
+ const result = await influx.query(customQuery);
+ return result;
+};
+
+/**
+ * Delete all data from kong_request meaasurements
+ */
+export const deleteAllDataFromKongRequest = async () => {
+ await influx.dropSeries({ measurement: SERIES.KONG_REQUEST });
+ return;
+};
+
+/**
+ * Delete all data from kong_datastore_cache meaasurements
+ */
+export const deleteAllDataFromKongDatastoreCache = async () => {
+ await influx.dropSeries({ measurement: SERIES.KONG_DATASTORE_CACHE });
+ return;
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/jwe-keys.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/jwe-keys.ts
new file mode 100644
index 00000000..19e3965c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/jwe-keys.ts
@@ -0,0 +1,77 @@
+import axios from 'axios';
+import { expect } from '../assert/chai-expect';
+import { getUrl } from './entities-gateway';
+import { logResponse } from './logging';
+
+/**
+ * Create a key-set
+ * @param {string} name - key set name, default is null
+ * @returns {AxiosResponse}
+ */
+export const createKeySetsForJweDecryptPlugin = async (name?: string) => {
+ const resp = await axios({
+ method: 'post',
+ url: getUrl('key-sets'),
+ data: { name: name ? name : null },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+/**
+ * Reusable request to delete a Key-Set for jwe-decrypt plugin
+ * @param {string} keySetIdOrName
+ * @returns {AxiosResponse}
+ */
+export const deleteKeySetsForJweDecryptPlugin = async (
+ keySetIdOrName: string
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('key-sets')}/${keySetIdOrName}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ return resp.data;
+};
+
+/**
+ * Create keys
+ * @param {object} keysPayload - request body for keys creation
+ * @returns {AxiosResponse}
+ */
+export const createEncryptedKeysForJweDecryptPlugin = async (
+ keysPayload: object
+) => {
+ const resp = await axios({
+ method: 'post',
+ url: getUrl('keys'),
+ data: keysPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data;
+};
+
+/**
+ * Reusable request to delete Keys for jwe-decrypt plugin
+ * @param {string} keysIdOrName
+ * @returns {AxiosResponse}
+ */
+export const deleteEncryptedKeysForJweDecryptPlugin = async (
+ keysIdOrName: string
+) => {
+ const resp = await axios({
+ method: 'delete',
+ url: `${getUrl('key-sets')}/${keysIdOrName}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ return resp.data;
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/logging.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/logging.ts
new file mode 100644
index 00000000..28192254
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/logging.ts
@@ -0,0 +1,36 @@
+import { AxiosResponse } from 'axios';
+
+/**
+ * Define whether to verbose log request responses or not
+ */
+const isLoggingEnabled = () => {
+ return process.env.VERBOSE_RESPONSE_LOGS !== 'false';
+};
+
+/**
+ * Log the axios response details (url, status, headers, body)
+ * @param {AxiosResponse} response axios response
+ */
+export const logResponse = (response: AxiosResponse): void => {
+ if (isLoggingEnabled()) {
+ console.log('\n');
+ console.log(`URL: ${response.config.url}`);
+ console.log(`METHOD: ${response.config.method?.toUpperCase()}`);
+ console.log(`STATUS: ${response.status}`);
+ console.log('HEADERS:');
+ console.log(response.headers);
+ console.log('BODY:');
+ console.log(response.data);
+ console.log('\n');
+ }
+};
+
+/**
+ * Conditional debug logging to console
+ * @param {string} msg message to be logged
+ */
+export const logDebug = (msg: string): void => {
+ if (isLoggingEnabled()) {
+ console.log('DEBUG: ', String(msg));
+ }
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/metrics.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/metrics.ts
new file mode 100644
index 00000000..664fe12a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/metrics.ts
@@ -0,0 +1,209 @@
+import axios from 'axios';
+import { wait } from './random';
+import { expect } from '../assert/chai-expect';
+import { getBasePath, Environment, isGateway, getNegative, logResponse, logDebug, vars } from '@support';
+import https from 'https';
+
+
+const host = getBasePath({ environment: isGateway() ? Environment.gateway.hostName : undefined });
+const metricsUrl = `https://${host}:8100/metrics`;
+const dataPlaneMetricsUrl =`https://${host}:8101/metrics`;
+const prometheusQueryUrl = `http://${host}:9090/api/v1/query`
+const appDynamicsMetricUrl = 'https://kong-nfr.saas.appdynamics.com/controller/rest/applications'
+
+const appDUser = 'kong-nfr@kong-nfr'
+const appDPassword = vars.app_dynamics.APPD_PASSWORD || ''
+
+
+const agent = new https.Agent({
+ rejectUnauthorized: false,
+});
+
+axios.defaults.httpsAgent = agent;
+
+/**
+ * Send a request to the status API to get all metrics
+ * @param {string} kongNodeName - target metric url
+ * @returns metrics response
+ */
+export const getAllMetrics = async (kongNodeName = "cp") => {
+ const resp = await axios({
+ method: 'get',
+ url: kongNodeName === "cp" ? metricsUrl : dataPlaneMetricsUrl
+ });
+
+ expect(resp.status, 'Status for getting /metrics should be 200').equal(200);
+ return resp.data;
+};
+
+/**
+ * Return a selected value from the metrics API if it is found
+ * @param {string} metricName - name of the metric to get, eg kong_data_plane_config_hash
+ * @returns {string} initial shared dict byte value
+ */
+export const getMetric = async (metricName) => {
+ const metrics = await getAllMetrics();
+ const re = new RegExp(`${metricName}\\{.+\\} (.+)`);
+ const match = metrics.match(re);
+ if (match) return match[1];
+};
+
+/**
+ * Recurse till the configuration hash changes twice and return the hash
+ * @param {string} configHash - initial value of the metric
+ * @param {object} options
+ * @property {number} targetNumberOfConfigHashChanges - times the config hash needs to ne changed to exit the recursion
+ * @property {number} timeout - timeout for waiting for configuration hash update
+ * @returns {string} - the latest configuration hash
+ */
+export const waitForConfigHashUpdate = async (
+ configHash,
+ options: any = {}
+) => {
+ options = { targetNumberOfConfigHashChanges: 1, timeout: 14000, ...options };
+ const currentHash = await getMetric('kong_data_plane_config_hash');
+ const interval = 2000;
+ let timesHashChanged = options.timesHashChanged
+ ? options.timesHashChanged
+ : 0;
+
+ // wait the given timeout period if target number of hash changes wasn't reached
+ if (
+ currentHash === configHash ||
+ timesHashChanged !== options.targetNumberOfConfigHashChanges
+ ) {
+ await wait(interval); // eslint-disable-line no-restricted-syntax
+ }
+
+ // return current hash value only when timeout is reached or the amount of hash changes
+ // equals to the given number. Sometimes hash needs to change twice for kong config changes to take effect
+ if (
+ options?.timeout <= 0 ||
+ timesHashChanged === options.targetNumberOfConfigHashChanges
+ ) {
+ return currentHash;
+ } else {
+ // increase the times that hash has changed only when current hash doesn't equal previous hash
+ if (currentHash !== configHash) {
+ timesHashChanged += 1;
+ }
+
+ // decrease the timeout for next iteration/recursion
+ options.timeout -= interval;
+
+ // note that here we are passing currentHash as parameter instead of configHash
+ // this is done to keep track of how many times the hash has changed
+ return await waitForConfigHashUpdate(currentHash, {
+ targetNumberOfConfigHashChanges: options.targetNumberOfConfigHashChanges,
+ timeout: options.timeout,
+ timesHashChanged,
+ });
+ }
+};
+
+/**
+ * Return the shared dict byte value from the metrics API
+ * @param {string} dict_name - name of the shared dict to check
+ * @returns shared dict byte value
+ */
+export const getSharedDictValue = async (dict_name) => {
+ const metrics = await getAllMetrics();
+ const re = new RegExp(
+ `kong_memory_lua_shared_dict_bytes\\{.+shared_dict="${dict_name}\\} (.+)`
+ );
+ return metrics.match(re)[1];
+};
+
+/**
+ * Return when given shared dict value has changed after a request is sent
+ * @param {string} initialValue - initial value of the metric
+ * @param {string} dict_name - name of the shared dict to check
+ */
+export const waitForDictUpdate = async (initialValue, dict_name) => {
+ let timeWaited = 0;
+ const timeout = 5000;
+
+ if (initialValue != false) {
+ while (
+ (await getSharedDictValue(dict_name)) == initialValue &&
+ timeWaited <= timeout
+ ) {
+ wait(10);
+ timeWaited += 10;
+ }
+ }
+};
+
+/**
+ * Querys the target metrics data from prometheus
+ * @param {string} query - target query to execute
+ */
+export const queryPrometheusMetrics = async (query) => {
+ const url = `${prometheusQueryUrl}?query=${query}`
+
+ const resp = await getNegative(url)
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data.result, `Should receive prometheus query results for ${query}`).to.not.be.empty;
+
+ return resp.data.data
+}
+
+/**
+ * Wait for the given service to appear in AppDynamics
+ * @param {string} serviceName - name of the service to wait for
+ * @param {string} appName - name of the app to wait for
+ * @returns {object} - response from AppDynamics
+*/
+export const waitForAppDMetrics = async (serviceName, appName) => {
+ const timeout = 150000
+ let timeWaited = 0
+ let resp
+ while (timeWaited <= timeout) {
+ resp = await axios({
+ url: `${appDynamicsMetricUrl}/${appName}/metric-data?metric-path=Business%20Transaction%20Performance%7CBusiness%20Transactions%7CSdetTier%7C${serviceName}%7CAverage%20Response%20Time%20%28ms%29&time-range-type=BEFORE_NOW&duration-in-mins=10&output=JSON`,
+ auth: { username: appDUser, password: appDPassword},
+ validateStatus: null,
+ });
+ if (resp.data.length > 0 && resp.data[0].metricName != 'METRIC DATA NOT FOUND') {
+ logResponse(resp)
+ break;
+ }
+ // eslint-disable-next-line no-restricted-syntax
+ await wait(5000)
+ timeWaited += 5000
+ }
+ logResponse(resp)
+ if (resp.data.length === 0) {
+ logDebug(`Service ${serviceName} could not be found in AppDynamics`)
+ }
+ else if(resp.data[0].metricName == 'METRIC DATA NOT FOUND') {
+ logDebug(`Service ${serviceName} exists but no metric data was found`)
+ }
+ return resp;
+}
+
+/**
+ * Querys the target metrics data from appdynamics
+ * @param {string} serviceName - name of the service to query
+ * @param {string} appName - name of the app to query
+ * @param {number} expectedRequestNum - expected number of requests
+* @returns {object} - response from AppDynamics
+ */
+export const queryAppdynamicsMetrics = async (serviceName, appName, expectedRequestNum) => {
+ await waitForAppDMetrics(serviceName, appName)
+ const resp = await axios({
+ url: `${appDynamicsMetricUrl}/${appName}/metric-data?metric-path=Business%20Transaction%20Performance%7CBusiness%20Transactions%7CSdetTier%7C${serviceName}%7CAverage%20Response%20Time%20%28ms%29&time-range-type=BEFORE_NOW&duration-in-mins=10&output=JSON`,
+ auth: { username: appDUser, password: appDPassword},
+ validateStatus: null,
+ });
+ logResponse(resp)
+ expect(resp.status, 'Status should be 200').to.equal(200)
+ expect(resp.data, `Should receive AppDynamics query results for Average Response Time`).to.not.be.empty
+ expect(resp.data[0].metricName, 'Should see correct metric name').to.contain('Average Response Time (ms)')
+ expect(resp.data[0].metricPath, 'Should see service name in metric path').to.contain(serviceName)
+ expect(resp.data[0].metricValues, 'Should see expected metric values').to.not.be.empty
+ expect(resp.data[0].metricValues[0].count, 'Should see expected metric values').to.equal(expectedRequestNum)
+
+ return resp.data
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/negative-axios.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/negative-axios.ts
new file mode 100644
index 00000000..0f656f30
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/negative-axios.ts
@@ -0,0 +1,57 @@
+import axios, { Method } from 'axios';
+import https from 'https';
+
+const agent = new https.Agent({
+ rejectUnauthorized: false,
+});
+
+/**
+ * Sends axios GET request which is expected to fail,
+ * the request promise will always be reject so that test authors can perform checks on the failed response
+ * @param {string} url - Axios request url
+ * @param {object} headers - otpional request headers
+ * @param {object|string} body - request body
+ * @param {object} additionalOptions - { rejectUnauthorized: true } to ignore self-signed cert error
+ * @returns {Object} - response property of the axios error response object
+ */
+export const getNegative = async (
+ url: string,
+ headers: object = {},
+ body?: object | string,
+ additionalOptions?: object | any
+) : Promise =>
+ axios({
+ url,
+ headers,
+ data: body,
+ // Don't raise errors for any status code
+ validateStatus: null,
+ httpsAgent: additionalOptions?.rejectUnauthorized ? agent : null,
+ });
+
+/**
+ * Sends post request which expected to fail,
+ * the request promise will always be rejected so that test authors can perform checks on the failed response
+ * @param {string} url - Axios request url
+ * @param {object} data - Axios request data, defaults to empty object
+ * @param {Method} method - Axios request method, defaults to post
+ * @param {object} headers - Axios request headers, deafults to empty object
+ * @param {object} additionalOptions - { rejectUnauthorized: true } to ignore self-signed cert error
+ * @returns {Object} - response property of the axios error response object
+ */
+export const postNegative = async (
+ url: string,
+ data: object = {},
+ method: Method = 'post',
+ headers: object = {},
+ additionalOptions?: object | any
+) : Promise =>
+ axios({
+ method,
+ headers,
+ url,
+ data,
+ // Don't raise errors for any status code
+ validateStatus: null,
+ httpsAgent: additionalOptions?.rejectUnauthorized ? agent : null,
+ });
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/prog.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/prog.ts
new file mode 100644
index 00000000..f08baf93
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/prog.ts
@@ -0,0 +1,33 @@
+import { execSync } from 'child_process';
+import { logDebug } from './logging';
+import Os from 'os';
+
+/**
+ * Run a given command
+ *
+ * @param {string} command - the command to be run
+ */
+export const execCustomCommand = (command) => {
+ let result;
+
+ try {
+ result = execSync(command, { encoding: 'utf-8', stdio: [] });
+ logDebug(result);
+ } catch (e: any) {
+ result = e;
+ logDebug(e.stderr);
+ }
+
+ return result;
+};
+
+/**
+ * Checks if tests are running on arm64 linux platform (arm64 linux is GH arm64 runner)
+ * @returns {boolean}
+ */
+export const checkForArm64 = () => {
+ const currentArch = Os.arch();
+ const currentPlatform = Os.platform();
+
+ return currentArch === 'arm64' && currentPlatform === 'linux';
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/random.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/random.ts
new file mode 100644
index 00000000..647f84b3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/random.ts
@@ -0,0 +1,26 @@
+import { v4 as uuidv4 } from 'uuid';
+
+/**
+ * @returns {string} - Random string
+ */
+export const randomString = () => {
+ return uuidv4().toUpperCase().split('-')[4];
+};
+
+/**
+ * @param {number} waitTime - number in milliseconds to wait
+ */
+export const wait = async (waitTime: number) => {
+ return await new Promise((resolve) => setTimeout(resolve, waitTime));
+};
+
+/**
+ * Find match of a given regex in a given string and return Boolean
+ * @param {string} regexPattern to search for
+ * @param {string} targetString
+ * @returns {boolean}
+ */
+export const findRegex = (regexPattern, targetString) => {
+ const regex = new RegExp(regexPattern, 'g');
+ return regex.test(targetString);
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/redis.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/redis.ts
new file mode 100644
index 00000000..ac13ba5c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/redis.ts
@@ -0,0 +1,128 @@
+import { createClient } from 'redis';
+import { Environment, getBasePath, isGateway } from '../config/environment';
+import { wait } from './random';
+import { expect } from '../assert/chai-expect';
+
+const redisUser = 'redisuser';
+const redisPassword = 'redispassword';
+
+export let client: any;
+
+export const createRedisClient = () => {
+ const host = getBasePath({ environment: isGateway() ? Environment.gateway.hostName : undefined });
+ const redisConnectUrl = `redis://${redisUser}:${redisPassword}@${host}:6379`;
+ client = createClient({ url: redisConnectUrl });
+};
+
+/**
+ * Gets target redis database key metadata
+ * @param {string} key - redis database key
+ */
+export const getTargetKeyData = async (key: any) => {
+ const rawKeyDetails = await client.hGetAll(key);
+ const keyDetails = Object.entries(rawKeyDetails)[0];
+
+ return { entryCount: keyDetails[1], host: keyDetails[0] };
+};
+
+/**
+ * Gets Redis database size
+ * @param {object} options - options.expectedSize: 2 - to specify target size of DB
+ * @returns {number} - DBSize of redis
+ */
+export const getDbSize = async (options: any = {}) => {
+ let dbSize = await client.DBSIZE();
+
+ if (options?.expectedSize && options?.expectedSize !== dbSize) {
+ console.log(
+ `Getting redis db size one more time as previous one was non-expected: ${dbSize}`
+ );
+ await wait(4000); // eslint-disable-line no-restricted-syntax
+ dbSize = await client.DBSIZE();
+ }
+
+ return dbSize;
+};
+
+/**
+ * Gets all Redis database Keys
+ * @returns {object} - redis database keys
+ */
+export const getAllKeys = async () => {
+ const allKeys = await client.sendCommand(['KEYS', '*']);
+
+ return allKeys;
+};
+
+/**
+ * Shuts down Redis service/container
+ */
+export const shutDownRedis = async () => {
+ return client.sendCommand(['shutdown']);
+};
+
+/**
+ * Clears all entries from Redis database
+ */
+export const resetRedisDB = async () => {
+ return await client.sendCommand(['flushdb']);
+};
+
+/**
+ * Reusable assertion to check standardized redis configuration fields
+ * @param {object} resp - axios admin api plugin response containg the redis fields
+ */
+export const expectRedisFieldsInPlugins = (resp) => {
+ const redisConfigurations = resp.config.redis
+
+ const redisConfigKeys = [
+ 'ssl',
+ 'server_name',
+ 'sentinel_addresses',
+ 'password',
+ 'port',
+ 'ssl_verify',
+ 'connect_timeout',
+ 'send_timeout',
+ 'read_timeout',
+ 'host',
+ 'sentinel_password',
+ 'sentinel_username',
+ 'timeout',
+ 'cluster_addresses',
+ 'database',
+ 'keepalive_backlog',
+ 'keepalive_pool_size',
+ 'sentinel_role',
+ 'sentinel_master',
+ 'username'
+ ]
+
+ expect(redisConfigurations, 'Should have redis object in plugin response').to.be.a(
+ 'object'
+ );
+ expect(Object.keys(redisConfigurations), 'Should have correct number of redis configurations').to.have.lengthOf(redisConfigKeys.length)
+ expect(redisConfigurations, 'Plugin should have correct redis configuration fields').to.have.keys(redisConfigKeys);
+
+ const stringValueKeys = ['server_name', 'sentinel_addresses', 'password', 'host', 'sentinel_password', 'sentinel_username', 'cluster_addresses', 'sentinel_role', 'sentinel_master', 'username'];
+ const numberValueKeys = ['port', 'connect_timeout', 'send_timeout', 'read_timeout', 'timeout', 'database', 'keepalive_backlog', 'keepalive_pool_size'];
+ const booleanValueKeys = ['ssl', 'ssl_verify'];
+
+ stringValueKeys.forEach((key) => {
+ if (redisConfigurations[key] !== null) {
+ expect(redisConfigurations[key], `${key} should be a string`).to.be.a('string');
+ }
+ });
+
+ numberValueKeys.forEach((key) => {
+ if (redisConfigurations[key] !== null) {
+ expect(redisConfigurations[key], `${key} should be a number`).to.be.a('number');
+ }
+ });
+
+ booleanValueKeys.forEach((key) => {
+ if (redisConfigurations[key]!== null) {
+ expect(redisConfigurations[key], `${key} should be a boolean`).to.be.a('boolean');
+ }
+ });
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/retry-axios.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/retry-axios.ts
new file mode 100644
index 00000000..2ccf3aa9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/retry-axios.ts
@@ -0,0 +1,65 @@
+import { AxiosResponse } from 'axios';
+import { logResponse } from './logging';
+
+/**
+ * Retry the given Axios request to match the status code using a timeout and interval
+ * @param {Promise} axiosRequest request to perform
+ * @param {(response: AxiosResponse) => void} assertions assertions to match
+ * @param {number} timeout timeout of retry loop
+ * @param {number} interval interval between tries
+ * @returns {Promise} axios response object
+ */
+export const retryRequest = async (
+ axiosRequest: () => Promise,
+ assertions: (response: AxiosResponse) => void,
+ timeout = 30000,
+ interval = 3000,
+ verbose = false,
+): Promise => {
+ let response: AxiosResponse = {} as any;
+ let errorMsg = '';
+ while (timeout >= 0) {
+ response = await axiosRequest();
+ logResponse(response);
+ try {
+ assertions(response);
+ return response;
+ } catch (error: any) {
+ if (verbose) {
+ errorMsg = error.message;
+ console.log(errorMsg);
+ console.log(
+ `** Assertion(s) Failed -- Retrying in ${interval / 1000} seconds **`
+ );
+ }
+ await new Promise((resolve) => setTimeout(resolve, interval));
+ timeout -= interval;
+ }
+ }
+
+ /*
+ * The last try.
+ *
+ * If we get here, we've timed out,
+ * but we might miss a try in some cases,
+ * For example, if the timeout is 10 seconds,
+ * and the interval is 3 seconds,
+ * we'll try 3 times, the last try will be at 9 seconds.
+ * But the condition might be true at 10 seconds,
+ * and the timeout not is less than 0,
+ * so we'll exit the above loop,
+ * and we'll miss the last try.
+ */
+ try {
+ assertions(response);
+ return response;
+ } catch (error: any) {
+ errorMsg = error.message;
+ console.log(errorMsg);
+ console.log(
+ `** Assertion(s) Failed -- Retrying in ${interval / 1000} seconds **`
+ );
+ }
+
+ throw new Error(errorMsg);
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/status-endpoint.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/status-endpoint.ts
new file mode 100644
index 00000000..9287a6c8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/status-endpoint.ts
@@ -0,0 +1,98 @@
+import axios, { AxiosResponse } from 'axios';
+import https from 'https';
+import {
+ logResponse,
+ getGatewayHost,
+ wait,
+ expect,
+ getBasePath,
+ Environment,
+} from '@support';
+
+const defaultPort = 8100;
+
+const agent = new https.Agent({
+ rejectUnauthorized: false,
+});
+
+axios.defaults.httpsAgent = agent;
+
+const adminUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.adminSec,
+})}`;
+
+/**
+ * Get /status/ready endpoint response
+ * @param {number} port - port to use
+ */
+export const getStatusReadyEndpointResponse = async (
+ port = defaultPort
+) : Promise =>
+ axios({
+ url: `https://${getGatewayHost()}:${port}/status/ready`,
+ validateStatus: null,
+ })
+
+/**
+ * Expect /status/ready to return 200 OK
+ * @param {number} port - port to use
+ */
+export const expectStatusReadyEndpointOk = async (port = defaultPort) => {
+ const response = await getStatusReadyEndpointResponse(port);
+ logResponse(response);
+ expect(response.status).to.equal(200);
+ expect(response.data.message).to.equal('ready');
+};
+
+/**
+ * Expect /status/ready to return 503 with given message
+ * @param {string} message - message to expect
+ * @param {number} port - port to use
+ */
+export const expectStatusReadyEndpoint503 = async (
+ message,
+ port = defaultPort
+) => {
+ const response = await getStatusReadyEndpointResponse(port);
+ logResponse(response);
+
+ expect(response.status).to.equal(503);
+ expect(response.data.message).to.equal(message);
+ return response;
+};
+
+/**
+ * Wait for /status/ready to return given status
+ * @param {number} returnStatus - status to wait for
+ * @param {number} timeout - timeout in ms
+ * @param {number} port - port to use
+ */
+export const waitForTargetStatus = async (
+ returnStatus,
+ timeout,
+ port = defaultPort
+) => {
+ let response;
+ while (timeout > 0) {
+ response = await getStatusReadyEndpointResponse(port);
+ if (response.status === returnStatus) {
+ // log final response
+ logResponse(response);
+ return true;
+ }
+ await wait(1000); // eslint-disable-line no-restricted-syntax
+ timeout -= 1000;
+ }
+ // log last response received
+ logResponse(response);
+ return false;
+};
+
+export const getClusteringDataPlanes = async () => {
+ const resp = await axios({
+ url:`${adminUrl}/clustering/data-planes`,
+ })
+
+ return resp.data
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/validate.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/validate.ts
new file mode 100644
index 00000000..f3aedbd8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/support/utilities/validate.ts
@@ -0,0 +1,21 @@
+/**
+ * Check if the given string is a valid date
+ * @param {string} date target date string
+ * @returns {boolean} if valid date - true; else - false
+ */
+export const isValidDate = (date: string): boolean => {
+ return !isNaN(Date.parse(date));
+};
+
+/**
+ * Check if the given string is a valid URL
+ * @param {string} url target url string
+ * @returns {boolean} if valid url - true; else - false
+ */
+export const isValidUrl = (url: string): boolean => {
+ try {
+ return Boolean(new URL(url));
+ } catch {
+ return false;
+ }
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/_fixtures.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/_fixtures.ts
new file mode 100644
index 00000000..519f5c90
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/_fixtures.ts
@@ -0,0 +1,7 @@
+export async function mochaGlobalSetup() {
+ // global setup goes here
+}
+
+export function mochaGlobalTeardown() {
+ // global teardown goes here
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/1_vitals-influxdb.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/1_vitals-influxdb.spec.ts
new file mode 100644
index 00000000..4dfab243
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/1_vitals-influxdb.spec.ts
@@ -0,0 +1,269 @@
+import {
+ createGatewayService,
+ createInfluxDBConnection,
+ createRouteForService,
+ deleteAllDataFromKongDatastoreCache,
+ deleteAllDataFromKongRequest,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ Environment,
+ executeCustomQuery,
+ expect,
+ getAllEntriesFromKongDatastoreCache,
+ getAllEntriesFromKongRequest,
+ getBasePath,
+ getNegative,
+ getWorkspaces,
+ isGwHybrid,
+ wait,
+ waitForConfigRebuild,
+ isGateway
+} from '@support';
+import axios from 'axios';
+
+describe('Vitals with InfluxDB Tests', function () {
+ this.timeout(120000);
+ const todaysDate = new Date().toISOString().split('T')[0];
+
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+ // isHybrid is being used across the test to control test flow for hybrid mode run
+ const isHybrid = isGwHybrid();
+ // Influxdb flush every 10s so let's wait for 12s to make sure it's definitely flushed
+ const flushWait = 12000;
+
+ const routePath = 'status';
+
+ let serviceId: string;
+ let routeId: string;
+ let serviceId2: string;
+ let routeId2: string;
+ let defaultWorkspaceId: string;
+ let hostname: string;
+
+ before(async function () {
+ // skip classic mode test run due to https://konghq.atlassian.net/browse/KAG-363
+ if (!isHybrid) {
+ this.skip();
+ }
+
+ // connect to influxdb
+ createInfluxDBConnection();
+
+ const service = await createGatewayService('VitalsService', {
+ url: 'http://httpbin/status',
+ });
+ serviceId = service.id;
+
+ const route = await createRouteForService(serviceId, [`/${routePath}`]);
+ routeId = route.id;
+
+ const workspaces: any = await getWorkspaces();
+
+ for (const workspace of workspaces.data) {
+ if (workspace.name === 'default') {
+ defaultWorkspaceId = workspace.id;
+ break;
+ } else {
+ console.log("Couldn't find default workspace id");
+ }
+ }
+
+ await waitForConfigRebuild();
+ await wait(flushWait); // eslint-disable-line no-restricted-syntax
+ await deleteAllDataFromKongRequest();
+ await deleteAllDataFromKongDatastoreCache();
+ });
+
+ const assertKongRequestDetails = (response: any) => {
+ expect(response.time, 'Should have time object in kong_request').to.not.be
+ .null;
+ expect(response.time._nanoISO).to.contain(todaysDate);
+ expect(response.route, 'Should have correct route id').to.eq(routeId);
+ expect(response.service, 'Should have correct service id').to.eq(serviceId);
+ expect(response.hostname, 'Should have correct hostname').to.eq(hostname);
+ expect(response.wid, 'Should have "wid" field in kong_request').to.be
+ .string;
+ expect(response.workspace, 'Should have default workspace id').to.eq(
+ defaultWorkspaceId
+ );
+
+ expect(response.kong_latency, 'Should have integer kong_latency').to.be.a(
+ 'number'
+ );
+ expect(response.proxy_latency, 'Should have integer proxy_latency').to.be.a(
+ 'number'
+ );
+ expect(
+ response.request_latency,
+ 'Should have integer request_latency'
+ ).to.be.a('number');
+ };
+
+ it('should add entry in influxdb kong_request after request', async function () {
+ await axios(`${proxyUrl}/${routePath}/200`);
+ // wait for request metric to be added to influxdb
+ await wait(flushWait); // eslint-disable-line no-restricted-syntax
+
+ const requestEntries: any = await getAllEntriesFromKongRequest(1);
+
+ expect(
+ requestEntries.length,
+ 'Should have 1 entry in kong_request'
+ ).to.equal(1);
+ expect(requestEntries[0].kong_latency, 'Should have kong_latency').to.be.a(
+ 'number'
+ );
+
+ hostname = requestEntries[0].hostname;
+ });
+
+ it('should add entry in influxdb kong_datastore_cache after request', async function () {
+ const cacheEntries: any = await getAllEntriesFromKongDatastoreCache();
+ expect(
+ cacheEntries.length,
+ 'Should have 1 entry in kong_datastore_cache'
+ ).to.equal(1);
+ });
+
+ it('should see Tag keys', async function () {
+ const tagKeys: any = await executeCustomQuery('show TAG keys');
+
+ expect(
+ tagKeys.groupRows[0].name,
+ 'should see kong_datastore_cache tag'
+ ).to.equal('kong_datastore_cache');
+ expect(tagKeys.groupRows[1].name, 'should see kong_request tag').to.equal(
+ 'kong_request'
+ );
+ expect(tagKeys[0], 'Should see tagKey hostname ').to.haveOwnProperty(
+ 'tagKey',
+ 'hostname'
+ );
+ expect(tagKeys[1], 'Should see tagKey wid').to.haveOwnProperty(
+ 'tagKey',
+ 'wid'
+ );
+ });
+
+ it('should see Field keys', async function () {
+ const fieldKeys: any = await executeCustomQuery('show FIELD keys');
+
+ expect(
+ fieldKeys.groupRows[0].name,
+ 'should see kong_datastore_cache field'
+ ).to.equal('kong_datastore_cache');
+ expect(
+ fieldKeys.groupRows[1].name,
+ 'should see kong_request field'
+ ).to.equal('kong_request');
+ expect(fieldKeys[0], 'Should see fieldKey hits').to.haveOwnProperty(
+ 'fieldKey',
+ 'hits'
+ );
+ expect(fieldKeys[0], 'Should see fieldKey hits type').to.haveOwnProperty(
+ 'fieldType',
+ 'integer'
+ );
+ });
+
+ it('should have correct data in kong_request measurement', async function () {
+ const requestEntries: any = await getAllEntriesFromKongRequest();
+
+ assertKongRequestDetails(requestEntries[0]);
+ expect(requestEntries[0].status, 'Should have status 200').to.eq(200);
+ });
+
+ it('should have correct data in kong_datastore_cache measurement', async function () {
+ const cacheEntries: any = await getAllEntriesFromKongDatastoreCache();
+
+ expect(cacheEntries[0].time, 'Should have time object in kong_request').to
+ .not.be.null;
+ expect(cacheEntries[0].hostname, 'Should have correct hostname').to.eq(
+ hostname
+ );
+
+ expect(cacheEntries[0].misses, 'Should have "misses" field').to.be.a(
+ 'number'
+ );
+ expect(cacheEntries[0].hits, 'Should have "hits" field').to.be.a('number');
+ expect(
+ cacheEntries[0].wid,
+ 'Should have "wid" field in kong_datastore_cache'
+ ).be.string;
+ });
+
+ it('should add multiple entries in influxdb kong_request after multiple requests', async function () {
+ await getNegative(`${proxyUrl}/${routePath}/404`);
+ await getNegative(`${proxyUrl}/${routePath}/500`);
+ await getNegative(`${proxyUrl}/${routePath}/200`);
+
+ await wait(flushWait); // eslint-disable-line no-restricted-syntax
+
+ const requestEntries: any = await getAllEntriesFromKongRequest(4);
+
+ expect(
+ requestEntries.length,
+ 'Should have 4 entries in kong_request'
+ ).to.equal(4);
+
+ requestEntries.forEach((entry: any, i: number) => {
+ assertKongRequestDetails(entry);
+
+ if (i === 0 || i === 3) {
+ expect(entry.status, 'Should have status 200').to.eq(200);
+ } else if (i === 1) {
+ expect(entry.status, 'Should have status 404').to.eq(404);
+ } else if (i === 2) {
+ expect(entry.status, 'Should have status 500').to.eq(500);
+ }
+ });
+ });
+
+ it('should see an entry in kong_request for a request with a new route and service', async function () {
+ const service = await createGatewayService('VitalsService2', {
+ url: 'http://httpbin/anything',
+ });
+ serviceId2 = service.id;
+
+ const route = await createRouteForService(serviceId2, ['/influxdb']);
+ routeId2 = route.id;
+
+ await waitForConfigRebuild();
+ await wait(flushWait); // eslint-disable-line no-restricted-syntax
+ await deleteAllDataFromKongRequest();
+ await deleteAllDataFromKongDatastoreCache();
+
+ await axios(`${proxyUrl}/influxdb`);
+ await wait(flushWait); // eslint-disable-line no-restricted-syntax
+
+ const requestEntries: any = await getAllEntriesFromKongRequest(1);
+
+ expect(requestEntries[0].route, 'Should have correct route id').to.eq(
+ routeId2
+ );
+ expect(requestEntries[0].service, 'Should have correct service id').to.eq(
+ serviceId2
+ );
+ expect(requestEntries[0].hostname, 'Should have correct hostname').to.eq(
+ hostname
+ );
+ expect(requestEntries[0].status, 'Should have status 2').to.eq(200);
+ expect(requestEntries[0].time._nanoISO).to.contain(todaysDate);
+ });
+
+ it('should see an entry in kong_datastore_cache for a request with a new route and service', async function () {
+ const cacheEntries: any = await getAllEntriesFromKongDatastoreCache();
+
+ expect(
+ cacheEntries.length,
+ 'Should have 1 entries in kong_datastore_cache'
+ ).to.be.greaterThanOrEqual(1);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteGatewayRoute(routeId2);
+ await deleteGatewayService(serviceId2);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/_hooks.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/_hooks.ts
new file mode 100644
index 00000000..4cec08e1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/_hooks.ts
@@ -0,0 +1,36 @@
+import { clearAllKongResources, createRedisClient, gatewayAuthHeader, isCI, waitForConfigRebuild } from '@support';
+import {
+ postGatewayEeLicense,
+ deleteGatewayEeLicense,
+} from '@shared/gateway_workflows';
+import axios from 'axios';
+
+export const mochaHooks: Mocha.RootHookObject = {
+ beforeAll: async function (this: Mocha.Context) {
+ try {
+ // Set Auth header for Gateway Admin requests
+ const { authHeaderKey, authHeaderValue } = gatewayAuthHeader();
+ axios.defaults.headers[authHeaderKey] = authHeaderValue;
+ createRedisClient();
+ if (isCI()) {
+ // Gateway for API tests starts without EE_LICENSE in CI, hence, we post license at the beginning of all tests to allow us test the functionality of license endpoint
+ await postGatewayEeLicense();
+ // Wait for the license propagation to complete before release to the test
+ await waitForConfigRebuild();
+ console.info('waitForConfigRebuild successfully executed after posting the ee license\n')
+ }
+ } catch (err) {
+ console.error(`Something went wrong in beforeAll hook while rebuilding configuration: ${err}\n`)
+
+ // remove all possible remnant entities from failed waitForConfigRebuild above to start tests from clean state and avoid flakiness
+ await clearAllKongResources();
+ }
+ },
+
+ afterAll: async function (this: Mocha.Context) {
+ // Gateway for API tests starts without EE_LICENSE in CI, hence, we delete license at the end of all tests to allow test rerun from clean state
+ if (isCI()) {
+ await deleteGatewayEeLicense();
+ }
+ },
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/consumer-groups-scope.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/consumer-groups-scope.spec.ts
new file mode 100644
index 00000000..898ee0d7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/consumer-groups-scope.spec.ts
@@ -0,0 +1,297 @@
+import axios from 'axios';
+import {
+ expect,
+ getBasePath,
+ Environment,
+ postNegative,
+ randomString,
+ createConsumer,
+ logResponse,
+ createConsumerGroup,
+ deleteConsumerGroup,
+ createConsumerGroupScopedPlugin,
+ addConsumerToConsumerGroup,
+ createConsumerGroupSettings,
+ createGatewayService,
+ createRouteForService,
+ createKeyAuthCredentialsForConsumer,
+ wait,
+ retryRequest,
+ removeConsumerFromConsumerGroup,
+ waitForConfigRebuild,
+ clearAllKongResources,
+ isGateway,
+} from '@support';
+
+describe('@gke: Gateway Consumer Groups with RLA', function () {
+ this.timeout(45000);
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/consumer_groups`;
+
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ const adminUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+
+ const path = `/${randomString()}`;
+ const key = 'apiKey';
+ const waitTime = 5000;
+ const rtConsumerGroupHeader = 'cgHeader';
+
+ const nonSupportedPlugins = [
+ { name: 'key-auth', config: {} },
+ { name: 'key-auth-enc', config: {} },
+ { name: 'basic-auth', config: {} },
+ { name: 'acl', config: { deny: ['test'] } },
+ ];
+
+ let rtPluginId: string;
+ let consumerGroup1: any;
+ let consumer1: any;
+ let consumer2: any;
+ let serviceId: string;
+
+ before(async function () {
+ await clearAllKongResources();
+ // create service and route
+ const service = await createGatewayService(randomString());
+ serviceId = service.id;
+ await createRouteForService(serviceId, [path]);
+
+ // create key-auth-enc plugin for consumer authentication
+ const pluginResp = await axios({
+ method: 'post',
+ url: `${adminUrl}/plugins`,
+ data: {
+ name: 'key-auth-enc',
+ },
+ });
+ logResponse(pluginResp);
+ expect(pluginResp.status, 'Status should be 201').to.equal(201);
+
+ // create consumer 1 and add key-auth-enc credentials to it
+ const consumer1Req = await createConsumer();
+
+ consumer1 = {
+ id: consumer1Req.id,
+ username: consumer1Req.username,
+ username_lower: consumer1Req.username.toLowerCase(),
+ };
+
+ const consumer1KeyReq = await createKeyAuthCredentialsForConsumer(
+ consumer1.id
+ );
+ consumer1 = { ...consumer1, key: consumer1KeyReq.key };
+
+ // create consumer 2 and add key-auth-enc credentials to it
+ const consumer2Req = await createConsumer();
+
+ consumer2 = {
+ id: consumer2Req.id,
+ username: consumer2Req.username,
+ username_lower: consumer2Req.username.toLowerCase(),
+ };
+
+ const consumer2KeyReq = await createKeyAuthCredentialsForConsumer(
+ consumer2.id
+ );
+ consumer2 = { ...consumer2, key: consumer2KeyReq.key };
+
+ // create a consumer group
+ const consumerGroup1Req = await createConsumerGroup();
+ consumerGroup1 = {
+ id: consumerGroup1Req.id,
+ name: consumerGroup1Req.name,
+ };
+
+ // add consumer 1 to consumer group
+ await addConsumerToConsumerGroup(consumer1.username, consumerGroup1.id);
+ });
+
+ describe('non support plugins', function () {
+ let consumerGroup: any;
+
+ beforeEach(async function () {
+ consumerGroup = await createConsumerGroup();
+ });
+
+ afterEach(async function () {
+ await deleteConsumerGroup(consumerGroup.id);
+ });
+
+ nonSupportedPlugins.forEach(({ name, config }) => {
+ it(`should not create a consumer_group scoped plugin for non-supported plugin ${name}`, async function () {
+ const resp = await postNegative(`${url}/${consumerGroup.id}/plugins`, {
+ name,
+ config,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ });
+ });
+ });
+
+ it('should create a consumer group scoped plugin with group name', async function () {
+ const pluginPayload = {
+ name: 'request-transformer-advanced',
+ config: {
+ add: {
+ headers: ['Plugindefaultconfig:pluginConfig'],
+ },
+ },
+ };
+
+ const resp = await createConsumerGroupScopedPlugin(
+ consumerGroup1.name,
+ pluginPayload
+ );
+
+ logResponse(resp);
+
+ expect(
+ resp.consumer_group.id,
+ 'Should see consumer_group id in the response'
+ ).to.equal(consumerGroup1.id);
+ expect(
+ resp.config.add.headers[0],
+ 'Should see RT plugin header in the response'
+ ).to.equal('Plugindefaultconfig:pluginConfig');
+
+ rtPluginId = resp.id;
+
+ await waitForConfigRebuild(); // add dynamic wait for test stability
+ });
+
+ it('should trigger RT plugin with the correct plugin config', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ headers: { [key]: consumer1.key },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(
+ resp.data.headers.Plugindefaultconfig,
+ 'Should see the correct RT header'
+ ).equal('pluginConfig');
+ };
+
+ await retryRequest(req, assertions, 20000);
+ });
+
+ it('should override RT plugin settings for a consumer group', async function () {
+ const pluginSetting = {
+ add: {
+ headers: [`cg:${rtConsumerGroupHeader}`],
+ },
+ };
+ const resp = await createConsumerGroupSettings(
+ consumerGroup1.id,
+ 'request-transformer',
+ rtPluginId,
+ pluginSetting
+ );
+
+ expect(resp.config.add.headers[0]).to.equal(`cg:${rtConsumerGroupHeader}`);
+ });
+
+ it('should apply overwritten RT plugin consumer group settings to a group consumer', async function () {
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ headers: { [key]: consumer1.key },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.headers.Cg, 'Should see the correct RT header').equal(
+ rtConsumerGroupHeader
+ );
+ expect(
+ resp.data.headers.Plugindefaultconfig,
+ 'Should not add RT plugin default header'
+ ).to.not.exist;
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not apply overwritten RT plugin consumer group settings to a non-group consumer', async function () {
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ headers: { [key]: consumer2.key },
+ });
+
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(
+ resp.data.headers.Cg,
+ 'Should not add RT default header for non-group consumer'
+ ).to.not.exist;
+ expect(
+ resp.data.headers.Plugindefaultconfig,
+ 'Should not add RT plugin overwritten header for non-group consumer'
+ ).to.not.exist;
+ });
+
+ it('should remove consumer 1 and add consumer 2 to the consumer group', async function () {
+ await removeConsumerFromConsumerGroup(
+ consumer1.username,
+ consumerGroup1.id
+ );
+ await addConsumerToConsumerGroup(consumer2.username, consumerGroup1.name);
+ });
+
+ it('should not apply RT plugin CG settings to a consumer1 who was removed from group', async function () {
+ await waitForConfigRebuild({ proxyReqHeader: { [key]: consumer1.key } });
+
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ headers: { [key]: consumer1.key },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(
+ resp.data.headers.Cg,
+ 'Should not add RT header to a consumer who was removed from the group'
+ ).to.not.exist;
+ expect(
+ resp.data.headers.Plugindefaultconfig,
+ 'Should not add RT plugin header for a consumer who was removed from the group'
+ ).to.not.exist;
+ });
+
+ it('should apply RT plugin CG settings to consumer2 who was moved to group 1', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ headers: { [key]: consumer2.key },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(
+ resp.data.headers.Cg,
+ 'Should see the correct RT header after moving to a group'
+ ).equal(rtConsumerGroupHeader);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ after(async function () {
+ await clearAllKongResources();
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/consumer-groups.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/consumer-groups.spec.ts
new file mode 100644
index 00000000..44bf5e0b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/consumer-groups.spec.ts
@@ -0,0 +1,959 @@
+import axios from 'axios';
+import {
+ expect,
+ getBasePath,
+ Environment,
+ getNegative,
+ postNegative,
+ randomString,
+ createConsumer,
+ deleteConsumer,
+ logResponse,
+ isGateway,
+ retryRequest,
+ waitForConfigRebuild,
+} from '@support';
+
+describe('@gke: Gateway Consumer Groups with RLA', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/consumer_groups`;
+ const consumerGroup1Name = randomString();
+ const consumerGroup2Name = randomString();
+ let consumerGroup1: any;
+ let consumerGroup2: any;
+ let consumer1: any;
+ let consumer2: any;
+
+ const assertConsumergroupResponse = (response: any, name: string) => {
+ expect(response.id, 'Should have id').to.be.a('string');
+ expect(response.name, 'Should have correct name').to.equal(name);
+ expect(response.created_at, 'Should have created_at').to.be.a('number');
+ };
+
+ before(async function () {
+ const consumer1Req = await createConsumer();
+ consumer1 = {
+ id: consumer1Req.id,
+ username: consumer1Req.username,
+ username_lower: consumer1Req.username.toLowerCase(),
+ };
+ const consumer2Req = await createConsumer();
+ consumer2 = {
+ id: consumer2Req.id,
+ username: consumer2Req.username,
+ username_lower: consumer2Req.username.toLowerCase(),
+ };
+ });
+
+ it('should not create a consumer group with empty name', async function () {
+ const resp = await postNegative(url, { name: '' });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (name: length must be at least 1)'
+ );
+ expect(
+ resp.data.fields.name,
+ 'Should have correct error field name'
+ ).to.equal('length must be at least 1');
+ });
+
+ it('should not create a consumer group without name', async function () {
+ const resp = await postNegative(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (name: required field missing)'
+ );
+ expect(
+ resp.data.fields.name,
+ 'Should have correct error field name'
+ ).to.equal('required field missing');
+ });
+
+ it('should create a consumer group 1', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: consumerGroup1Name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ assertConsumergroupResponse(resp.data, consumerGroup1Name);
+
+ consumerGroup1 = { id: resp.data.id, name: consumerGroup1Name };
+ await waitForConfigRebuild()
+ });
+
+ it('should see the consumer group 1 in groups list', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+ let found = false;
+
+ expect(
+ resp.data,
+ 'Should not have empty consumer group list'
+ ).not.to.be.ofSize(0);
+
+ for (const group of resp.data.data) {
+ if (group.id === consumerGroup1.id) {
+ expect(
+ group.name,
+ 'Should see the 1st group name in the list'
+ ).to.equal(consumerGroup1.name);
+ found = true;
+ }
+ }
+
+ if (!found) {
+ throw new Error(
+ 'The consumer group 1 was not found in consumer groups list'
+ );
+ }
+ });
+
+ it('should not add non-existing consumer to a consumer group', async function () {
+ const resp = await postNegative(`${url}/${consumerGroup1.name}/consumers`, {
+ consumer: 'nonExisting',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ `Consumer 'nonExisting' not found`
+ );
+ });
+
+ it('should not add consumer to a non-existing consumer group', async function () {
+ const resp = await postNegative(`${url}/wrongGroup/consumers`, {
+ consumer: consumer1.username,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ `Group 'wrongGroup' not found`
+ );
+ });
+
+ it('should not add consumer to a consumer group with empty body', async function () {
+ const resp = await postNegative(`${url}/${consumerGroup1.name}/consumers`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ 'must provide consumer'
+ );
+ });
+
+ it('should add consumer 1 to consumer group 1 using consumer_groups endpoint', async function () {
+ const req = () => axios({
+ method: 'post',
+ url: `${url}/${consumerGroup1.name}/consumers`,
+ data: {
+ consumer: consumer1.username,
+ },
+ });
+
+ const assertions = (resp) => {
+ assertConsumergroupResponse(resp.data.consumer_group, consumerGroup1.name);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.consumers[0].username,
+ 'Should have correct username'
+ ).to.eq(consumer1.username);
+ expect(
+ resp.data.consumers[0].username_lower,
+ 'Should have correct username_lower'
+ ).to.eq(consumer1.username_lower);
+ expect(resp.data.consumers[0].id, 'Should have correct consumer id').to.eq(
+ consumer1.id
+ );
+ expect(resp.data.consumers[0].created_at, 'Should have created_at').to.be.a(
+ 'number'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not add consumer 1 to consumer group 1 2nd time', async function () {
+ const req = () => postNegative(`${url}/${consumerGroup1.name}/consumers`, {
+ consumer: consumer1.username,
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ `Consumer '${consumer1.username}' already in group '${consumerGroup1.id}'`
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should define settings for consumer group 1', async function () {
+ const req = () => axios({
+ method: 'put',
+ url: `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data: {
+ config: {
+ limit: [2],
+ window_size: [5],
+ },
+ },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.plugin, 'Should have correct plugin name').to.eq(
+ 'rate-limiting-advanced'
+ );
+ expect(
+ resp.data.consumer_group,
+ 'Should have correct consumer group name'
+ ).to.eq(consumerGroup1.name);
+ expect(resp.data.config.limit, 'Should have correct limit').to.be.equalTo([
+ 2,
+ ]);
+ expect(
+ resp.data.config.window_size,
+ 'Should have correct window_size'
+ ).to.be.equalTo([5]);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not define settings for a group without limit', async function () {
+ const data = {
+ config: {
+ limit: [2],
+ },
+ };
+ const resp = await postNegative(
+ `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data,
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ 'schema violation (config.window_size: required field missing)'
+ );
+ expect(
+ resp.data.fields.config.window_size,
+ 'Should have window_size in error'
+ ).to.equal('required field missing');
+ });
+
+ it('should not define settings for a group without window_size', async function () {
+ const data = {
+ config: {
+ window_size: [2],
+ },
+ };
+ const resp = await postNegative(
+ `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data,
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ 'schema violation (config.limit: required field missing)'
+ );
+ expect(
+ resp.data.fields.config.limit,
+ 'Should have limit in error'
+ ).to.equal('required field missing');
+ });
+
+ it('should not define settings for a group with unequal window_size and limit', async function () {
+ const data = {
+ config: {
+ limit: [52, 40],
+ window_size: [2],
+ },
+ };
+
+ const req = () => postNegative(
+ `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data,
+ 'put'
+ );
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'You must provide the same number of windows and limits'
+ );
+ expect(
+ resp.data.fields['@entity'][0],
+ 'Should have limit in error'
+ ).to.equal('You must provide the same number of windows and limits');
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not define settings for a group with unequal limit and window_size', async function () {
+ const data = {
+ config: {
+ limit: [52, 59],
+ window_size: [10, 20, 30],
+ },
+ };
+
+ const resp = await postNegative(
+ `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data,
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'You must provide the same number of windows and limits'
+ );
+ expect(
+ resp.data.fields['@entity'][0],
+ 'Should have limit in error'
+ ).to.equal('You must provide the same number of windows and limits');
+ });
+
+ it('should not define settings for a group with non-array limit', async function () {
+ const data = {
+ config: {
+ limit: 52,
+ window_size: [10],
+ },
+ };
+
+ const req = () => postNegative(
+ `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data,
+ 'put'
+ );
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'config.limit: expected an array'
+ );
+ expect(resp.data.fields.config.limit, 'Should have limit error').to.equal(
+ 'expected an array'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not define settings for a group with non-array window_size', async function () {
+ const data = {
+ config: {
+ limit: [52],
+ window_size: 10,
+ },
+ };
+
+ const resp = await postNegative(
+ `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data,
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'config.window_size: expected an array'
+ );
+ expect(
+ resp.data.fields.config.window_size,
+ 'Should have window_size error'
+ ).to.equal('expected an array');
+ });
+
+ it('should get consumer group 1 specific details', async function () {
+ let isConsumer = false;
+ let isPlugin = false;
+
+ const req = () => axios({
+ url: `${url}/${consumerGroup1.name}`,
+ });
+
+ const assertions = (resp) => {
+ assertConsumergroupResponse(resp.data.consumer_group, consumerGroup1.name);
+ expect(
+ resp.data.consumers,
+ 'Should not have empty consumer group list'
+ ).not.to.be.ofSize(0);
+ };
+
+ const resp = await retryRequest(req, assertions);
+
+ for (const consumer of resp.data.consumers) {
+ if (consumer.id === consumer1.id) {
+ expect(
+ consumer.username,
+ 'Should see the 1st consumer name in the group details'
+ ).to.equal(consumer1.username);
+ expect(
+ consumer.username_lower,
+ 'Should have correct username_lower'
+ ).to.equal(consumer1.username_lower);
+ isConsumer = true;
+ }
+ }
+
+ if (!isConsumer) {
+ throw new Error('The consumer was not found in consumer group 1 details');
+ }
+
+ expect(
+ resp.data.plugins,
+ 'Should not have empty consumer group list'
+ ).not.to.be.ofSize(0);
+
+ for (const plugin of resp.data.plugins) {
+ if (plugin.consumer_group.id === consumerGroup1.id) {
+ expect(
+ plugin.config.limit,
+ 'Should have correct limit for the consumer group'
+ ).to.be.equalTo([2]);
+ expect(
+ plugin.config.window_size,
+ 'Should have correct window_size for the consumer group'
+ ).to.be.equalTo([5]);
+ expect(
+ plugin.config.retry_after_jitter_max,
+ 'Should have default jitter number'
+ ).to.eq(0);
+ expect(
+ plugin.config.window_type,
+ 'Should have deafult sliding window_type'
+ ).eq('sliding');
+ expect(plugin.name, 'Should have correct plugin name').eq(
+ 'rate-limiting-advanced'
+ );
+ expect(plugin.id, 'Should have plugin id').to.be.a('string');
+ expect(plugin.created_at, 'Should have created_at number').to.be.a(
+ 'number'
+ );
+ isPlugin = true;
+ }
+ }
+
+ if (!isPlugin) {
+ throw new Error(
+ 'The plugin was not found in plugins list of the consumer group 1'
+ );
+ }
+ });
+
+ it('should not get non-existing consumer group specific details', async function () {
+ const resp = await getNegative(`${url}/wrong`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ `Group 'wrong' not found`
+ );
+ });
+
+ it('should define settings for consumer group 1 with jitter_max', async function () {
+ const req = () => axios({
+ method: 'put',
+ url: `${url}/${consumerGroup1.name}/overrides/plugins/rate-limiting-advanced`,
+ data: {
+ config: {
+ limit: [25, 15],
+ window_size: [55, 25],
+ retry_after_jitter_max: 10,
+ },
+ },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.plugin, 'Should have correct plugin name').to.eq(
+ 'rate-limiting-advanced'
+ );
+ expect(
+ resp.data.consumer_group,
+ 'Should have correct consumer group name'
+ ).to.eq(consumerGroup1.name);
+ expect(resp.data.config.limit, 'Should have correct limit').to.be.equalTo([
+ 25, 15,
+ ]);
+ expect(
+ resp.data.config.window_size,
+ 'Should have correct window_size'
+ ).to.be.equalTo([55, 25]);
+ expect(
+ resp.data.config.retry_after_jitter_max,
+ 'Should have correct jitter_max'
+ ).to.eq(10);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should create a consumer group 2', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: consumerGroup2Name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ assertConsumergroupResponse(resp.data, consumerGroup2Name);
+
+ consumerGroup2 = { id: resp.data.id, name: consumerGroup2Name };
+
+ await waitForConfigRebuild()
+ });
+
+ it('should add consumer 2 to consumer group 2 using consumers endpoint', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer2.username}/consumer_groups`
+ );
+
+ const resp = await axios({
+ method: 'post',
+ url: consumerUrl,
+ data: {
+ group: consumerGroup2.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(
+ resp.data.consumer_groups,
+ 'Should have 1 consumer group in the list'
+ ).to.be.ofSize(1);
+
+ assertConsumergroupResponse(
+ resp.data.consumer_groups[0],
+ consumerGroup2.name
+ );
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.consumer.username, 'Should have correct username').to.eq(
+ consumer2.username
+ );
+ expect(
+ resp.data.consumer.username_lower,
+ 'Should have correct username_lower'
+ ).to.eq(consumer2.username_lower);
+ expect(resp.data.consumer.id, 'Should have correct consumer id').to.eq(
+ consumer2.id
+ );
+ expect(resp.data.consumer.created_at, 'Should have created_at').to.be.a(
+ 'number'
+ );
+ expect(resp.data.consumer.custom_id, 'Should have custom_id null').to.be
+ .null;
+ });
+
+ it('should get all consumer groups of the consumer 1', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer1.username}/consumer_groups`
+ );
+ const resp = await axios(consumerUrl);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.data,
+ 'Should belong to only 1 consumer_group'
+ ).to.be.ofSize(1);
+ expect(
+ resp.data.data[0].name,
+ 'Should have consumer_group 1 name'
+ ).to.equal(consumerGroup1.name);
+ expect(
+ resp.data.data[0].id,
+ 'Should have consumer_group 1 id'
+ ).to.equal(consumerGroup1.id);
+ });
+
+ it('should add consumer 1 to consumer group 2 using consumers endpoint', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer1.username}/consumer_groups`
+ );
+
+ const resp = await axios({
+ method: 'post',
+ url: consumerUrl,
+ data: {
+ group: consumerGroup2.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(
+ resp.data.consumer_groups,
+ 'Should see 1 consumer group in the list'
+ ).to.be.ofSize(1);
+
+ assertConsumergroupResponse(
+ resp.data.consumer_groups[0],
+ consumerGroup2.name
+ );
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.consumer.username, 'Should have correct username').to.eq(
+ consumer1.username
+ );
+ expect(
+ resp.data.consumer.username_lower,
+ 'Should have correct username_lower'
+ ).to.eq(consumer1.username_lower);
+ expect(resp.data.consumer.id, 'Should have correct consumer id').to.eq(
+ consumer1.id
+ );
+ expect(resp.data.consumer.created_at, 'Should have created_at').to.be.a(
+ 'number'
+ );
+ expect(resp.data.consumer.custom_id, 'Should have custom_id null').to.be
+ .null;
+ });
+
+ it('should delete consumer 1 from all consumer groups', async function () {
+ // At this point consumer 2 belongs to consumer group 2
+ // consumer 1 belongs to consumer group 1 and 2
+
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer1.username}/consumer_groups`
+ );
+ const resp = await axios({
+ method: 'delete',
+ url: consumerUrl,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not see consumer 1 in none of consumer groups', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer1.username}/consumer_groups`
+ );
+ const resp = await axios(consumerUrl);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have empty response').to.be.empty;
+ });
+
+ it('should not get all consumer groups of a wrong consumer', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/wrong/consumer_groups`
+ );
+ const resp = await postNegative(consumerUrl);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ `Consumer 'wrong' not found`
+ );
+ });
+
+ it('should see consumer 2 in consumers list of consumer group 2', async function () {
+ const resp = await axios(`${url}/${consumerGroup2.name}/consumers`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.data,
+ 'Should have 1 consumer in consumers list'
+ ).to.be.ofSize(1);
+ expect(
+ resp.data.data[0].username,
+ 'Should have correct username'
+ ).to.equal(consumer2.username);
+ expect(
+ resp.data.data[0].username_lower,
+ 'Should have correct username_lower'
+ ).to.equal(consumer2.username_lower);
+ expect(resp.data.data[0].id, 'Should have correct id').to.equal(
+ consumer2.id
+ );
+ });
+
+ it('should delete consumer 2 by id from consumer group 2', async function () {
+ // At this point only consumer 2 belongs to consumer_group 2
+
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer2.id}/consumer_groups/${consumerGroup2.id}`
+ );
+ const resp = await axios({
+ method: 'delete',
+ url: consumerUrl,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not see consumer 1 in consumers list of consumer group 1', async function () {
+ const resp = await axios(`${url}/${consumerGroup1.id}/consumers`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have empty body for consumers').to.be.empty;
+ });
+
+ it('should not delete wrong consumer from a target consumer group', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/wrong/consumer_groups/${consumerGroup2.id}`
+ );
+ const resp = await postNegative(consumerUrl, {}, 'delete');
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should not delete a consumer from a non existing consumer group', async function () {
+ // At this point only consumer 2 belongs to consumer_group 2
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer2.id}/consumer_groups/1c8feded-b71c-4b2e-82d0-60110d4846c4`
+ );
+
+ const req = () => postNegative(consumerUrl, {}, 'delete');
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should add multiple consumers to consumer group 2', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/${consumerGroup2.name}/consumers`,
+ data: {
+ consumer: [consumer1.username, consumer2.id],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.consumers,
+ 'Should have 2 consumers in the group'
+ ).to.be.ofSize(2);
+ expect(
+ resp.data.consumer_group.name,
+ 'Should have name of consumer group 2'
+ ).to.eq(consumerGroup2Name);
+
+ for (const consumer of resp.data.consumers) {
+ if (consumer.username === consumer1.username) {
+ expect(consumer.username_lower).to.eq(consumer1.username_lower);
+ } else if (consumer.username === consumer2.username) {
+ expect(consumer.username_lower).to.eq(consumer2.username_lower);
+ } else {
+ throw new Error(
+ `Consumer username for ${JSON.stringify(
+ consumer
+ )} was not found in the response`
+ );
+ }
+ }
+ });
+
+ it('should delete consumer 1 by id from consumer group 2 using groups endpoint', async function () {
+ // At this point consumer 1 and 2 belong to consumer group 2
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${consumerGroup2.name}/consumers/${consumer1.id}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ expect(resp.data, 'Should have empty data in response').to.be.empty;
+ });
+
+ it('should delete consumer 2 by name from consumer group 2 using groups endpoint', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${consumerGroup2.name}/consumers/${consumer2.username}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ expect(resp.data, 'Should have empty data in response').to.be.empty;
+ });
+
+ it('should see no consumers in consumers list of consumer group 2', async function () {
+ const req = () => axios(`${url}/${consumerGroup2.id}/consumers`);
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have empty data in response').to.be.empty;
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not delete non existing consumer from consumer group 2 using groups endpoint', async function () {
+ const resp = await postNegative(
+ `${url}/${consumerGroup2.name}/consumers/wrong`,
+ {},
+ 'delete'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ "Consumer 'wrong' not found"
+ );
+ });
+
+ it('should add the same consumer 1 to 2 groups', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer1.username}/consumer_groups`
+ );
+
+ const req = () => axios({
+ method: 'post',
+ url: consumerUrl,
+ data: {
+ group: [consumerGroup1.name, consumerGroup2.id],
+ },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.consumer_groups,
+ 'Should see 1 consumer group in the list'
+ ).to.be.ofSize(2);
+
+ for (const consumerGroup of resp.data.consumer_groups) {
+ if (consumerGroup.id === consumerGroup1.id) {
+ expect(consumerGroup.name).to.eq(consumerGroup1.name);
+ } else if (consumerGroup.id === consumerGroup2.id) {
+ expect(consumerGroup.name).to.eq(consumerGroup2.name);
+ } else {
+ throw new Error(
+ `Consumer group name for ${JSON.stringify(
+ consumerGroup
+ )} was not found in the response`
+ );
+ }
+ }
+
+ expect(resp.data.consumer.username, 'Should have correct username').to.eq(
+ consumer1.username
+ );
+ expect(
+ resp.data.consumer.username_lower,
+ 'Should have correct username_lower'
+ ).to.eq(consumer1.username_lower);
+ expect(resp.data.consumer.id, 'Should have correct consumer id').to.eq(
+ consumer1.id
+ );
+ expect(resp.data.consumer.created_at, 'Should have created_at').to.be.a(
+ 'number'
+ );
+ expect(resp.data.consumer.custom_id, 'Should have custom_id null').to.be
+ .null;
+
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not add the consumer 1 to non-existing group', async function () {
+ const consumerUrl = url.replace(
+ '/consumer_groups',
+ `/consumers/${consumer1.username}/consumer_groups`
+ );
+
+ const req = () => postNegative(consumerUrl, {
+ group: ['non-existing-group'],
+ });
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ "Group 'non-existing-group' not found"
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should delete the consumer group 1 by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${consumerGroup1.id}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not delete a consumer from non existing consumer group using groups endpoint', async function () {
+ const resp = await postNegative(
+ `${url}/wrong/consumers/${consumer1.username}`,
+ {},
+ 'delete'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ "Group 'wrong' not found"
+ );
+ });
+
+ it('should delete the consumer group 2 by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${consumerGroup2.name}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should get 204 for deleting non existing consumer group', async function () {
+ const req = () => axios({
+ method: 'delete',
+ url: `${url}/wrong`,
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ after(async function () {
+ await deleteConsumer(consumer1.id);
+ await deleteConsumer(consumer2.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/key-sets.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/key-sets.spec.ts
new file mode 100644
index 00000000..62231436
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/key-sets.spec.ts
@@ -0,0 +1,179 @@
+import axios, { AxiosResponse } from 'axios';
+import {
+ expect,
+ getNegative,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('@gke: Gateway Admin API: Key-Sets For jwe-decrypt plugin', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/key-sets`;
+
+ const keySetPayload = {
+ name: 'jwe-key-set',
+ };
+ let keySetId = String;
+ let keySetNoNameId = String;
+ let keySetPatchId = String;
+ const tag1 = 'jwe-key-set-tag';
+
+ const assertRespDetails = (response: AxiosResponse) => {
+ const resp = response.data;
+ expect(resp.tags, 'Should not have tags').to.be.null;
+ expect(resp.id, 'Should have id of type string').to.be.a('string');
+ expect(resp.created_at, 'created_at should be a number').to.be.a('number');
+ expect(resp.updated_at, 'updated_at should be a number').to.be.a('number');
+ };
+
+ it('should create a key set', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: keySetPayload,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ keySetPayload.name
+ );
+ assertRespDetails(resp);
+ keySetId = resp.data.id;
+ });
+
+ it('should create a key set without supplying payload', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(resp.data.name, 'Should have "null" as name').equal(null);
+ assertRespDetails(resp);
+ keySetNoNameId = resp.data.id;
+ });
+
+ it('should not create a key set with same name', async function () {
+ const resp = await postNegative(url, keySetPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').equal(409);
+ expect(resp.data.name, 'Should have correct error name').equal(
+ 'unique constraint violation'
+ );
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `UNIQUE violation detected on '{name="${keySetPayload.name}"}'`
+ );
+ });
+
+ it('should get the key set by name', async function () {
+ const resp = await axios(`${url}/${keySetPayload.name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ keySetPayload.name
+ );
+ assertRespDetails(resp);
+ });
+
+ it('should get the key-set by id', async function () {
+ const resp = await axios(`${url}/${keySetId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ keySetPayload.name
+ );
+ assertRespDetails(resp);
+ });
+
+ it('should patch the key set', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${keySetId}`,
+ data: {
+ tags: [tag1],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Array Size should equal 1').length(1);
+ expect(
+ resp.data.tags[0],
+ 'Single tag should be "jwe-key-set-tag"'
+ ).to.equal(tag1);
+ keySetPatchId = resp.data.id;
+ });
+
+ it('should get the recently patched key set', async function () {
+ const resp = await axios(`${url}/${keySetPatchId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ keySetPayload.name
+ );
+ expect(
+ resp.data.tags[0],
+ 'Single tag should be "jwe-key-set-tag"'
+ ).to.equal(tag1);
+ });
+
+ it('should get the key-sets', async function () {
+ const resp = await axios(`${url}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have correct array length').to.have.length(
+ 2
+ );
+ });
+
+ it('should not get the key set by wrong name', async function () {
+ const resp = await getNegative(`${url}/wrong`);
+ logResponse(resp);
+
+ expect(resp.status, 'Should have correct error code').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'Not found'
+ );
+ });
+
+ it('should not get the key set by wrong id', async function () {
+ const resp = await getNegative(
+ `${url}/650d4122-3928-45a1-909d-73921163bb13`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Should respond with error').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'Not found'
+ );
+ });
+
+ it('should delete the key set by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${keySetPayload.name}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should delete the key set by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${keySetNoNameId}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/keyring.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/keyring.spec.ts
new file mode 100644
index 00000000..a3e62a87
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/keyring.spec.ts
@@ -0,0 +1,303 @@
+import axios from 'axios';
+import {
+ expect,
+ getNegative,
+ getBasePath,
+ Environment,
+ createConsumer,
+ createBasicAuthCredentialForConsumer,
+ deleteConsumer,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('@gke: Gateway Admin API: Keyring', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined
+ })}/keyring`;
+ const basicAuthPassword = 'secretPassword';
+ const updatedPassword = 'somenewpassword';
+ let firstActiveKeyId: string;
+ let newGeneratedKeyId: string;
+ let exportedFirstKeyring: string;
+ let exportedNewGeneratedKeyring: string;
+ let consumerData: any;
+ let basicAuthCredentialData: any;
+
+ before(async function () {
+ const consumer = await createConsumer();
+ consumerData = {
+ id: consumer.id,
+ username: consumer.username,
+ username_lower: consumer.username.toLowerCase(),
+ };
+ });
+
+ it('should see all key ids', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.data.active, 'Should have active key').to.be.string;
+ firstActiveKeyId = resp.data.active;
+
+ expect(resp.data.ids, 'should have array of keyring ids').to.be.an('array');
+ expect(
+ resp.data.ids,
+ 'should have at least one id in the ids array'
+ ).not.to.be.ofSize(0);
+ expect(resp.data.ids, 'should contain active keyring id').to.be.containing(
+ firstActiveKeyId
+ );
+ });
+
+ it('should see the active key id', async function () {
+ const resp = await axios(`${url}/active`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.id, 'should have correct active key').to.equal(
+ firstActiveKeyId
+ );
+ });
+
+ it('should export the keyring', async function () {
+ const resp = await axios({
+ method: 'POST',
+ url: `${url}/export`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have a string keyring').to.be.a.string;
+ exportedFirstKeyring = resp.data.data;
+ });
+
+ it('should encrypt basic-auth password and not return it during creation', async function () {
+ const resp = await createBasicAuthCredentialForConsumer(
+ consumerData.username,
+ basicAuthPassword
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.password, 'Should have correct password').to.not.equal(
+ basicAuthPassword
+ );
+ expect(
+ resp.data.password,
+ 'Password should not contain the keyring id'
+ ).to.not.contain(firstActiveKeyId);
+ expect(
+ resp.data.username,
+ 'Should contain the basic-auth credential username in response'
+ ).to.exist;
+
+ basicAuthCredentialData = {
+ password: resp.data.password,
+ username: resp.data.username,
+ id: resp.data.id,
+ };
+ });
+
+ it('should read back the consumer credential by credential id', async function () {
+ // removing /keyring path from the main url
+ const resp = await axios(
+ `${url.split('/keyring')[0]}/consumers/${
+ consumerData.username
+ }/basic-auth/${basicAuthCredentialData.id}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.username,
+ 'Should see the basic-auth username in response'
+ ).equal(basicAuthCredentialData.username);
+ expect(
+ resp.data.password,
+ 'Should have correct password when reading back consumer data'
+ ).to.not.equal(basicAuthPassword);
+ expect(
+ resp.data.password,
+ 'Password should not contain the keyring id when reading back consumer data'
+ ).to.not.contain(firstActiveKeyId);
+ });
+
+ it('should not read back the consumer credential by wrong credential id', async function () {
+ // removing /keyring path from the main url
+ const resp = await getNegative(
+ `${url.split('/keyring')[0]}/consumers/${
+ consumerData.username
+ }/basic-auth/19e936f5-2ee6-4fd7-9461-2dd7097c6091`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'Not found'
+ );
+ });
+
+ it('should generate a new key', async function () {
+ // removing /keyring path from the main url
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/generate`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.key, 'Should have a key in response').to.be.a.string;
+ expect(resp.data.id, 'Should have an id in response').to.be.a.string;
+
+ newGeneratedKeyId = resp.data.id;
+ });
+
+ it('should activate the new key', async function () {
+ // removing /keyring path from the main url
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/activate`,
+ data: {
+ key: newGeneratedKeyId,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should see all keys and the new generated key as active', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.data.active, 'Should see the new key as active').to.equal(
+ newGeneratedKeyId
+ );
+ expect(resp.data.ids, 'should have array of keyring ids').to.be.an('array');
+ expect(
+ resp.data.ids,
+ 'should have at least one id in the ids array'
+ ).to.be.containingAllOf([firstActiveKeyId, newGeneratedKeyId]);
+ });
+
+ it('should export the new generated keyring', async function () {
+ const resp = await axios({
+ method: 'POST',
+ url: `${url}/export`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have a string keyring').to.be.a.string;
+ exportedNewGeneratedKeyring = resp.data.data;
+ });
+
+ it('should import the first exported keyring', async function () {
+ const resp = await axios({
+ method: 'POST',
+ url: `${url}/import`,
+ data: {
+ data: exportedFirstKeyring,
+ },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.active,
+ 'Should see the new imported key as active'
+ ).to.equal(firstActiveKeyId);
+ expect(
+ resp.data.ids,
+ 'should have at least one id in the ids array'
+ ).to.be.containingAllOf([firstActiveKeyId, newGeneratedKeyId]);
+ });
+
+ it('should rotate the basic-auth credentials after keyring import', async function () {
+ const resp = await axios({
+ method: 'PATCH',
+ url: `${url.split('/keyring')[0]}/consumers/${
+ consumerData.username
+ }/basic-auth/${basicAuthCredentialData.id}`,
+ data: {
+ password: updatedPassword,
+ },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.username,
+ 'Should see the basic-auth username in response after password update'
+ ).equal(basicAuthCredentialData.username);
+ expect(
+ resp.data.id,
+ 'Should see the basic-auth credential id after password update'
+ ).equal(basicAuthCredentialData.id);
+ expect(
+ resp.data.password,
+ 'Should not contain old password after update'
+ ).to.not.equal(basicAuthPassword);
+ expect(
+ resp.data.password,
+ 'Should not contain old hashed password after update'
+ ).to.not.equal(basicAuthCredentialData.password);
+ expect(
+ resp.data.password,
+ 'Should not contain new password after update'
+ ).to.not.equal(updatedPassword);
+ expect(
+ resp.data.password,
+ 'Updated password should not contain the keyring id'
+ ).to.not.contain(firstActiveKeyId);
+ });
+
+ it('should import the 2nd exported key', async function () {
+ const resp = await axios({
+ method: 'POST',
+ url: `${url}/import`,
+ data: {
+ data: exportedNewGeneratedKeyring,
+ },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.active,
+ 'Should see the new imported key as active'
+ ).to.equal(newGeneratedKeyId);
+ expect(
+ resp.data.ids,
+ 'should have at least one id in the ids array'
+ ).to.be.containingAllOf([firstActiveKeyId, newGeneratedKeyId]);
+ });
+
+ it('should read back the consumer credential after updating the password and importing new keyring', async function () {
+ const resp = await axios(
+ `${url.split('/keyring')[0]}/consumers/${consumerData.id}/basic-auth/${
+ basicAuthCredentialData.id
+ }`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.username,
+ 'Should see the basic-auth username in response'
+ ).equal(basicAuthCredentialData.username);
+ expect(
+ resp.data.password,
+ 'Should not contain old password in response'
+ ).to.not.equal(basicAuthPassword);
+ expect(
+ resp.data.password,
+ 'Should not contain new password in response'
+ ).to.not.equal(updatedPassword);
+ });
+
+ after(async function () {
+ await deleteConsumer(consumerData.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/keys.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/keys.spec.ts
new file mode 100644
index 00000000..9c008be5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/keys.spec.ts
@@ -0,0 +1,482 @@
+import { authDetails } from '@fixtures';
+import {
+ createKeySetsForJweDecryptPlugin,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ isGateway,
+ logResponse,
+ postNegative,
+} from '@support';
+import axios, { AxiosResponse } from 'axios';
+
+describe('@gke: Gateway Admin API: Keys For jwe-decrypt plugin', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/keys`;
+ const keySets = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/key-sets`;
+
+ const pemKeySetsName = 'pem-jwe-key-sets';
+ const jwkKeySetsName = 'jwk-jwe-key-sets';
+ const keysJwkName = 'jwe-key-sets-jwk';
+ const keysName = 'jwe-keys';
+ const tag1 = 'jwe-tag';
+ const jwkRaw = JSON.parse(authDetails.jwe.jwk);
+
+ let pemKeySetsId = String;
+ let jwkKeySetsId = String;
+ let keysPemId = String;
+ let nullKeysPemId = String;
+ let keysJwkId = String;
+ let keysPayload: any;
+
+ const assertRespDetails = (response: AxiosResponse) => {
+ const resp = response.data;
+ expect(resp.tags, 'Should not have tags').to.be.null;
+ expect(resp.id, 'Should have id of type string').to.be.a('string');
+ expect(resp.created_at, 'created_at should be a number').to.be.a('number');
+ expect(resp.updated_at, 'updated_at should be a number').to.be.a('number');
+ };
+
+ before(async function () {
+ const pemKeySets = await createKeySetsForJweDecryptPlugin(pemKeySetsName);
+ pemKeySetsId = pemKeySets.id;
+ const jwkKeySets = await createKeySetsForJweDecryptPlugin(jwkKeySetsName);
+ jwkKeySetsId = jwkKeySets.id;
+ });
+
+ it('should not create keys if public key field violates schema', async function () {
+ keysPayload = {
+ name: keysName,
+ set: {
+ id: pemKeySetsId,
+ },
+ pem: {
+ private_key: authDetails.jwe.private,
+ public_key: authDetails.cert.certificate,
+ },
+ kid: '42',
+ };
+ const resp = await postNegative(url, keysPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.pem,
+ 'Should indicate public key field is missing'
+ ).to.match(/could not load public key.+/);
+ });
+
+ it('should not create keys if private key field violates schema', async function () {
+ keysPayload = {
+ name: keysName,
+ set: {
+ id: pemKeySetsId,
+ },
+ pem: {
+ public_key: authDetails.jwe.public,
+ private_key: authDetails.cert.certificate,
+ },
+ kid: '42',
+ };
+ const resp = await postNegative(url, keysPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.pem,
+ 'Should indicate private key field is missing'
+ ).to.match(/could not load private key.+/);
+ });
+
+ it('should not create keys if kid field missing', async function () {
+ keysPayload = {
+ name: keysName,
+ set: {
+ id: pemKeySetsId,
+ },
+ pem: {
+ public_key: authDetails.jwe.public,
+ private_key: authDetails.jwe.private,
+ },
+ };
+ const resp = await postNegative(url, keysPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.kid,
+ 'Should indicate kid field is missing'
+ ).to.equal('required field missing');
+ });
+
+ it('should not create keys if kid field violates schema', async function () {
+ keysPayload = {
+ name: keysName,
+ set: {
+ id: pemKeySetsId,
+ },
+ pem: {
+ public_key: authDetails.jwe.public,
+ private_key: authDetails.jwe.private,
+ },
+ kid: 42,
+ };
+ const resp = await postNegative(url, keysPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.kid,
+ 'Should indicate kid field is missing'
+ ).to.equal('expected a string');
+ });
+
+ it('should not create keys if jwk field missing', async function () {
+ keysPayload = {
+ name: keysJwkName,
+ set: {
+ id: jwkKeySetsId,
+ },
+ kid: '42',
+ };
+ const resp = await postNegative(url, keysPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(resp.data.message, 'Should contain "pem" or "jwk"').to.match(
+ /pem|jwk/
+ );
+ });
+
+ it('should not create keys if jwk field violates schema', async function () {
+ keysPayload = {
+ name: keysJwkName,
+ set: {
+ id: jwkKeySetsId,
+ },
+ jwk: jwkRaw,
+ kid: '42',
+ };
+ const resp = await postNegative(url, keysPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.jwk,
+ 'Should indicate jwk field requires a string'
+ ).to.equal('expected a string');
+ });
+
+ it('should not create keys using jwk if jwk and kid values do not match', async function () {
+ keysPayload = {
+ name: keysJwkName,
+ set: {
+ id: jwkKeySetsId,
+ },
+ jwk: authDetails.jwe.jwk,
+ kid: '24',
+ };
+ const resp = await postNegative(url, keysPayload);
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.message,
+ 'Should indicate kid values must be equal'
+ ).to.equal('schema violation (kid in jwk.kid must be equal to keys.kid)');
+ });
+
+ it('should create keys if key set field missing', async function () {
+ keysPayload = {
+ name: 'no-key-set',
+ pem: {
+ public_key: authDetails.jwe.public,
+ private_key: authDetails.jwe.private,
+ },
+ kid: '47',
+ };
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: keysPayload,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(
+ resp.data.pem.public_key,
+ 'Should have public key in the response'
+ ).equal(authDetails.jwe.public);
+ expect(
+ resp.data.pem.private_key,
+ 'Should have private key in the response'
+ ).equal(authDetails.jwe.private);
+ expect(resp.data.name, 'Should have correct entity name').equal(
+ 'no-key-set'
+ );
+ expect(resp.data.set, 'Should have null value for set value').to.be.null;
+ assertRespDetails(resp);
+ nullKeysPemId = resp.data.id;
+ });
+
+ it('should create keys using pem files', async function () {
+ keysPayload = {
+ name: keysName,
+ set: {
+ id: pemKeySetsId,
+ },
+ pem: {
+ public_key: authDetails.jwe.public,
+ private_key: authDetails.jwe.private,
+ },
+ kid: '42',
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: keysPayload,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(
+ resp.data.pem.public_key,
+ 'Should have public key in the response'
+ ).equal(authDetails.jwe.public);
+ expect(
+ resp.data.pem.private_key,
+ 'Should have private key in the response'
+ ).equal(authDetails.jwe.private);
+ expect(resp.data.name, 'Should have correct entity name').equal(
+ keysPayload.name
+ );
+ assertRespDetails(resp);
+ keysPemId = resp.data.id;
+ });
+
+ it('should create keys using jwk', async function () {
+ keysPayload = {
+ name: keysJwkName,
+ set: {
+ id: jwkKeySetsId,
+ },
+ jwk: authDetails.jwe.jwk,
+ kid: jwkRaw.kid,
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: keysPayload,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(resp.data.jwk, 'Should have jwk in the response').equal(
+ authDetails.jwe.jwk
+ );
+ expect(resp.data.name, 'Should have correct entity name').equal(
+ keysPayload.name
+ );
+ assertRespDetails(resp);
+ keysJwkId = resp.data.id;
+ });
+
+ it('should not create pem keys with same name', async function () {
+ const resp = await postNegative(url, keysPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').equal(409);
+ expect(resp.data.name, 'Should have correct error name').equal(
+ 'unique constraint violation'
+ );
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `UNIQUE violation detected on '{name="${keysPayload.name}"}'`
+ );
+ });
+
+ it('should get the pem keys by name', async function () {
+ const resp = await axios(`${url}/${keysPayload.name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should have correct name').equal(keysPayload.name);
+ assertRespDetails(resp);
+ });
+
+ it('should get the jwk keys by name', async function () {
+ const resp = await axios(`${url}/${keysJwkName}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should have correct name').equal(keysJwkName);
+ assertRespDetails(resp);
+ });
+
+ it('should get the pem keys by id', async function () {
+ const resp = await axios(`${url}/${keysPemId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.pem.public_key,
+ 'Should have public key in the response'
+ ).equal(authDetails.jwe.public);
+ expect(
+ resp.data.pem.private_key,
+ 'Should have private key in the response'
+ ).equal(authDetails.jwe.private);
+ assertRespDetails(resp);
+ });
+
+ it('should get the jwk keys by id', async function () {
+ const resp = await axios(`${url}/${keysJwkId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.jwk, 'Should have jwk in the response').equal(
+ authDetails.jwe.jwk
+ );
+ assertRespDetails(resp);
+ });
+
+ it('should get the keys by id', async function () {
+ const resp = await axios(`${url}/${nullKeysPemId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.pem.public_key,
+ 'Should have public key in the response'
+ ).equal(authDetails.jwe.public);
+ expect(
+ resp.data.pem.private_key,
+ 'Should have private key in the response'
+ ).equal(authDetails.jwe.private);
+ assertRespDetails(resp);
+ });
+
+ it('should patch the pem keys', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${keysPayload.name}`,
+ data: {
+ tags: [tag1],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Array Size should equal 1').length(1);
+ expect(resp.data.tags[0], 'Single tag should be jwe-tag').to.equal(tag1);
+ });
+
+ it('should patch the jwk keys', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${keysJwkName}`,
+ data: {
+ tags: [tag1],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Array Size should equal 1').length(1);
+ expect(resp.data.tags[0], 'Single tag should be jwe-tag').to.equal(tag1);
+ });
+
+ it('should not get the keys by wrong name', async function () {
+ const resp = await getNegative(`${url}/wrong`);
+ logResponse(resp);
+
+ expect(resp.status, 'Should have correct error code').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'Not found'
+ );
+ });
+
+ it('should not get the keys by wrong id', async function () {
+ const resp = await getNegative(
+ `${url}/650d4122-3928-45a1-909d-73921163bb13`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Should respond with error').to.equal(404);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'Not found'
+ );
+ });
+
+ it('should delete the jwk key set by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${keySets}/${jwkKeySetsName}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should delete the pem key set by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${keySets}/${pemKeySetsName}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should delete the pem keys by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${keysName}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should delete the null keys by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${nullKeysPemId}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should delete the jwk keys by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${keysJwkName}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/licenses.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/licenses.spec.ts
new file mode 100644
index 00000000..829cf861
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/licenses.spec.ts
@@ -0,0 +1,282 @@
+import axios, { AxiosResponse } from 'axios';
+import { authDetails } from '@fixtures';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ postNegative,
+ deleteGatewayService,
+ logResponse,
+ randomString,
+ createGatewayService,
+ isCI,
+ wait,
+ isGateway,
+} from '@support';
+
+describe('Gateway /licenses API tests', function () {
+ const isCIrun = isCI();
+ const waitTime = 5000;
+ const sampleId = '7ad8a306-cb1f-4b61-8b51-47c3604c3748';
+ const licenseKey = 'ASDASDASDASDASDASDASDASDASD_a1VASASD';
+ const validLicense = authDetails.license.valid;
+ const inValidLicense = authDetails.license.invalid;
+ const validLicenseAws = '{vault://aws/gateway-secret-test/ee_license}'
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/licenses`;
+
+ const pluginsUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+
+ let licenseId: string;
+ let serviceId: string;
+ let basePayload: any;
+
+ const assertBasicRespDetails = (resp: AxiosResponse) => {
+ expect(resp.data.created_at, 'Should have created_at entry').to.be.a(
+ 'number'
+ );
+ expect(resp.data.id, 'Should have id').to.be.string;
+ expect(resp.data.updated_at, 'Should have updated_at entry').to.be.a(
+ 'number'
+ );
+ expect(resp.data.payload, 'Should have payload entry').to.be.string;
+ };
+
+ before(async function () {
+ const service = await createGatewayService(randomString());
+ serviceId = service.id;
+
+ basePayload = {
+ name: 'key-auth-enc',
+ service: {
+ id: serviceId,
+ },
+ };
+ });
+
+ it('should GET all licenses and see the existing license', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should see 1 license in licenses').to.have.lengthOf(
+ 1
+ );
+ licenseId = resp.data.data[0].id;
+ });
+
+ it('should delete the license by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${licenseId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should not POST an invalid license', async function () {
+ const resp = await postNegative(url, { payload: inValidLicense });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should see correct error message').to.include(
+ 'Unable to validate license: validation failed'
+ );
+ });
+
+ it('should not PATCH create a valid license', async function () {
+ const resp = await postNegative(
+ `${url}/${sampleId}`,
+ { payload: validLicense },
+ 'PATCH'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should see correct error message').to.include(
+ 'could not find the entity with primary key'
+ );
+ });
+
+ if (isCIrun) {
+ it('should not enable key-auth-enc ee plugin without license', async function () {
+ const pluginPayload = {
+ name: 'key-auth-enc',
+ config: { key_names: ['apiKey'] },
+ };
+
+ const resp = await postNegative(pluginsUrl, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(
+ resp.data.message,
+ 'Should see correct error message for plugin creation'
+ ).to.include(`'key-auth-enc' is an enterprise only plugin`);
+ });
+ }
+
+ it('should POST a valid license', async function () {
+ const resp = await postNegative(url, { payload: validLicense });
+ logResponse(resp);
+
+ licenseId = resp.data.id;
+ assertBasicRespDetails(resp);
+ });
+
+ if (isCIrun) {
+ it('should enable key-auth-enc ee plugin with license', async function () {
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+
+ const pluginPayload = {
+ ...basePayload,
+ config: { key_names: ['apiKey'] },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url: pluginsUrl,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ });
+ }
+
+ it('should not POST a duplicate valid license', async function () {
+ const resp = await postNegative(url, { payload: validLicense });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should see correct error message').to.include(
+ 'UNIQUE violation detected'
+ );
+ });
+
+ it('should not PUT a duplicate valid license with different id', async function () {
+ const resp = await postNegative(
+ `${url}/${sampleId}`,
+ { payload: validLicense },
+ 'PUT'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should see correct error message').to.include(
+ 'UNIQUE violation detected'
+ );
+ });
+
+ it('should not PATCH an existing license with invalid poayload', async function () {
+ const resp = await postNegative(
+ `${url}/${licenseId}`,
+ { payload: inValidLicense },
+ 'PATCH'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should see correct error message').to.include(
+ 'Unable to validate license: validation failed'
+ );
+ });
+
+ it('should PATCH an existing license with valid poayload', async function () {
+ const resp = await postNegative(
+ `${url}/${licenseId}`,
+ { payload: validLicense },
+ 'PATCH'
+ );
+ logResponse(resp);
+
+ assertBasicRespDetails(resp);
+ });
+
+ it('should PUT a duplicate valid license with same primary key id', async function () {
+ const resp = await postNegative(
+ `${url}/${licenseId}`,
+ { payload: validLicense },
+ 'PUT'
+ );
+ logResponse(resp);
+
+ assertBasicRespDetails(resp);
+ });
+
+ it('should GET the license by id', async function () {
+ const resp = await axios(`${url}/${licenseId}`);
+ logResponse(resp);
+
+ assertBasicRespDetails(resp);
+ });
+
+ it('should GET the license report', async function () {
+ const reportUrl = `${url.split('licenses')[0]}license/report`;
+ const resp = await axios(reportUrl);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.license.license_key, 'Should see correct license key').to.eq(
+ licenseKey
+ );
+ expect(resp.data.plugins_count, 'Should see plugins_count in response').to
+ .exist;
+ expect(resp.data.deployment_info, 'Should see deployment_info in response').to
+ .exist;
+ expect(resp.data.timestamp, 'Should see timestamp in response').to
+ .exist;
+ expect(resp.data.checksum, 'Should see checksum in response').to
+ .exist;
+ expect(resp.data.system_info, 'Should see system_info in response').to
+ .exist;
+ expect(
+ resp.data.counters.buckets[0].request_count,
+ 'Should see request_count'
+ ).to.be.greaterThanOrEqual(0);
+ expect(
+ resp.data.counters.total_requests,
+ 'Should see total_requests'
+ ).to.be.greaterThanOrEqual(0);
+
+ expect(
+ resp.data.workspaces_count,
+ 'Should see workspaces_count in response'
+ ).to.eq(1);
+ expect(
+ resp.data.db_version,
+ 'Should see db_version in response'
+ ).to.include('postgres');
+ });
+
+ // unskip when https://konghq.atlassian.net/browse/KAG-4341 is fixed
+ it.skip('should delete the license and post a new one from aws vault', async function () {
+ let resp = await axios({
+ method: 'delete',
+ url: `${url}/${licenseId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ resp = await postNegative(url, { payload: validLicenseAws });
+ logResponse(resp);
+ expect(resp.data.message, 'Should not see license decode error').to.not.include('Unable to validate license: could not decode license json')
+
+ licenseId = resp.data.id;
+ assertBasicRespDetails(resp);
+ });
+
+ after(async function () {
+ // safegurad subsequent tests from failing due to ee license by additionally posting the ee license
+ const resp = await postNegative(url, { payload: validLicense });
+ logResponse(resp);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/log-levels-dp.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/log-levels-dp.spec.ts
new file mode 100644
index 00000000..3daf9596
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/log-levels-dp.spec.ts
@@ -0,0 +1,174 @@
+import axios from 'axios';
+import {
+ expect,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ wait,
+ isGwHybrid,
+ getGatewayContainerLogs,
+ findRegex,
+ isGateway,
+ eventually,
+ getClusteringDataPlanes,
+} from '@support';
+
+// unskip this test suite whenever the RPC changes are re-enabled
+// https://konghq.atlassian.net/browse/KAG-4407
+describe.skip('RPC Log Level Tests: Data Plane', function () {
+ this.timeout(40000);
+
+ let currentLogs: any;
+ let dpNodeId: string;
+ let url: string;
+
+ const isHybrid = isGwHybrid();
+ const kongContainerName = 'kong-dp1';
+
+ const logLevels = ['warn', 'info', 'error', 'crit', 'debug'];
+ const wrongLogLevels = ['debian', 10, '198', 'err'];
+ const originalLogLevel = 'info'
+
+ before(async function () {
+ // skip test runs for classic mode
+ if (!isHybrid) {
+ this.skip();
+ }
+
+ const resp = await getClusteringDataPlanes();
+ dpNodeId = resp.data[0].id
+
+ url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/clustering/data-planes/${dpNodeId}/log-level`;
+ });
+
+ it('should get current dp log level', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.current_level, 'Should have correct current log-level').to.equal(
+ originalLogLevel
+ );
+ expect(resp.data.original_level, 'Should have correct original log-level').to.equal(
+ originalLogLevel
+ );
+ expect(resp.data.timeout, 'Should see timeout').to.equal(0);
+ });
+
+ wrongLogLevels.forEach((wrongLogLevel) => {
+ it(`should not change dp log-level to wrong level ${wrongLogLevel}`, async function () {
+ const resp = await postNegative(
+ url,
+ {current_level: wrongLogLevel},
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 500').to.equal(500);
+ expect(resp.data.message, 'Should have correct error message for wrong log level').to.include(
+ `unknown log level: ${wrongLogLevel}`
+ );
+ });
+ });
+
+ logLevels.forEach((logLevel) => {
+ it(`should change dp node log-level to ${logLevel}`, async function () {
+ let resp = await axios({
+ method: 'put',
+ url: url,
+ data: {current_level: logLevel}
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.original_level, 'Should have unchanged original log-level').to.equal(
+ originalLogLevel
+ );
+ expect(resp.data.current_level, 'Should have changed current log-level').to.equal(
+ logLevel
+ );
+
+ if (logLevel === originalLogLevel) {
+ expect(resp.data.timeout, 'Should see correct timeout for original log-level').to.equal(0);
+ } else {
+ expect(resp.data.timeout, 'Should see 60s or less timeout for new log-levels').to.be.lte(
+ 60
+ );
+ }
+ });
+ });
+
+ it('should change dp log-level to debug for 10 seconds', async function () {
+ const resp = await postNegative(
+ url,
+ {current_level: 'debug', timeout: 10},
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ });
+
+ it('should get and see the updated log-level', async function () {
+ const resp = await axios(url)
+ logResponse(resp);
+
+ expect(resp.data.timeout, 'Should see less than 10 seconds timeout').to.be.lte(
+ 10
+ );
+ expect(resp.data.current_level, 'Should have changed current log-level').to.equal(
+ 'debug'
+ );
+ });
+
+ it('should see debug logs in container', async function () {
+ await eventually(async () => {
+ currentLogs = getGatewayContainerLogs(
+ kongContainerName, 5
+ );
+
+ const isLogFound = findRegex('\\[debug\\]', currentLogs);
+ expect(
+ isLogFound,
+ 'Should see debug logs after setting log-level to debug'
+ ).to.be.true;
+ });
+ });
+
+ it('should not see debug logs in data plane after timeout is expired', async function () {
+ // wait for 6 sseconds for timeout to expire
+ // eslint-disable-next-line no-restricted-syntax
+ await wait(6000)
+
+ await eventually(async () => {
+ currentLogs = getGatewayContainerLogs(
+ kongContainerName, 3
+ );
+ const isLogFound = findRegex('\\[debug\\]', currentLogs);
+ expect(
+ isLogFound,
+ 'Should not see debug logs after setting log-level to notice'
+ ).to.be.false;
+ });
+ });
+
+ it('should get and see the original log-level after timeout expire', async function () {
+ const resp = await axios(url)
+ logResponse(resp);
+
+ expect(resp.data.current_level, 'Should have changed to original log-level after timeout').to.equal(
+ originalLogLevel
+ );
+ expect(resp.data.original_level, 'Should see original log-level').to.equal(
+ originalLogLevel
+ );
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/log-levels.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/log-levels.spec.ts
new file mode 100644
index 00000000..d58af57a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/log-levels.spec.ts
@@ -0,0 +1,510 @@
+import axios from 'axios';
+import {
+ expect,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ randomString,
+ deleteGatewayService,
+ deleteGatewayRoute,
+ createRouteForService,
+ createGatewayService,
+ wait,
+ isGwHybrid,
+ getGatewayContainerLogs,
+ findRegex,
+ addRoleToUser,
+ createRoleEndpointPermission,
+ createRole,
+ createUser,
+ deleteUser,
+ getNegative,
+ deleteRole,
+ createPlugin,
+ getKongContainerName,
+ retryRequest,
+ isGateway,
+ eventually,
+} from '@support';
+
+describe('Dynamic Log Level Tests', function () {
+ this.timeout(30000);
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/debug`;
+
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+
+ let serviceId: string;
+ let routeId: string;
+ let currentLogs: any;
+ const path = `/${randomString()}`;
+ const isHybrid = isGwHybrid();
+ const kongContainerName = getKongContainerName();
+
+ const logUrl = isHybrid
+ ? 'cluster/control-planes-nodes/log-level'
+ : 'cluster/log-level';
+
+ const logLevels = ['warn', 'info', 'error', 'crit', 'debug'];
+ const wrongLogLevels = ['debian', 10, '198', 'err'];
+ const logLevelEndpoints = ['/debug/node/log-level', `/debug/${logUrl}`];
+
+ const user = { name: randomString(), token: randomString(), id: '' };
+ const userHeader = { 'kong-admin-token': user.token };
+ const role = { name: randomString(), id: '' };
+
+ before(async function () {
+ const service = await createGatewayService(randomString());
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ // create role
+ const roleReq = await createRole(role.name);
+ role.id = roleReq.id;
+
+ // create user
+ const userReq = await createUser(user.name, user.token);
+ user.id = userReq.id;
+
+ // attach role to a user
+ await addRoleToUser(user.name, role.name);
+
+ // create role endpoint permission to read logLevelEndpoints[0]
+ await createRoleEndpointPermission(role.id, logLevelEndpoints[0], 'read');
+ // create role endpoint permission to have all permissions on logLevelEndpoints[0]/crit
+ await createRoleEndpointPermission(role.id, `${logLevelEndpoints[0]}/crit`);
+ // create role endpoint permission to have all permissions on logLevelEndpoints[1]/*
+ await createRoleEndpointPermission(
+ role.id,
+ `${logLevelEndpoints[1]}/*`,
+ 'update'
+ );
+
+ await wait(isHybrid ? 7000 : 5000); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should get current log level', async function () {
+ const resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ 'Current log level: info'
+ );
+ });
+
+ logLevels.forEach((logLevel) => {
+ it(`should change node log-level to ${logLevel}`, async function () {
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/node/log-level/${logLevel}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should change log level').to.equal(
+ `Log level changed to ${logLevel}`
+ );
+
+ resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: ${logLevel}`
+ );
+ });
+ });
+
+ it('should have correct message when log-level is already set', async function () {
+ const lastLogLevelInTheList = logLevels[logLevels.length - 1];
+ const resp = await postNegative(
+ `${url}/node/log-level/${lastLogLevelInTheList}`,
+ {},
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `Log level is already ${lastLogLevelInTheList}`
+ );
+ });
+
+ it('should proxy traffic after log-level change', async function () {
+ const resp = await axios(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should see debug logs in container', async function () {
+ await wait(3000); // eslint-disable-line no-restricted-syntax
+ const logs = getGatewayContainerLogs(kongContainerName);
+ const isLogFound = findRegex('\\[debug\\]', logs);
+ expect(isLogFound, 'Should see debug logs').to.be.true;
+ });
+
+ it(`should change node log-level to notice`, async function () {
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/node/log-level/notice`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should change log level').to.equal(
+ `Log level changed to notice`
+ );
+
+ resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: notice`
+ );
+ });
+
+ it('should see notice logs after log is set to notice', async function () {
+ await eventually(async () => {
+ currentLogs = getGatewayContainerLogs(
+ kongContainerName
+ );
+ const isLogFound = findRegex('\\[notice\\]', currentLogs);
+ expect(
+ isLogFound,
+ 'Should see notice logs after setting log-level to notice'
+ ).to.be.true;
+ });
+ });
+
+ it('should see notice logs after log is set to notice', async function () {
+ const isLogFound = findRegex('\\[notice\\]', currentLogs);
+ expect(
+ isLogFound,
+ 'Should see notice logs after setting log-level to notice'
+ ).to.be.true;
+ });
+
+ wrongLogLevels.forEach((wrongLogLevel) => {
+ it(`should not change log-level to wrong level ${wrongLogLevel}`, async function () {
+ const resp = await postNegative(
+ `${url}/node/log-level/${wrongLogLevel}`,
+ {},
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct message').to.include(
+ `Unknown log level: ${wrongLogLevel}`
+ );
+ });
+ });
+
+ it('should not see debug logs in container after log is set to notice', async function () {
+ await eventually(async () => {
+ currentLogs = getGatewayContainerLogs(
+ kongContainerName
+ );
+ const isLogFound = findRegex('\\[debug\\]', currentLogs);
+ expect(
+ isLogFound,
+ 'Should not see debug logs after setting log-level to notice'
+ ).to.be.false;
+ });
+ });
+
+ it('should change log-level for all cluster CP nodes', async function () {
+ const logLevel = 'error';
+ const resp = await postNegative(`${url}/${logUrl}/${logLevel}`, {}, 'put');
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should change log level').to.equal(
+ `Log level changed to ${logLevel}`
+ );
+ });
+
+ it('should have correct message when log-level is already set for cluster CP nodes', async function () {
+ const logLevel = 'error';
+ const resp = await postNegative(`${url}/${logUrl}/${logLevel}`, {}, 'put');
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `Log level is already ${logLevel}`
+ );
+ });
+
+ it('should have correct message for setting node log-level when log-level is already set for Cluster', async function () {
+ const logLevel = 'error';
+ const resp = await postNegative(
+ `${url}/node/log-level/${logLevel}`,
+ {},
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct message').to.include(
+ `Log level is already ${logLevel}`
+ );
+ });
+
+ it('should get log-level after changing it for all cluster CP nodes', async function () {
+ const logLevel = 'error';
+ const resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: ${logLevel}`
+ );
+ });
+
+ it(`should change node log-level to alert for 10 seconds`, async function () {
+ // creating datadog plugin with wrong config to simulate error logs
+ await createPlugin({
+ name: 'datadog',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ });
+
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/${logUrl}/alert?timeout=10`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should change log level').to.equal(
+ `Log level changed to alert`
+ );
+
+ await wait(2000); // eslint-disable-line no-restricted-syntax
+
+ resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: alert`
+ );
+ });
+
+ it('should not see error logs after log-level is set to alert', async function () {
+ // sending request to simulate DD error in the logs
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ const logs = getGatewayContainerLogs(kongContainerName);
+
+ const isLogFound = findRegex('\\[error\\]', logs);
+ const isInfoLogFound = findRegex('\\[info\\]', logs);
+ const isDebugLogFound = findRegex('\\[debug\\]', logs);
+
+ [isLogFound, isInfoLogFound, isDebugLogFound].forEach((logLevel) => {
+ expect(
+ logLevel,
+ `Should not see ${logLevel} logs after setting log-level to alert`
+ ).to.be.false;
+ });
+ });
+
+ it('should see log level change back to info after 10 seconds', async function () {
+ // checking the the log level is info after 10 seconds alert
+ const req = () => axios(`${url}/node/log-level`);
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: info`
+ );
+ };
+
+ /*
+ * Adding 5 seconds to the timeout to avoid flakiness
+ * as the behavior of the timeout of the log_level is not very exact.
+ *
+ * For example, if we set the timeout to 10 seconds,
+ * the log level will be changed back to info after 10 seconds,
+ * but the request to get the log level might be sent at 10.00001 seconds,
+ * the internal mechanism of the timeout of the log_level
+ * can't be that exact, so we might see the log level is not expected.
+ */
+ await retryRequest(req, assertions, 12000);
+ });
+
+ /*
+ * FIXME: skipping this test to avoid breaking the CI for the moment.
+ *
+ * This test is consistently failing only for x86 arch and I can't figure
+ * out why.
+ */
+ it.skip('should see error logs as alert level was changed to info after 10 seconds', async function () {
+ // sending request to simulate DD error in the logs
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ await wait(isHybrid ? 7000 : 2000); // eslint-disable-line no-restricted-syntax
+ const logs = getGatewayContainerLogs(
+ isHybrid ? 'kong-dp1' : kongContainerName
+ );
+
+ const isLogFound = findRegex('\\[error\\]', logs);
+ expect(isLogFound, 'Should see error logs').to.be.true;
+ });
+
+ it('should change log-level back to info with timeout of 0 seconds', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/node/log-level/notice?timeout=0`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ const req = () => axios({
+ method: 'get',
+ url: `${url}/node/log-level`,
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: info`
+ );
+ };
+
+ await retryRequest(req, assertions, 10000);
+ });
+
+ describe('Dynamic Log Level RBAC Permissions for a User', function () {
+ it('should GET /node/log-level with permission', async function () {
+ const resp = await axios({
+ url: `${url}/node/log-level`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: info`
+ );
+ });
+
+ it('should not GET /node/log-level with wrong token', async function () {
+ const resp = await getNegative(`${url}/node/log-level`, {
+ 'kong-admin-token': 'wrong',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ });
+
+ it(`should PUT /node/log-level/crit with permission`, async function () {
+ const logLevel = 'crit';
+
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/node/log-level/${logLevel}`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should change log level').to.equal(
+ `Log level changed to ${logLevel}`
+ );
+
+ resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: ${logLevel}`
+ );
+ });
+
+ it(`should not PUT /node/log-level/alert with no permission`, async function () {
+ const logLevel = 'alert';
+
+ const resp = await postNegative(
+ `${url}/node/log-level/${logLevel}`,
+ {},
+ 'put',
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ });
+
+ it(`should PUT ${logUrl}/notice with ${logUrl}/* update permission`, async function () {
+ const logLevel = 'notice';
+
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/${logUrl}/${logLevel}`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should change log level').to.equal(
+ `Log level changed to ${logLevel}`
+ );
+
+ resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: ${logLevel}`
+ );
+ });
+
+ it(`should PUT ${logUrl}/warn with ${logUrl}/* update permission`, async function () {
+ const logLevel = 'warn';
+
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/${logUrl}/${logLevel}`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should change log level').to.equal(
+ `Log level changed to ${logLevel}`
+ );
+
+ resp = await axios(`${url}/node/log-level`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.message, 'Should have correct log-level').to.equal(
+ `Current log level: ${logLevel}`
+ );
+ });
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteUser(user.id);
+ await deleteRole(role.id);
+
+ // set log-level back to info
+ const resp = await postNegative(`${url}/node/log-level/info`, {}, 'put');
+ logResponse(resp);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/e2e-user-permissions.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/e2e-user-permissions.spec.ts
new file mode 100644
index 00000000..26c96a25
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/e2e-user-permissions.spec.ts
@@ -0,0 +1,400 @@
+import axios from 'axios';
+import {
+ expect,
+ getNegative,
+ postNegative,
+ getBasePath,
+ Environment,
+ createRole,
+ deleteRole,
+ randomString,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ createUser,
+ deleteUser,
+ addRoleToUser,
+ createRoleEndpointPermission,
+ createRoleEntityPermission,
+ createPlugin,
+ deletePlugin,
+ deleteRoleEndpointPermission,
+ isGwHybrid,
+ wait,
+ logResponse,
+ retryRequest,
+ isGateway,
+} from '@support';
+
+describe('@gke: Gateway RBAC: E2E User Permissions', function () {
+ const isHybrid = isGwHybrid();
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+
+ const updateRoleName = 'rbacUpdatedRoleName';
+ const workspaceName = 'default';
+ const serviceName = randomString();
+ const servicesEndpoint = '/services/*';
+ const routesEndpoint = '/routes/*';
+ const consumersEndpoint = '/consumers';
+ const user = { name: randomString(), token: randomString(), id: '' };
+ const userHeader = { 'kong-admin-token': user.token };
+ const pluginPayload = {
+ name: 'basic-auth',
+ config: {
+ hide_credentials: true,
+ },
+ };
+ const role = { name: randomString(), id: '' };
+ const serviceEntity = { id: '', type: 'services' };
+ const routeEntity = { id: '', type: 'routes' };
+ let pluginId: string;
+
+ before(async function () {
+ // create service
+ const serviceReq = await createGatewayService(serviceName);
+ serviceEntity.id = serviceReq.id;
+
+ // create route for the service
+ const routeReq = await createRouteForService(serviceEntity.id);
+ routeEntity.id = routeReq.id;
+
+ // create a basic-auth plugin
+ const pluginReq = await createPlugin(pluginPayload);
+ pluginId = pluginReq.id;
+
+ // create role
+ const roleReq = await createRole(role.name);
+ role.id = roleReq.id;
+
+ // create user
+ const userReq = await createUser(user.name, user.token);
+ user.id = userReq.id;
+
+ // attach role to a user
+ await addRoleToUser(user.name, role.name);
+
+ // create role endpoint permission
+ await createRoleEndpointPermission(
+ role.id,
+ servicesEndpoint,
+ 'update,delete',
+ true
+ );
+ await createRoleEndpointPermission(role.id, routesEndpoint, 'read', true);
+ await createRoleEndpointPermission(role.id, consumersEndpoint, '*');
+
+ // create role entity permission
+ await createRoleEntityPermission(role.id, pluginId, 'plugins', 'delete');
+ });
+
+ it(`should see all role permissions`, async function () {
+ const resp = await axios(`${url}/rbac/roles/${role.id}/permissions`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.endpoints.default[`/default${routesEndpoint}`].actions,
+ `Should see read action for route endpoint`
+ ).to.deep.include({ read: { negative: true } });
+
+ expect(
+ resp.data.endpoints.default[`/default${routesEndpoint}`].actions,
+ `Should see no other action than 'read'`
+ ).to.not.deep.include({ update: { negative: true } });
+
+ ['update', 'delete'].forEach((action) => {
+ expect(
+ resp.data.endpoints.default[`/default${servicesEndpoint}`].actions,
+ `Should see read action for service endpoint`
+ ).to.deep.include({ [action]: { negative: true } });
+ });
+
+ expect(
+ resp.data.endpoints.default[`/default${servicesEndpoint}`].actions,
+ `Should see no other action than 'update, delete'`
+ ).to.not.deep.include({ read: { negative: true } });
+
+ ['create', 'update', 'delete', 'read'].forEach((action) => {
+ expect(
+ resp.data.endpoints.default[`/default${consumersEndpoint}`].actions,
+ `Should see action ${action} for consumers endpoint`
+ ).to.deep.include({ [action]: { negative: false } });
+ });
+
+ expect(
+ resp.data.entities[`${pluginId}`].actions,
+ 'Should see delete action for plugin entity'
+ )
+ .to.be.ofSize(1)
+ .and.have.members(['delete']);
+ });
+
+ it('should not have permission to delete the plugin entity', async function () {
+ const resp = await postNegative(
+ `${url}/plugins/${pluginId}`,
+ {},
+ 'delete',
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ expect(resp.data.message, 'Should have correct forbidden message').to.eq(
+ `${user.name}, you do not have permissions to delete this resource`
+ );
+ });
+
+ it('should not have permission to read the route endpoint', async function () {
+ const resp = await getNegative(
+ `${url}/routes/${routeEntity.id}`,
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ expect(resp.data.message, 'Should have correct forbidden message').to.eq(
+ `${user.name}, you do not have permissions to read this resource`
+ );
+ });
+
+ it('should not have permission to update the service endpoint', async function () {
+ const resp = await postNegative(
+ `${url}/services/${serviceEntity.id}`,
+ { name: 'newServiceName' },
+ 'patch',
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ expect(resp.data.message, 'Should have correct forbidden message').to.eq(
+ `${user.name}, you do not have permissions to update this resource`
+ );
+ });
+
+ it('should have permission to read consumers endpoint', async function () {
+ const resp = await axios({
+ url: `${url}${consumersEndpoint}`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should have no permission to read endpints other than specified', async function () {
+ const resp = await getNegative(`${url}/developers`, userHeader);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ });
+
+ // now patching role permissions to check if those take effect after update
+ it('should not see deleted consumers endpoint permission in the list', async function () {
+ // removing services endpoint permissions entirely
+ let resp = await deleteRoleEndpointPermission(role.id, servicesEndpoint);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ // update consumers endpoint to read only
+ resp = await axios({
+ method: 'patch',
+ url: `${url}/rbac/roles/${role.id}/endpoints/${workspaceName}${consumersEndpoint}`,
+ data: {
+ actions: 'read',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ // allow reading route endpoint
+ resp = await axios({
+ method: 'patch',
+ url: `${url}/rbac/roles/${role.id}/endpoints/${workspaceName}${routesEndpoint}`,
+ data: {
+ negative: false,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ // adding read permission to plugin entity
+ resp = await axios({
+ method: 'patch',
+ url: `${url}/rbac/roles/${role.id}/entities/${pluginId}`,
+ data: {
+ actions: 'read,delete',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ // updating role name
+ resp = await axios({
+ method: 'put',
+ url: `${url}/rbac/roles/${role.id}`,
+ data: {
+ name: updateRoleName,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should see all updates in role permissions', async function () {
+ const req = () => axios(`${url}/rbac/roles/${role.id}/permissions`);
+
+ const assertions = (resp) => {
+ logResponse(resp);
+
+ expect(
+ resp.data.entities[`${pluginId}`].actions,
+ 'Should see correct actions for plugin entity'
+ )
+ .to.be.ofSize(2)
+ .and.have.members(['delete', 'read']);
+ expect(
+ resp.data.endpoints.default[`/default${servicesEndpoint}`],
+ 'Should not have services endpoint'
+ ).to.not.exist;
+
+ expect(
+ resp.data.endpoints.default[`/default${routesEndpoint}`].actions,
+ 'Should see negative false for routes endpoint'
+ ).to.deep.include({ read: { negative: false } });
+ };
+
+ await retryRequest(req, assertions, 10000);
+ });
+
+ it('should not delete a service after removing the permission entirely', async function () {
+ const resp = await postNegative(
+ `${url}/services/${serviceEntity.id}`,
+ {},
+ 'delete',
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ });
+
+ it('should have read permission on consumers endpoint', async function () {
+ const resp = await axios({
+ url: `${url}${consumersEndpoint}`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should not have update permission on consumers endpoint', async function () {
+ const resp = await postNegative(
+ `${url}${consumersEndpoint}`,
+ { username: 'newName' },
+ 'patch',
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ });
+
+ it('should have permission to read the routes endpoint', async function () {
+ const resp = await axios({
+ url: `${url}/routes/${routeEntity.id}`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should update user and have no impact on services endpoint permission', async function () {
+ const userNewToken = randomString();
+
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/rbac/users/${user.id}`,
+ data: {
+ name: randomString(),
+ user_token: userNewToken,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.user_token,
+ 'Token should be encoded in the response'
+ ).to.not.equal(userNewToken);
+
+ user.name = resp.data.name;
+ user.token = userNewToken;
+ userHeader['kong-admin-token'] = user.token;
+
+ resp = await postNegative(
+ `${url}/services/${serviceEntity.id}`,
+ {},
+ 'delete',
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ await wait(isHybrid ? 4000 : 100); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should have permission to read the routes endpoint after updating the user token', async function () {
+ const resp = await axios({
+ url: `${url}/routes/${routeEntity.id}`,
+ headers: userHeader,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should not have permission to read the routes endpoint after deleting the role', async function () {
+ await deleteRole(role.id);
+
+ const req = () =>
+ getNegative(
+ `${url}/routes/${routeEntity.id}`,
+ userHeader
+ );
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not have update permission on consumers endpoint after deleting the role', async function () {
+ const resp = await postNegative(
+ `${url}${consumersEndpoint}`,
+ { username: 'newName' },
+ 'patch',
+ userHeader
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ });
+
+ after(async function () {
+ await deleteUser(user.id);
+ await deletePlugin(pluginId);
+ await deleteGatewayRoute(routeEntity.id);
+ await deleteGatewayService(serviceEntity.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/endpoint-permissions.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/endpoint-permissions.spec.ts
new file mode 100644
index 00000000..f2ab3fec
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/endpoint-permissions.spec.ts
@@ -0,0 +1,320 @@
+import axios from 'axios';
+import {
+ expect,
+ getNegative,
+ postNegative,
+ getBasePath,
+ Environment,
+ createRole,
+ deleteRole,
+ randomString,
+ logResponse,
+ waitForConfigRebuild,
+ retryRequest,
+ eventually,
+ isGateway,
+} from '@support';
+
+describe('@smoke @gke: Gateway RBAC: Role Endpoint Permissions', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/rbac/roles`;
+
+ const roleName = randomString();
+ const workspaceName = 'default';
+ const endpoint1 = '/services';
+ const endpoint2 = '/developers/*/applications/*';
+ const customEndpoint = '/nonexisting/*/something/*';
+ let role: any;
+
+ before(async function () {
+ const resp = await createRole(roleName);
+ role = {
+ name: resp.name,
+ id: resp.id,
+ };
+ await waitForConfigRebuild();
+ });
+
+ it('should create a role endpoint permission', async function () {
+ const req = () =>
+ axios({
+ method: 'post',
+ url: `${url}/${role.id}/endpoints`,
+ data: {
+ workspace: workspaceName,
+ endpoint: endpoint1,
+ negative: false,
+ actions: 'read,create',
+ },
+ });
+
+ const assertions = (resp) => {
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.role, 'Response should have role id').to.haveOwnProperty(
+ 'id',
+ role.id
+ );
+ expect(resp.data.workspace, 'Should see correct workspace name').to.eq(
+ workspaceName
+ );
+ expect(resp.data.created_at, 'Should have created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.endpoint, 'Should have correct endpoint').to.eq(
+ endpoint1
+ );
+ expect(resp.data.negative, 'Should have negative false').to.be.false;
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'create',
+ 'read',
+ ]);
+ };
+
+ await waitForConfigRebuild();
+ await retryRequest(req, assertions);
+ });
+
+ it('should not create an endpoint permission twice', async function () {
+ const req = () =>
+ postNegative(
+ `${url}/${role.id}/endpoints`,
+ {
+ endpoint: endpoint1,
+ actions: 'read,create,update',
+ },
+ 'post'
+ );
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should have correct error name').to.eq(
+ 'primary key violation'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not create an endpoint permission without endpoint', async function () {
+ const resp = await postNegative(
+ `${url}/${role.id}/endpoints`,
+ {
+ actions: 'read,create',
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ "'endpoint' is a required field"
+ );
+ });
+
+ it('should retrieve a role endpoint permission', async function () {
+ const resp = await axios(
+ `${url}/${role.id}/endpoints/${workspaceName}${endpoint1}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.role, 'Response should have role id').to.haveOwnProperty(
+ 'id',
+ role.id
+ );
+ expect(resp.data.created_at, 'Should have created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.workspace, 'Should see correct workspace name').to.eq(
+ workspaceName
+ );
+ expect(resp.data.endpoint, 'Should have correct endpoint').to.eq(endpoint1);
+ expect(resp.data.negative, 'Should have negative false').to.be.false;
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'create',
+ 'read',
+ ]);
+ });
+
+ it('should not retrieve non-existing endpoint permission', async function () {
+ const resp = await getNegative(
+ `${url}/${role.id}/endpoints/${workspaceName}/plugins`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should update a role endpoint permission', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${role.id}/endpoints/${workspaceName}${endpoint1}`,
+ data: {
+ negative: true,
+ actions: 'update,delete',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.role, 'Response should have role id').to.haveOwnProperty(
+ 'id',
+ role.id
+ );
+ expect(resp.data.created_at, 'Should have created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.workspace, 'Should see correct workspace name').to.eq(
+ workspaceName
+ );
+ expect(resp.data.endpoint, 'Should have correct endpoint').to.eq(endpoint1);
+ expect(resp.data.negative, 'Should have negative true').to.be.true;
+ expect(resp.data.actions, 'Should have updated actions').to.have.members([
+ 'delete',
+ 'update',
+ ]);
+ });
+
+ it('should not update a role endpoint permission with wrong payload', async function () {
+ const resp = await postNegative(
+ `${url}/${role.id}/endpoints/${workspaceName}${endpoint1}`,
+ {
+ endpoint: '/acls',
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should create a role endpoint permission with wildcard endpoint and actions', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/${role.name}/endpoints`,
+ data: {
+ workspace: workspaceName,
+ endpoint: endpoint2,
+ negative: true,
+ actions: '*',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.endpoint, 'Should have correct endpoint').to.eq(endpoint2);
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'delete',
+ 'update',
+ 'create',
+ 'read',
+ ]);
+ });
+
+ it('should list all role endpoint permissions', async function () {
+ const resp = await axios(`${url}/${role.id}/endpoints`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have 1 role permission listed').to.be.ofSize(
+ 2
+ );
+
+ expect(
+ resp.data.data.map((permission) => permission.endpoint),
+ 'Should see all endpoint permissions in the list'
+ ).to.have.members([endpoint1, endpoint2]);
+ });
+
+ it('should delete role endpoint permission by role id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${role.id}/endpoints/${workspaceName}${endpoint1}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not retrieve a role endpoint permission after deletion', async function () {
+ await eventually(async () => {
+ const resp = await getNegative(
+ `${url}/${role.id}/endpoints/${workspaceName}${endpoint1}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+ });
+
+ it('should not list deleted role endpoint permission', async function () {
+ const resp = await axios(`${url}/${role.id}/endpoints`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have 1 role permission listed').to.be.ofSize(
+ 1
+ );
+
+ expect(
+ resp.data.data[0].endpoint,
+ 'Should see only the existing endpoint permission'
+ ).to.equal(endpoint2);
+ });
+
+ it('should delete role endpoint permission by role name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${role.name}/endpoints/${workspaceName}${endpoint2}`,
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should create a permission with custom endpoint', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/${role.id}/endpoints`,
+ data: {
+ endpoint: customEndpoint,
+ actions: 'create',
+ comment: 'custom',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.endpoint, 'Should have correct endpoint').to.eq(
+ customEndpoint
+ );
+ expect(resp.data.negative, 'Should have negative false').to.be.false;
+ expect(resp.data.comment, 'Should have custom comment').to.eq('custom');
+ expect(resp.data.actions, 'Should have correct actions').to.eql(['create']);
+ });
+
+ it('should delete role custom endpoint permission', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${role.name}/endpoints/${workspaceName}${customEndpoint}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not retrieve a role custom endpoint permission after deletion', async function () {
+ const resp = await getNegative(
+ `${url}/${role.id}/endpoints/${workspaceName}${customEndpoint}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ after(async function () {
+ await deleteRole(role.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/entity-permissions.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/entity-permissions.spec.ts
new file mode 100644
index 00000000..95b1f18d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/entity-permissions.spec.ts
@@ -0,0 +1,315 @@
+import {
+ createGatewayService,
+ createRole,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ deleteRole,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ postNegative,
+ randomString,
+ logResponse,
+ isGateway,
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway RBAC: Role Entity Permissions', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/rbac/roles`;
+
+ const roleName = randomString();
+ const serviceName = randomString();
+ let role: { name: string; id: string };
+ let serviceEntity: { id: string; type: string };
+ let routeEntity: { id: string; type: string };
+
+ before(async function () {
+ const roleReq = await createRole(roleName);
+ role = {
+ name: roleReq.name,
+ id: roleReq.id,
+ };
+
+ const serviceReq = await createGatewayService(serviceName);
+ serviceEntity = {
+ id: serviceReq.id,
+ type: 'services',
+ };
+
+ const routeReq = await createRouteForService(serviceEntity.id);
+ routeEntity = {
+ id: routeReq.id,
+ type: 'routes',
+ };
+ });
+
+ it('should create role service entity permission', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/${role.id}/entities`,
+ data: {
+ entity_id: serviceEntity.id,
+ entity_type: serviceEntity.type,
+ negative: false,
+ actions: 'read,create',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.role, 'Response should have role id').to.haveOwnProperty(
+ 'id',
+ role.id
+ );
+ expect(resp.data.comment, 'Should not see comment').to.not.exist;
+ expect(resp.data.entity_type, 'Should see correct entity_type').to.eq(
+ serviceEntity.type
+ );
+ expect(resp.data.entity_id, 'Should see correct entity_id').to.eq(
+ serviceEntity.id
+ );
+ expect(resp.data.negative, 'Should have negative false').to.be.false;
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'create',
+ 'read',
+ ]);
+ });
+
+ it('should not create role service entity permission twice', async function () {
+ const resp = await postNegative(
+ `${url}/${role.id}/entities`,
+ {
+ entity_id: serviceEntity.id,
+ entity_type: serviceEntity.type,
+ negative: true,
+ actions: 'delete',
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Error should mention repeated add').to.equal(
+ 'primary key violation'
+ );
+ });
+
+ it('should not create role entity permission without entity_id', async function () {
+ const resp = await postNegative(
+ `${url}/${role.id}/entities`,
+ {
+ entity_id: serviceEntity.id,
+ negative: false,
+ actions: 'read,create',
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ "Missing required parameter: 'entity_type'"
+ );
+ });
+
+ it('should not create role entity permission without entity_type', async function () {
+ const resp = await postNegative(
+ `${url}/${role.id}/entities`,
+ {
+ entity_type: serviceEntity.type,
+ negative: false,
+ actions: 'read,create',
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ "Missing required parameter: 'entity_id'"
+ );
+ });
+
+ it('should retrieve role entity permission', async function () {
+ const resp = await axios(`${url}/${role.id}/entities/${serviceEntity.id}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.entity_type, 'Should see correct entity_type').to.eq(
+ serviceEntity.type
+ );
+ expect(resp.data.entity_id, 'Should see correct entity_id').to.eq(
+ serviceEntity.id
+ );
+ expect(resp.data.negative, 'Should have negative false').to.be.false;
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'create',
+ 'read',
+ ]);
+ });
+
+ it('should update role entity permission', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${role.id}/entities/${serviceEntity.id}`,
+ data: {
+ negative: true,
+ actions: '*',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.negative, 'Should have negative false').to.be.true;
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'create',
+ 'read',
+ 'update',
+ 'delete',
+ ]);
+ });
+
+ it('should not update role entity permission type and id', async function () {
+ const resp = await postNegative(
+ `${url}/${role.id}/entities/${serviceEntity.id}`,
+ {
+ entity_id: routeEntity.id,
+ entity_type: routeEntity.type,
+ negative: true,
+ actions: '*',
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should create role route entity permission with wildcard actions', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/${role.id}/entities`,
+ data: {
+ entity_id: routeEntity.id,
+ entity_type: routeEntity.type,
+ negative: true,
+ actions: '*',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.role, 'Response should have role id').to.haveOwnProperty(
+ 'id',
+ role.id
+ );
+ expect(resp.data.entity_type, 'Should see correct entity_type').to.eq(
+ routeEntity.type
+ );
+ expect(resp.data.entity_id, 'Should see correct entity_id').to.eq(
+ routeEntity.id
+ );
+ expect(resp.data.negative, 'Should have negative true').to.be.true;
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'create',
+ 'read',
+ 'update',
+ 'delete',
+ ]);
+ });
+
+ it('should list all role entity permissions', async function () {
+ const resp = await axios(`${url}/${role.name}/entities`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have 2 role permission listed').to.be.ofSize(
+ 2
+ );
+
+ expect(
+ resp.data.data.map((permission) => permission.entity_type),
+ 'Should see route and service permissions in the list'
+ ).to.have.members([serviceEntity.type, routeEntity.type]);
+ });
+
+ it('should delete role entity permission by role id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${role.id}/entities/${serviceEntity.id}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not retrieve role entity permission after deletion', async function () {
+ const resp = await getNegative(
+ `${url}/${role.id}/entities/${serviceEntity.id}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should not see deleted entity permission in the list', async function () {
+ const resp = await axios(`${url}/${role.name}/entities`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should have 1 role permission listed').to.be.ofSize(
+ 1
+ );
+
+ expect(
+ resp.data.data.map((permission) => permission.entity_id),
+ 'Should see only route permission in the list'
+ ).to.have.members([routeEntity.id]);
+ });
+
+ it('should delete role entity permission by role name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${role.name}/entities/${routeEntity.id}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should create role wildcard entity permission', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/${role.id}/entities`,
+ data: {
+ entity_id: '*',
+ entity_type: '*',
+ negative: false,
+ actions: 'read,create',
+ comment: 'my comment',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.entity_type, 'Should see correct entity_type').to.eq(
+ 'wildcard'
+ );
+ expect(resp.data.entity_id, 'Should see correct entity_id').to.eq('*');
+ expect(resp.data.comment, 'Should see comment').to.eq('my comment');
+ expect(resp.data.actions, 'Should have correct actions').to.have.members([
+ 'create',
+ 'read',
+ ]);
+ });
+
+ after(async function () {
+ await deleteRole(role.id);
+ await deleteGatewayRoute(routeEntity.id);
+ await deleteGatewayService(serviceEntity.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/roles.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/roles.spec.ts
new file mode 100644
index 00000000..be6b2e3f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/roles.spec.ts
@@ -0,0 +1,199 @@
+import axios from 'axios';
+import {
+ expect,
+ getNegative,
+ getBasePath,
+ Environment,
+ randomString,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('@gke: Gateway RBAC: Roles', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/rbac/roles`;
+
+ const role1Name = 'role1';
+ const role1UpdatedName = 'role1updated';
+ const role2Name = 'role2';
+ const role2UpdatedName = 'role1updated';
+ const customId = '43cc6549-f625-4bae-a150-5fc23e0665cb';
+ const customName = randomString();
+ let role1Id: string;
+ let role2Id: string;
+
+ it('should create a role', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: role1Name,
+ comment: 'My role',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.eq(201);
+ expect(resp.data.comment, 'Should have correct comment').to.eq('My role');
+ expect(resp.data.created_at, 'Should have created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.name, 'Should have correct name').to.eq(role1Name);
+ expect(resp.data.id, 'Should have id').to.be.a.string;
+ role1Id = resp.data.id;
+ });
+
+ it('should retreive a role by id', async function () {
+ const resp = await axios(`${url}/${role1Id}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ expect(resp.data.name, 'Should have correct name').to.eq(role1Name);
+ expect(resp.data.id, 'Should have correct id').to.eq(role1Id);
+ });
+
+ it('should create a role using put request', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${role2Name}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ expect(resp.data.comment, 'Should have null as comment').to.be.null;
+ expect(resp.data.created_at, 'Should have created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.name, 'Should have correct name').to.eq(role2Name);
+ expect(resp.data.id, 'Should have id').to.be.a.string;
+ role2Id = resp.data.id;
+ });
+
+ it('should retreive a role by name', async function () {
+ const resp = await axios(`${url}/${role2Name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ expect(resp.data.name, 'Should have correct name').to.eq(role2Name);
+ expect(resp.data.id, 'Should have correct id').to.eq(role2Id);
+ });
+
+ it('should update a role name and comment', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${role1Name}`,
+ data: {
+ name: role1UpdatedName,
+ comment: 'comment from patch',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ expect(resp.data.comment, 'Should have null as comment').to.eq(
+ 'comment from patch'
+ );
+ expect(resp.data.name, 'Should have correct name').to.eq(role1UpdatedName);
+ expect(resp.data.id, 'Should have id').to.eq(role1Id);
+ });
+
+ it('should see all roles', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ // should see total 5 roles, 3 deafult and 2 created
+ expect(resp.data.data, 'Should have 5 roles listed').to.be.ofSize(5);
+
+ expect(
+ resp.data.data.map((role) => role.name),
+ 'Should see role1 name in the list'
+ ).to.include(role1UpdatedName);
+ expect(
+ resp.data.data.map((role) => role.name),
+ 'Should see role2 name in the list'
+ ).to.include(role2Name);
+ });
+
+ it('should delete a role by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${role1Id}`,
+ });
+
+ expect(resp.status, 'Status should be 204').to.eq(204);
+ });
+
+ it('should not retreive a deleted role', async function () {
+ const resp = await getNegative(`${url}/${role1Id}`);
+
+ expect(resp.status, 'Status should be 404').to.eq(404);
+ });
+
+ it('should update a role using put request', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${role2Id}`,
+ data: {
+ name: role2UpdatedName,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ expect(resp.data.name, 'Should have correct name').to.eq(role2UpdatedName);
+ expect(resp.data.id, 'Should have id').to.eq(role2Id);
+ });
+
+ it('should not see deleted role in roles list', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ // should see total 4 roles, 3 deafult and 1 created
+ expect(resp.data.data, 'Should have 4 roles listed').to.be.ofSize(4);
+
+ expect(
+ resp.data.data.map((role) => role.name),
+ 'Should see updated role2 name in the list'
+ ).to.include(role2UpdatedName);
+ expect(
+ resp.data.data.map((role) => role.id),
+ 'Should see role2 id in the list'
+ ).to.include(role2Id);
+ });
+
+ it('should delete a role by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${role1UpdatedName}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.eq(204);
+ });
+
+ it(`should create a role using put request if given primary key doesn't exist`, async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${customId}`,
+ data: {
+ name: customName,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ expect(resp.data.id, 'Should have correct id').to.eq(customId);
+ expect(resp.data.name, 'Should have correct name').to.eq(customName);
+
+ const delResp = await axios({
+ method: 'delete',
+ url: `${url}/${customId}`,
+ });
+ logResponse(delResp);
+
+ expect(delResp.status, 'Status should be 204').to.eq(204);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/user-roles.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/user-roles.spec.ts
new file mode 100644
index 00000000..feb13a72
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/user-roles.spec.ts
@@ -0,0 +1,234 @@
+import axios from 'axios';
+import {
+ expect,
+ postNegative,
+ createRole,
+ deleteRole,
+ getBasePath,
+ Environment,
+ randomString,
+ createUser,
+ deleteUser,
+ createRoleEndpointPermission,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe(`@gke: Gateway RBAC: User's Roles`, function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/rbac`;
+
+ const userName = randomString();
+ const userToken = randomString();
+ const roleName1 = randomString();
+ const roleName2 = randomString();
+ let role1: any;
+ let role2: any;
+ let user: any;
+
+ before(async function () {
+ // creating 2 test roles
+ const roleResp = await createRole(roleName1);
+ role1 = {
+ name: roleResp.name,
+ id: roleResp.id,
+ };
+
+ const roleResp2 = await createRole(roleName2);
+
+ role2 = {
+ name: roleResp2.name,
+ id: roleResp2.id,
+ };
+
+ // creating endpoint permissions for 1st role
+ await createRoleEndpointPermission(role1.id, '/services/*');
+ await createRoleEndpointPermission(role1.id, '/rbac', 'read', true);
+
+ // creating endpoint permissions for 2nd role
+ await createRoleEndpointPermission(role2.id, '/plugins', 'delete');
+
+ // create a test user
+ const userResp = await createUser(userName, userToken);
+
+ user = {
+ name: userResp.name,
+ id: userResp.id,
+ token: userResp.user_token,
+ };
+ });
+
+ it('should list role permissions by role id', async function () {
+ const resp = await axios(`${url}/roles/${role1.id}/permissions/`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.entities, 'Should not have entity permissions').to.be
+ .empty;
+
+ ['create', 'update', 'delete', 'read'].forEach((action) => {
+ expect(
+ resp.data.endpoints.default['/default/services/*'].actions,
+ `Should have correct service permission action for ${action}`
+ ).to.deep.include({ [action]: { negative: false } });
+ });
+
+ expect(
+ resp.data.endpoints.default['/default/rbac'].actions,
+ `Should have correct rbac permission actions`
+ ).to.deep.include({ read: { negative: true } });
+ });
+
+ it('should list role permissions by role name', async function () {
+ const resp = await axios(`${url}/roles/${role2.name}/permissions/`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.entities, 'Should not have entity permissions').to.be
+ .empty;
+
+ expect(
+ resp.data.endpoints.default['/default/plugins'].actions,
+ `Should have correct rbac permission action metadata`
+ ).to.deep.include({ delete: { negative: false } });
+ });
+
+ it('should add a role to a user by user id', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/users/${user.id}/roles`,
+ data: {
+ roles: role1.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.user.name, 'Should see correct user name').to.eq(
+ user.name
+ );
+ expect(resp.data.user.id, 'Should see correct user id').to.eq(user.id);
+ expect(resp.data.roles, 'Should see 1 role').to.have.lengthOf(1);
+ expect(resp.data.roles[0].name, 'Should see correct role name').to.eq(
+ role1.name
+ );
+ expect(resp.data.roles[0].id, 'Should see correct role id').to.eq(role1.id);
+ });
+
+ it('should add a role to a user by user name', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/users/${user.name}/roles`,
+ data: {
+ roles: role2.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.user.name, 'Should see correct user name').to.eq(
+ user.name
+ );
+ expect(resp.data.roles, 'Should see 2 roles').to.have.lengthOf(2);
+ expect(resp.data.roles.map((role) => role.name)).to.include(role2.name);
+ });
+
+ it('should not add a role to a user by role id', async function () {
+ const resp = await postNegative(
+ `${url}/users/${user.name}/roles`,
+ {
+ roles: role2.id,
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should see correct error message').to.eq(
+ `role not found with name '${role2.id}'`
+ );
+ });
+
+ it('should list user roles', async function () {
+ const resp = await axios(`${url}/users/${user.name}/roles`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.user.name, 'Should see correct user name').to.eq(
+ user.name
+ );
+ expect(resp.data.user.id, 'Should see correct user id').to.eq(user.id);
+ expect(resp.data.roles, 'Should see 2 roles').to.have.lengthOf(2);
+
+ expect(resp.data.roles.map((role) => role.name)).to.have.members([
+ role1.name,
+ role2.name,
+ ]);
+ });
+
+ it('should list user permissions', async function () {
+ const resp = await axios(`${url}/users/${user.name}/permissions`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.endpoints.default).to.include.all.keys(
+ '/default/rbac',
+ '/default/plugins',
+ '/default/services/*'
+ );
+ });
+
+ it('should delete a role from a user', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/users/${user.name}/roles`,
+ data: {
+ roles: role1.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should list user permissions after deleting a role', async function () {
+ const resp = await axios(`${url}/users/${user.name}/permissions`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.endpoints.default['/default/plugins'].actions,
+ `Should have correct rbac permission action metadata`
+ ).to.deep.include({ delete: { negative: false } });
+
+ expect(resp.data.endpoints.default).to.not.have.all.keys(
+ '/default/rbac',
+ '/default/services/*'
+ );
+
+ // deleting role1 entirely
+ await deleteRole(role1.id);
+ });
+
+ it('should not add a deleted role to a user', async function () {
+ const resp = await postNegative(
+ `${url}/users/${user.id}/roles`,
+ {
+ roles: role1.name,
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should see correct error message').to.eq(
+ `role not found with name '${role1.name}'`
+ );
+ });
+
+ after(async function () {
+ await deleteRole(role2.id);
+ await deleteUser(user.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/users.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/users.spec.ts
new file mode 100644
index 00000000..98da3d7f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/rbac/users.spec.ts
@@ -0,0 +1,246 @@
+import axios from 'axios';
+import {
+ expect,
+ getNegative,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ isGateway,
+ retryRequest,
+ waitForConfigRebuild,
+} from '@support';
+
+describe('@smoke @gke: Gateway RBAC: Users', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/rbac/users`;
+
+ const user1Name = 'user1';
+ const user1UpdatedName = 'user1updated';
+ const user1Token = 'rbac1';
+ const user2Name = 'user2';
+ const user2Token = 'rbac2';
+ let user1Id: string;
+ let user2Id: string;
+
+ it('should create a user', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: user1Name,
+ user_token: user1Token,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.comment, 'Comment should be null').to.be.null;
+ expect(resp.data.user_token, 'Should have user_token').to.be.string;
+ expect(
+ resp.data.user_token,
+ 'Should have encrypted user_token'
+ ).to.not.contain(user1Token);
+ expect(resp.data.user_token_ident, 'Should have user_token_ident').to.be.a
+ .string;
+ expect(resp.data.enabled, 'Should be enabled by default').to.be.true;
+ expect(resp.data.name, 'Should have correct user name').to.eq(user1Name);
+ expect(resp.data.created_at).to.be.a('number');
+
+ user1Id = resp.data.id;
+ });
+
+ it('should not create a user without token', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: 'someUsername',
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should have correct error name').to.equal(
+ 'schema violation'
+ );
+ expect(resp.data.message, 'Should have correct message').to.contain(
+ 'schema violation (user_token:'
+ );
+ expect(
+ resp.data.fields,
+ 'Should have correct violated fields'
+ ).to.haveOwnProperty('user_token', 'required field missing');
+ });
+
+ it('should not create a user without name', async function () {
+ const req = () => postNegative(
+ url,
+ {
+ user_token: 'mytoken',
+ },
+ 'post'
+ );
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should have correct error name').to.equal(
+ 'schema violation'
+ );
+ expect(resp.data.message, 'Should have correct message').to.contain(
+ 'schema violation (name:'
+ );
+ expect(
+ resp.data.fields,
+ 'Should have correct violated fields'
+ ).to.haveOwnProperty('name', 'required field missing');
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should geta a user by id', async function () {
+ const resp = await axios(`${url}/${user1Id}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.enabled, 'Should be enabled by default').to.be.true;
+ expect(resp.data.name, 'Should have correct user name').to.eq(user1Name);
+ expect(resp.data.id, 'Should have correct user id').to.equal(user1Id);
+ });
+
+ it('should not get a user by wrong id', async function () {
+ const resp = await getNegative(
+ `${url}/0cb764cd-0b2f-4956-b7f2-c3cb60c55907`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should update the user', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${user1Id}`,
+ data: {
+ name: user1UpdatedName,
+ user_token: 'updatedToken',
+ comment: 'A new comment',
+ enabled: false,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should update user name').to.eq(user1UpdatedName);
+ expect(resp.data.enabled, 'User should be disabled').to.be.false;
+ expect(resp.data.id, 'Should have correct user id').to.equal(user1Id);
+ expect(resp.data.comment, 'Should have updated comment').to.equal(
+ 'A new comment'
+ );
+ expect(resp.data.id, 'Should have updated user token').to.not.eq(
+ user1Token
+ );
+ });
+
+ it('should not update a wrong user with wrong name', async function () {
+ const resp = await postNegative(
+ `${url}/wrongName`,
+ {
+ user_token: 'newToken',
+ comment: 'A new comment for wrong user',
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should create a user with enabled false', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: user2Name,
+ user_token: user2Token,
+ enabled: false,
+ comment: 'comment',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.comment, 'Comment should be null').to.eq('comment');
+ expect(resp.data.enabled, 'Should not be enabled').to.be.false;
+ expect(resp.data.name, 'Should have correct user name').to.eq(user2Name);
+ expect(
+ resp.data.user_token,
+ 'Should have encrypted user_token'
+ ).to.not.contain(user2Token);
+
+ user2Id = resp.data.id;
+
+ await waitForConfigRebuild()
+ });
+
+ it('should get a user by name', async function () {
+ const resp = await axios(`${url}/${user2Name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.enabled, 'User should be disabled').to.be.false;
+ expect(resp.data.name, 'Should have correct user name').to.eq(user2Name);
+ expect(resp.data.id, 'Should have correct user id').to.equal(user2Id);
+ });
+
+ it('should not get a user by wrong name', async function () {
+ const resp = await getNegative(`${url}/wrongName`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should get all users', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should see total 2 users').to.have.lengthOf(2);
+
+ resp.data.data.forEach((user) => {
+ expect(user.name, 'Should see all user names').to.equal(
+ user.id === user1Id ? user1UpdatedName : user2Name
+ );
+ });
+ });
+
+ it('should delete a user by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${user1UpdatedName}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should see correct number of users after deleting one', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should see total 2 users').to.have.lengthOf(1);
+ });
+
+ it('should delete a user by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${user2Id}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/router.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/router.spec.ts
new file mode 100644
index 00000000..5eca62d1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/router.spec.ts
@@ -0,0 +1,416 @@
+/* eslint-disable no-useless-escape */
+import axios from 'axios';
+import https from 'https';
+import {
+ expect,
+ getNegative,
+ postNegative,
+ getBasePath,
+ Environment,
+ randomString,
+ createGatewayService,
+ deleteGatewayService,
+ logResponse,
+ isGwHybrid,
+ isLocalDatabase,
+ wait,
+ getGatewayHost,
+ getGatewayContainerLogs,
+ findRegex,
+ retryRequest,
+ getKongContainerName,
+ waitForConfigRebuild,
+ deleteGatewayRoute,
+ isGateway,
+ isKoko,
+} from '@support';
+
+const agent = new https.Agent({
+ rejectUnauthorized: false,
+});
+
+axios.defaults.httpsAgent = agent;
+
+const testWrongHeaders = [
+ { header: 'testHeader', value: 'wrong' },
+ { header: 'testH', value: 'test' },
+];
+
+const testCorrectHeaders = [
+ { header: 'testHeader', value: 'test' },
+ { header: 'testHeader', value: 'test2' },
+];
+
+const regexWrongPatterns = ['/5555-helo', '/heo-world', '/wrong-test'];
+const regexCorrectPatterns = ['/helo-test', '/hello-auto', '/world-te'];
+const currentHost = getGatewayHost();
+
+const isLocalDb = isLocalDatabase();
+
+describe('@smoke @koko @gke: Router Functionality Tests', function () {
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxySec,
+ })}`;
+
+ const isHybrid = isGwHybrid();
+ const serviceName = randomString();
+ const kongContainerName = getKongContainerName();
+ const regexPath = '~/(hell?o|world)-(?[a-zA-Z]+)';
+ const waitTime = 5000;
+ const hybridWaitTime = 6000;
+
+ let serviceDetails: any;
+ let routeId: string;
+ let adminUrl: any;
+ let routesUrl: any
+
+ const routePayload = {
+ name: randomString(),
+ paths: [`/${randomString()}`, '/plain/a.b%25c', '~/prefix/[0-9]+'],
+ methods: ['GET'],
+ };
+
+ before(async function () {
+ adminUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+
+ routesUrl = `${adminUrl}/routes`;
+
+ const serviceReq = await createGatewayService(serviceName);
+
+ serviceDetails = {
+ id: serviceReq.id,
+ name: serviceReq.name,
+ };
+ });
+
+ it('should create a route with header', async function () {
+ const payload = {
+ ...routePayload,
+ headers: {
+ testHeader: ['test', 'test2'],
+ },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url: `${adminUrl}/services/${serviceDetails.id}/routes`,
+ data: payload,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ routeId = resp.data.id;
+
+ expect(resp.data.name, 'Should have correct route name').equal(
+ payload.name
+ );
+
+ expect(
+ resp.data.headers,
+ 'Should have correct route header key'
+ ).to.have.property('testHeader');
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime) : waitTime
+ );
+ });
+
+ it('should not create a route with duplicate name', async function () {
+ const resp = await postNegative(
+ routesUrl,
+ routePayload,
+ 'post',
+ {},
+ { rejectUnauthorized: true }
+ );
+ logResponse(resp);
+
+ // *** RESPONSE DIFFERENCES IN GATEWAY AND KOKO ***
+ if (isGateway()) {
+ expect(resp.status, 'Status should be 409').equal(409);
+ expect(resp.data.name, 'Should have correct error name').equal(
+ 'unique constraint violation'
+ );
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `UNIQUE violation detected on '{name="${routePayload.name}"}'`
+ );
+ } else if (isKoko()) {
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error name').to.equal(
+ 'data constraint error'
+ );
+
+ // in Konnect we need to wait for the route creation to take effect
+ await waitForConfigRebuild()
+ }
+ });
+
+ testWrongHeaders.forEach(({ header, value }) => {
+ it(`should not route a request with wrong header: ${header}:${value}`, async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${routePayload.paths[0]}`,
+ {
+ [header]: value,
+ },
+ {},
+ { rejectUnauthorized: true }
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').equal(404);
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `no Route matched with those values`
+ );
+ });
+ });
+
+ it('should not route a request without the required header', async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${routePayload.paths[0]}`,
+ {},
+ {},
+ { rejectUnauthorized: true }
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').equal(404);
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `no Route matched with those values`
+ );
+ });
+
+ testCorrectHeaders.forEach(({ header, value }) => {
+ it(`should route a request with correct header: ${header}:${value}`, async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${routePayload.paths[0]}`,
+ headers: { [header]: value },
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').equal(200);
+ });
+ });
+
+ it('should NOT route a request when http method is not allowed', async function () {
+ const resp = await postNegative(
+ `${proxyUrl}${routePayload.paths[0]}`,
+ {},
+ 'post',
+ {
+ testHeader: 'test',
+ },
+ { rejectUnauthorized: true }
+ );
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').equal(404);
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `no Route matched with those values`
+ );
+ });
+
+ it('should route a request with route path /plain/a.b%25c', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${routePayload.paths[1]}`,
+ headers: { testHeader: 'test' },
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').equal(200);
+ });
+
+ it('should match route path /prefix/123 when regex is ~/prefix/[0-9]+', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}/prefix/123}`,
+ headers: { testHeader: 'test' },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').equal(200);
+ });
+
+ it('should NOT match route path /extra/prefix/123 when regex is ~/prefix/[0-9]+', async function () {
+ const resp = await getNegative(
+ `${proxyUrl}/extra/prefix/123}`,
+ {
+ testHeader: 'test',
+ },
+ {},
+ { rejectUnauthorized: true }
+ );
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').equal(404);
+ });
+
+ it('should PATCH the route headers and paths', async function () {
+ // *** KOKO DOES NOT PATCH REQUESTS AND HEADERS SHOULD BE NULL ***
+ const resp = await axios({
+ method: isGateway() ? 'patch' : 'put',
+ url: `${routesUrl}/${routeId}`,
+ data: {
+ service: {
+ id: serviceDetails.id
+ },
+ headers: isGateway() ? [] : null,
+ paths: [regexPath]
+ },
+ headers: { 'Content-Type': 'application/json' },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.paths[0], 'Should have correct path').to.equal(regexPath);
+
+ // *** KOKO DOES NOT CONTAIN HEADERS in RESPONSE ***
+ if(isGateway()) {
+ expect(resp.data.headers, 'Should have empty headers').to.be.empty
+ } else if (isKoko()) {
+ expect(resp.data.headers, 'Should have no headers').to.not.exist
+ }
+
+ await waitForConfigRebuild()
+ });
+
+ regexCorrectPatterns.forEach((correctPattern) => {
+ it(`should route a request with matching regex path: ${correctPattern}`, async function () {
+ const req = () => axios(`${proxyUrl}${correctPattern}`);
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').equal(200);
+ };
+
+ await retryRequest(req, assertions);
+ });
+ });
+
+ regexWrongPatterns.forEach((wrongPattern) => {
+ it(`should not route a request with non-matching regex path: ${wrongPattern}`, async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${wrongPattern}`,
+ {},
+ {},
+ { rejectUnauthorized: true }
+ );
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').equal(404);
+ });
+ });
+
+ it('should PATCH the route host', async function () {
+ // *** KOKO DOES NOT PATCH REQUESTS ***
+ const resp = await axios({
+ method: isGateway() ? 'patch' : 'put',
+ url: `${routesUrl}/${routeId}`,
+ data: {
+ service: {
+ id: serviceDetails.id
+ },
+ hosts: ['test'],
+ paths: [routePayload.paths[0]],
+ },
+ headers: { 'Content-Type': 'application/json' },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.hosts[0], 'Should have correct host').to.equal('test');
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should not route a request with wrong host', async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${routePayload.paths[0]}`,
+ {},
+ {},
+ { rejectUnauthorized: true }
+ );
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').equal(404);
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `no Route matched with those values`
+ );
+ });
+
+ it('should route the request with correct host', async function () {
+ const resp = await axios({
+ method: isGateway() ? 'patch' : 'put',
+ url: `${routesUrl}/${routeId}`,
+ data: {
+ service: {
+ id: serviceDetails.id
+ },
+ paths: [routePayload.paths[0]],
+ hosts: [currentHost]
+ },
+ headers: { 'Content-Type': 'application/json' },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.hosts[0], 'Should have correct host').to.equal(
+ currentHost
+ );
+
+ await waitForConfigRebuild();
+
+ const req = () => axios(`${proxyUrl}${routePayload.paths[0]}`);
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').equal(200);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ if(isGateway()) {
+ it('should delete the route by name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${routesUrl}/${routePayload.name}`,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not panic and create a route with long regex path', async function () {
+ // Skip this test for GKE deployment
+ if (process.env.GKE) {
+ this.skip();
+ }
+ // generate string longer than 2048 bytes
+ // *** KOKO MAX BYTES IS 1024 AND IT DOESN'T RECOGNIZE "\\/" ESCAPE SEQUENCE ***
+ const path = 'x'.repeat(3072);
+
+ const resp = await postNegative(
+ `${adminUrl}/services/${serviceDetails.id}/routes`,
+ {
+ name: randomString(),
+ paths: [`~/${path}/[^\\/]{14}()$`],
+ },
+ 'post',
+ {},
+ { rejectUnauthorized: true }
+ );
+ logResponse(resp);
+
+ routeId = resp.data.id;
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ await wait(4000); // eslint-disable-line no-restricted-syntax
+ const currentLogs = getGatewayContainerLogs(kongContainerName, 15);
+ const panickLog = findRegex('panicked', currentLogs);
+ const outOfRangeLog = findRegex('out of range', currentLogs);
+
+ expect(panickLog, 'Should not see router panic error log').to.be.false;
+ expect(outOfRangeLog, 'Should not see out of range error log').to.be.false;
+ });
+ }
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(isGateway() ? serviceName : serviceDetails.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/routes.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/routes.spec.ts
new file mode 100644
index 00000000..bad730e9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/routes.spec.ts
@@ -0,0 +1,244 @@
+import {
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ logResponse,
+ postNegative,
+ isKoko,
+ isGateway,
+ randomString,
+ deleteGatewayService,
+ wait,
+ eventually
+} from '@support';
+import axios, { AxiosResponse } from 'axios';
+
+describe('@smoke @gke: Gateway Admin API: Routes', function () {
+ let serviceId: string
+ let routeId: string;
+
+ const path = `/${randomString()}`;
+ const newPath = `/${randomString()}`;
+ const routeName = "APITestRoute"
+ const basePath = getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ });
+ const url = `${basePath}/routes`;
+ const proxyUrl = getBasePath({
+ environment: isGateway() ? Environment.gateway.proxy : undefined,
+ });
+ const proxyUrlSec = getBasePath({
+ environment: isGateway() ? Environment.gateway.proxySec : undefined,
+ });
+
+ const servicePayload = {
+ name: 'APITestService',
+ url: 'http://httpbin/anything',
+ };
+
+ const routePayload = {
+ methods: ['GET'],
+ paths: [path],
+ protocols: ['http'],
+ strip_path: true,
+ preserve_host: false,
+ };
+
+ const assertRespDetails = (response: AxiosResponse) => {
+ const resp = response.data;
+ expect(resp.paths, 'Should have correct paths').to.deep.equal(routePayload.paths);
+ expect(resp.methods, 'Should have correct methods').to.deep.equal(routePayload.methods);
+ expect(resp.protocols, 'Should have correct protocols').to.deep.equal(routePayload.protocols);
+ expect(resp.strip_path, 'Should have correct strip_path value').equal(routePayload.strip_path);
+ expect(resp.preserve_host, 'Should have correct preserve_host value').equal(routePayload.preserve_host);
+ expect(resp.https_redirect_status_code, 'Should include default redirect status code').equal(426);
+ // *** HANDLES NULL OR UNDEFINED **
+ expect(resp.tags == null, 'Should not have tags').to.be.true;
+ expect(resp.id, 'Should have id of type string').to.be.a('string');
+ expect(resp.created_at, 'created_at should be a number').to.be.a('number');
+ expect(resp.updated_at, 'updated_at should be a number').to.be.a('number');
+ };
+
+ before(async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${basePath}/services`,
+ data: servicePayload,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ servicePayload.name
+ );
+ expect(resp.headers, 'Should include request id in header').to.have.property('x-kong-admin-request-id');
+ expect(resp.headers['x-kong-admin-request-id'], 'request id should be a string').to.be.a('string')
+ serviceId = resp.data.id
+ });
+
+ it('should create a global route', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ ...routePayload,
+ name: routeName,
+ },
+ });
+ logResponse(resp);
+ routeId = resp.data.id;
+
+ expect(resp.status, 'Status should be 201').equal(201);
+ assertRespDetails(resp);
+ });
+
+ it('should patch route to be scoped to service', async function () {
+ const resp = await axios({
+ method: 'PATCH',
+ url: `${url}/${routeId}`,
+ data: {
+ ...routePayload,
+ service: { id: serviceId },
+ },
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.service.id, 'Should be scoped to service').equal(serviceId);
+ });
+
+ it('should not create a route with empty path', async function () {
+ const wrongPayload = {
+ name: randomString(),
+ paths: [],
+ };
+ const resp = await postNegative(url, wrongPayload);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 400').equal(400);
+ // *** RESPONSE DIFFERENCES IN GATEWAY AND KOKO ***
+ if (isGateway()) {
+ expect(resp.data.name, 'Should have correct error name').equal(
+ 'schema violation'
+ );
+ expect(resp.data.message, 'Should have correct error name').contain(
+ `schema violation (must set one of 'methods', 'hosts'`
+ );
+ } else if (isKoko()) {
+ expect(resp.data.message, 'Should have correct error name').to.equal(
+ 'validation error'
+ );
+ }
+ });
+
+ it('should get the route by name', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${url}/${routeName}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ assertRespDetails(resp);
+ });
+
+ it('should get the route by id', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${url}/${routeId}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ assertRespDetails(resp);
+ });
+
+ // *** KOKO DOES NOT PATCH ROUTES BY NAME ***
+ if (isGateway()) {
+ it('should patch the route with new tags', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${routeName}`,
+ data: {
+ tags: ['patchedRoutebyName']
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Should have new tag').contain('patchedRoutebyName');
+ });
+ }
+
+ it('should be able to update route by id with new path', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${routeId}`,
+ data: {
+ paths: [newPath],
+ },
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.paths, 'Should have updated path').contain(newPath);
+ await wait(5000); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should not get the route by wrong name', async function () {
+ const resp = await getNegative(`${url}/wrong`);
+ logResponse(resp);
+
+ expect(resp.status, 'Should have correct error code').to.equal(404);
+ const errMsg = (resp.data.message || resp.statusText).toLowerCase();
+ expect(errMsg, 'Should have correct error message').to.equal('not found');
+ });
+
+ it('should not get the route by wrong id', async function () {
+ const resp = await getNegative(
+ `${url}/650d4122-3928-45a1-909d-73921163bb13`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Should respond with error').to.equal(404);
+ const errMsg = (resp.data.message || resp.statusText).toLowerCase();
+ expect(errMsg, 'Should have correct error message').to.equal('not found');
+ });
+
+ it('should be able to send a request to the route', async function () {
+ await eventually(async () => {
+ const resp = await axios({
+ method: 'get',
+ url: `${proxyUrl}${newPath}`,
+ validateStatus: null,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.headers, 'Should include request id in header').to.have.property('x-kong-request-id');
+ expect(resp.headers['x-kong-request-id'], 'request id should be a string').to.be.a('string')
+ });
+ });
+
+ it('should be able to send a secure request to the route', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${proxyUrlSec}${newPath}`,
+ validateStatus: null,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.headers, 'Should have request_id').to.have.property('x-kong-request-id');
+ expect(resp.headers['x-kong-request-id'], 'request id should be a string').to.be.a('string')
+ });
+
+ // *** KOKO DOES NOT DELETE ROUTES BY NAME ***
+ it('should delete the route', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${isGateway() ? routeName : routeId}`
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ // delete service
+ await deleteGatewayService(serviceId);
+ });
+});
+
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/service.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/service.spec.ts
new file mode 100644
index 00000000..54c44903
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/service.spec.ts
@@ -0,0 +1,268 @@
+import {
+ deleteGatewayRoute,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ logResponse,
+ postNegative,
+ randomString,
+ getKongVersionFromContainer,
+ getKongContainerName,
+ getKongVersion,
+ isKoko,
+ isGateway
+} from '@support';
+import axios, { AxiosResponse } from 'axios';
+
+describe('@smoke @koko @gke: Gateway Admin API: Services', function () {
+ let url: string
+ let serviceId: string
+ let routeId: string;
+
+ before(function () {
+ const basePath = getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ });
+ url = `${basePath}/services`;
+ });
+
+ const servicePayload = {
+ name: 'APITestService',
+ url: 'http://httpbin/anything',
+ };
+
+ const newPath = '/anythingUpdated';
+ const kongContainerName = getKongContainerName();
+ const kongVersion = getKongVersion();
+
+ const assertRespDetails = (response: AxiosResponse) => {
+ const resp = response.data;
+ expect(resp.port, 'Should have port 80').equal(80);
+ expect(resp.protocol, 'Should have protocol "http"').equal('http');
+ expect(resp.host, 'Should have correct host').equal('httpbin');
+ expect(resp.path, 'Should have correct path').equal('/anything');
+ expect(resp.connect_timeout, 'Should have correct connect_timeout').equal(
+ 60000
+ );
+ expect(resp.read_timeout, 'Should have correct read_timeout').equal(60000);
+ expect(resp.write_timeout, 'Should have correct write_timeout').equal(
+ 60000
+ );
+ expect(resp.retries, 'Should have 5 retries').equal(5);
+ // *** HANDLES NULL OR UNDEFINED **
+ expect(resp.tags == null, 'Should not have tags').to.be.true;
+ expect(resp.id, 'Should have id of type string').to.be.a('string');
+ expect(resp.created_at, 'created_at should be a number').to.be.a('number');
+ expect(resp.updated_at, 'updated_at should be a number').to.be.a('number');
+ };
+
+ it('should create a service and a route for service', async function () {
+ let resp = await axios({
+ method: 'post',
+ url,
+ data: servicePayload
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ servicePayload.name
+ );
+ assertRespDetails(resp);
+ serviceId = resp.data.id
+
+ resp = await axios({
+ method: 'post',
+ url: `${url}/${serviceId}/routes`,
+ data: {
+ name: randomString(),
+ paths: [`/${randomString()}`],
+ },
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ routeId = resp.data.id;
+ });
+
+ it('should not create a service with same name', async function () {
+ const resp = await postNegative(url, servicePayload, 'post');
+ logResponse(resp);
+
+ // *** RESPONSE DIFFERENCES IN GATEWAY AND KOKO ***
+ if (isGateway()) {
+ expect(resp.status, 'Status should be 409').equal(409);
+ expect(resp.data.name, 'Should have correct error name').equal(
+ 'unique constraint violation'
+ );
+ expect(resp.data.message, 'Should have correct error name').equal(
+ `UNIQUE violation detected on '{name="${servicePayload.name}"}'`
+ );
+ } else if (isKoko()) {
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error name').to.equal(
+ 'data constraint error'
+ );
+ }
+ });
+
+ it('should not create a service with incorrect path', async function () {
+ const wrongPayload = {
+ name: 'my-service',
+ protocol: 'http',
+ port: 8000,
+ host: 'httpbin',
+ path: 'anything',
+ };
+ const resp = await postNegative(url, wrongPayload, 'post');
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ // *** RESPONSE DIFFERENCES IN GATEWAY AND KOKO ***
+ if (isGateway()) {
+ expect(resp.data.name, 'Should have correct error name').equal(
+ 'schema violation'
+ );
+ expect(resp.data.message, 'Should have correct error name').contain(
+ `schema violation (path: should start with: /`
+ );
+ } else if (isKoko()) {
+ expect(resp.data.message, 'Should have correct error name').to.equal(
+ 'validation error'
+ );
+ }
+ });
+
+ it('should get the service by name', async function () {
+ const resp = await axios(`${url}/${servicePayload.name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ servicePayload.name
+ );
+ assertRespDetails(resp);
+ });
+
+ it('should get the service by id', async function () {
+ const resp = await axios(`${url}/${serviceId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should have correct service name').equal(
+ servicePayload.name
+ );
+ assertRespDetails(resp);
+ });
+
+ // *** KOKO DOES NOT PATCH SERVICES BY NAME ***
+ if (isGateway()) {
+ it('should patch the service', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${servicePayload.name}`,
+ data: {
+ protocol: 'https',
+ port: 8080,
+ path: newPath,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.path, 'Should have correct path').equal(newPath);
+ expect(resp.data.port, 'Should have port 8080').equal(8080);
+ expect(resp.data.protocol, 'Should have protocol "https"').equal('https');
+ });
+ }
+
+ it('should not get the service by wrong name', async function () {
+ const resp = await getNegative(`${url}/wrong`);
+ logResponse(resp);
+
+ expect(resp.status, 'Should have correct error code').to.equal(404);
+ const errMsg = (resp.data.message || resp.statusText).toLowerCase();
+ expect(errMsg, 'Should have correct error message').to.equal('not found');
+ });
+
+ it('should not get the service by wrong id', async function () {
+ const resp = await getNegative(
+ `${url}/650d4122-3928-45a1-909d-73921163bb13`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Should respond with error').to.equal(404);
+ const errMsg = (resp.data.message || resp.statusText).toLowerCase();
+ expect(errMsg, 'Should have correct error message').to.equal('not found');
+ });
+
+ // *** KOKO ALLOWS DELETION -- RETURNS 204 ***
+ if (isGateway()) {
+ it('should not delete the service when it has associated route', async function () {
+ const resp = await postNegative(
+ `${url}/${serviceId}`,
+ {},
+ 'delete'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').equal(400);
+ expect(resp.data.message, 'Should have correct error message').include(
+ `an existing 'routes' entity references this 'services' entity`
+ );
+ });
+ }
+
+ it('should delete the service by id when it has no associated route', async function () {
+ await deleteGatewayRoute(routeId);
+
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${serviceId}`
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should create a service with url and path specified separately in request', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: servicePayload.name,
+ protocol: 'https',
+ port: 443,
+ host: 'mockbin.org',
+ path: '/kongstrongservice',
+ },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ serviceId = resp.data.id
+ expect(resp.data.name, 'Should have correct service name').equal(
+ servicePayload.name
+ );
+ expect(resp.data.host, 'Should have correct host').equal('mockbin.org');
+ expect(resp.data.path, 'Should have correct path').equal(
+ '/kongstrongservice'
+ );
+ });
+
+ // *** KOKO DOES NOT DELETE SERVICES BY NAME ***
+ it('should delete the service', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${isGateway() ? servicePayload.name : serviceId}`
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ // run this test only when KONG_PACKAGE env variable is specified
+ if (kongContainerName && kongContainerName !== 'kong-cp') {
+ it('should have correct kong docker image version', async function () {
+ const version = getKongVersionFromContainer(kongContainerName);
+ expect(version).to.eq(`Kong Enterprise ${kongVersion}`);
+ });
+ }
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/cert-upstreams.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/cert-upstreams.spec.ts
new file mode 100644
index 00000000..ab5c824a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/cert-upstreams.spec.ts
@@ -0,0 +1,654 @@
+import { authDetails } from '@fixtures';
+import {
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ postNegative,
+ logResponse,
+ isGateway,
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway Admin API: Cert-Associated Upstreams', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/certificates`;
+
+ const name = 'test-cert-upstream';
+ const putName = 'put-test-cert';
+ const patchName = 'patch-test-cert';
+ const negTestName = 'negative-test';
+
+ const uuid = 'b1acbb2f-c06e-4123-b2d7-f5d397eedd72';
+ const invalidName = 'ÅÍÎÏÓÔÒÚÆ';
+ const tag = 'certtag';
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const updateTag = 'certtag-2';
+ const hostHeader = 'example.com';
+ const invalidHeader = 'not a header';
+
+ let certificateData: any;
+ let upstreamData: any;
+
+ before(async function () {
+ // Create a mock certificate to associate with the upstream
+ const certificate = await axios({
+ method: 'post',
+ url: url,
+ data: {
+ cert: authDetails.cert.certificate,
+ key: authDetails.cert.key,
+ },
+ });
+ certificateData = {
+ cert: certificate.data.cert,
+ key: certificate.data.key,
+ id: certificate.data.id,
+ };
+ });
+
+ it('should create cert-associated upstream by name', async function () {
+ const certUpstream = await axios({
+ url: `${url}/${certificateData.id}/upstreams`,
+ method: 'post',
+ data: {
+ name: name,
+ },
+ });
+ logResponse(certUpstream);
+
+ upstreamData = {
+ name: certUpstream.data.name,
+ id: certUpstream.data.id,
+ };
+
+ expect(certUpstream.status, 'should return status 201').to.equal(201);
+ expect(certUpstream.data, 'should have id field').to.have.property('id');
+ expect(certUpstream.data.name, 'should have expected name').to.equal(name);
+ expect(
+ certUpstream.data,
+ 'should contain client_certificate field'
+ ).to.have.property('client_certificate');
+ expect(
+ certUpstream.data.client_certificate.id,
+ 'should match cert id to expect id'
+ ).to.equal(certificateData.id);
+ });
+
+ it('should get cert-associated upstream', async function () {
+ const resp = await axios(`${url}/${certificateData.id}/upstreams`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.data.length, 'should contain upstream in resp').to.equal(
+ 1
+ );
+ expect(resp.data.data[0].id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.data[0].name, 'should have expected name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should not create upstream with same name', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams`,
+ { name: upstreamData.name },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 409 status').to.equal(409);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ `UNIQUE violation detected on '{name="${upstreamData.name}"}'`
+ );
+ });
+
+ it('should delete cert-associated upstream by id', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should return 404 when attempting to delete upstream that does not exist', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams/not-an-upstream-at-all`,
+ {},
+ 'delete'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 404').to.equal(404);
+ });
+
+ it('should not create upstream with empty body', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams`,
+ {},
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 400').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ 'schema violation (name: required field missing)'
+ );
+ });
+
+ it('should not create upstream with invalid name', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams`,
+ { name: invalidName },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 400').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ `schema violation (name: Invalid name ('${invalidName}'); must be a valid hostname)`
+ );
+ });
+
+ // TODO: uncomment headers line and check when FT-2646 is resolved
+ it('should create upstream with valid healthcheck parameters', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/`,
+ data: {
+ name: upstreamData.name,
+ healthchecks: {
+ active: {
+ timeout: 2,
+ unhealthy: {
+ interval: 1,
+ tcp_failures: 5,
+ timeouts: 1,
+ http_failures: 5,
+ http_statuses: [500],
+ },
+ type: 'http',
+ concurrency: 11,
+ // headers: [{ 'X-Content-Type-Options': ['nosniff'] }],
+ healthy: {
+ interval: 1,
+ successes: 1,
+ http_statuses: [200, 204, 302, 201],
+ },
+ http_path: '/',
+ https_sni: 'example.com',
+ https_verify_certificate: true,
+ },
+ passive: {
+ type: 'http',
+ unhealthy: {
+ http_statuses: [500],
+ http_failures: 3,
+ timeouts: 1,
+ tcp_failures: 1,
+ },
+ healthy: {
+ http_statuses: [200, 201],
+ successes: 2,
+ },
+ },
+ threshold: 23,
+ },
+ },
+ method: 'post',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ expect(resp.data, 'should have id in response').to.have.property('id');
+ expect(
+ resp.data.healthchecks.active.healthy.successes,
+ 'should have correct healthcheck active success value'
+ ).to.equal(1);
+ expect(
+ resp.data.healthchecks.active.unhealthy.http_failures,
+ 'should have correct healthcheck http failure value'
+ ).to.equal(5);
+ expect(
+ resp.data.healthchecks.passive.healthy.successes,
+ 'should have correct healthcheck passive success value'
+ ).to.equal(2);
+ expect(
+ resp.data.healthchecks.passive.unhealthy.http_failures,
+ 'should have correct healthcheck passive http failure value'
+ ).to.equal(3);
+ expect(
+ resp.data.healthchecks.threshold,
+ 'should have correct healthcheck threshold value'
+ ).to.equal(23);
+ // expect(
+ // resp.data.healthchecks.active.headers,
+ // 'should have correct headers'
+ // ).to.equal(`[{ 'X-Content-Type-Options': ['nosniff'] }]`);
+
+ upstreamData.id = resp.data.id;
+ });
+
+ it('should delete the upstream with healthcheck params by name', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.name}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should not create upstream with invalid healthcheck success parameters (active)', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams`,
+ {
+ name: negTestName,
+ healthchecks: {
+ active: {
+ healthy: {
+ successes: -1,
+ },
+ },
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.contain(
+ 'value should be between 0 and 255'
+ );
+ });
+
+ it('should not create upstream with invalid value passed into http_statuses (active)', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams`,
+ {
+ name: negTestName,
+ healthchecks: { active: { healthy: { http_statuses: ['200'] } } },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.contain(
+ 'expected an integer'
+ );
+ });
+
+ it('should not create upstream with invalid value passed into http_statuses (passive)', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams`,
+ {
+ name: negTestName,
+ healthchecks: { passive: { healthy: { http_statuses: ['200'] } } },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.contain(
+ 'expected an integer'
+ );
+ });
+
+ it('should not create upstream with invalid headers passed into active healthchecks', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams/`,
+ {
+ name: negTestName,
+ healthchecks: {
+ active: {
+ headers: [{ 'not valid header': [''] }],
+ },
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.contain(
+ 'headers = "expected a string"'
+ );
+ });
+
+ // TODO: reenable these tests after FT-2643 is resolved
+ it.skip('should create upstream with valid algorithm parameter', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/`,
+ method: 'post',
+ data: {
+ name: upstreamData.name,
+ algorithm: 'consistent-hashing',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ expect(resp.data, 'should have id in response').to.have.property('id');
+ expect(resp.data.name, 'should return correct name').to.equal(
+ upstreamData.name
+ );
+ expect(resp.data.algorithm, 'should have updated algorithm').to.equal(
+ 'consistent-hashing'
+ );
+ });
+
+ it.skip('should delete the upstream with algorithm param by name', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.name}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should not create upstream with invalid algorithm', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams`,
+ { name: negTestName, algorithm: 'not-algo' },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 400').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ `schema violation (algorithm: expected one of: consistent-hashing, least-connections, round-robin, latency)`
+ );
+ });
+
+ it('should create upstream with valid host header', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/`,
+ method: 'post',
+ data: {
+ name: upstreamData.name,
+ host_header: hostHeader,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ expect(resp.data, 'should have id in response').to.have.property('id');
+ expect(resp.data.host_header, 'should have correct host header').to.equal(
+ hostHeader
+ );
+ expect(resp.data.name, 'should have correct name').to.equal(
+ upstreamData.name
+ );
+
+ upstreamData.id = resp.data.id;
+ upstreamData.host_header = resp.data.host_header;
+ });
+
+ it('should delete the upstream with host header by name', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.name}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should not create upstream with invalid host header', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams/`,
+ { name: negTestName, host_header: invalidHeader },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should return error message').to.equal(
+ 'schema violation (host_header: invalid hostname: not a header)'
+ );
+ });
+
+ it('should create upstream using PUT by name', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.name}`,
+ method: 'put',
+ data: {
+ name: upstreamData.name,
+ },
+ });
+ logResponse(resp);
+
+ // status code should be 201 as per FT-2607 or docs updated
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data, 'should contain an id in response').to.have.property(
+ 'id'
+ );
+ expect(resp.data.name, 'should have correct name').to.equal(
+ upstreamData.name
+ );
+
+ upstreamData.id = resp.data.id;
+ });
+
+ it('should delete upstream by id', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should create upstream using PUT and a valid uuid', async function () {
+ upstreamData.id = uuid;
+
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.id}`,
+ method: 'put',
+ data: {
+ name: upstreamData.name,
+ },
+ });
+ logResponse(resp);
+
+ // Commenting until FT-2607 fixed
+ // expect(resp.status, 'should return status 201').to.equal(201);
+ expect(resp.data.id, 'should have correct id').to.equal(upstreamData.id);
+ expect(resp.data.name, 'should have correct name').to.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should not create upstream with invalid name using PUT', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams/${invalidName}`,
+ { name: invalidName },
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.include(
+ 'must be a valid hostname'
+ );
+ });
+
+ it('should edit the upstream with PUT by id', async function () {
+ upstreamData.name = putName;
+
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.id}`,
+ method: 'put',
+ data: {
+ name: upstreamData.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should get updated upstream information by updated name', async function () {
+ const resp = await axios(
+ `${url}/${certificateData.id}/upstreams/${upstreamData.name}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should edit the upstream with PUT by name', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.name}`,
+ method: 'put',
+ data: {
+ tags: [tag],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'status should be 200').to.equal(200);
+ expect(resp.data.id, 'should return id in response').to.equal(
+ upstreamData.id
+ );
+ expect(resp.data.name, 'should have expected name').to.equal(
+ upstreamData.name
+ );
+ expect(resp.data.tags, 'should return expected tags').to.contain(tag);
+ });
+
+ it('should get PUT updated upstream information by id', async function () {
+ const resp = await axios(
+ `${url}/${certificateData.id}/upstreams/${upstreamData.id}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ expect(resp.data.tags, 'should include updated tags').to.contain(tag);
+ });
+
+ it('should edit the upstream with PATCH by id', async function () {
+ upstreamData.name = patchName;
+
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.id}`,
+ method: 'patch',
+ data: {
+ name: upstreamData.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should get patched information', async function () {
+ const resp = await axios(
+ `${url}/${certificateData.id}/upstreams/${upstreamData.id}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should edit the upstream with PATCH by name', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.name}`,
+ method: 'patch',
+ data: {
+ name: name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.name, 'should have updated name').to.be.equal(name);
+
+ upstreamData.name = name;
+ });
+
+ it('should get patched upstream information by updated name', async function () {
+ const resp = await axios(
+ `${url}/${certificateData.id}/upstreams/${upstreamData.name}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should not edit upstream with PATCH and invalid name', async function () {
+ const resp = await postNegative(
+ `${url}/${certificateData.id}/upstreams/not-a-current-name`,
+ { tags: ['test-tag'] },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 404').to.equal(404);
+ expect(resp.data.message, 'should have correct error message').to.equal(
+ 'Not found'
+ );
+ });
+
+ it('should delete upstream by id', async function () {
+ const resp = await axios({
+ url: `${url}/${certificateData.id}/upstreams/${upstreamData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should return 404 for deleted upstream', async function () {
+ const resp = await getNegative(
+ `${url}/${certificateData.id}/upstreams/${upstreamData.id}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 404').to.equal(404);
+ });
+
+ after(async function () {
+ // remove the certificate
+ await axios({
+ url: `${url}/${certificateData.id}`,
+ method: 'delete',
+ });
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/targets.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/targets.spec.ts
new file mode 100644
index 00000000..c8ff8301
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/targets.spec.ts
@@ -0,0 +1,496 @@
+import {
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ isGwHybrid,
+ postNegative,
+ logResponse,
+ retryRequest,
+ isGateway,
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway Admin API: Targets', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/upstreams`;
+
+ const name = 'test-upstream';
+ const target = 'example.com:8000';
+ const targetNoPort = 'example.com';
+ const updatedTarget = 'example.org:8000';
+ const targetCreationMethod = 'post';
+
+ const tag = 'testtag';
+
+ let upstreamData: any;
+ let targetData: any;
+
+ before(async function () {
+ // Create an upstream to associate with the target
+ const resp = await axios({
+ method: 'post',
+ url: url,
+ data: {
+ name: name,
+ healthchecks: {
+ passive: {
+ unhealthy: {
+ http_failures: 3,
+ },
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ upstreamData = {
+ name: resp.data.name,
+ id: resp.data.id,
+ };
+ });
+
+ it('should create target associated with upstream by upstream id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}/targets`,
+ method: targetCreationMethod,
+ data: {
+ target: target,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ });
+
+ it('should see created target associated with upstream by upstream id', async function () {
+ const resp = await axios(`${url}/${upstreamData.id}/targets`);
+ targetData = {
+ target: resp.data.data[0].target, // returned as host:8000 (or host:port if port given)
+ id: resp.data.data[0].id,
+ };
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data.length, 'should have one target in list').to.equal(1);
+ expect(resp.data.data.id, 'should return id');
+ });
+
+ it('should not create target if not given target field', async function () {
+ const resp = await postNegative(
+ `${url}/${upstreamData.id}/targets`,
+ {},
+ targetCreationMethod
+ );
+ logResponse(resp);
+
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ 'schema violation (target: required field missing)'
+ );
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ });
+
+ it('should not create target if given invalid target field', async function () {
+ const resp = await postNegative(
+ `${url}/${upstreamData.name}/targets`,
+ { target: 'not a valid target' },
+ targetCreationMethod
+ );
+ logResponse(resp);
+
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ 'schema violation (target: Invalid target; not a valid hostname or ip address)'
+ );
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ });
+
+ //TODO: reenable this test when FT-2644 is resolved
+ it.skip('should not create duplicate of existing target using host:port', async function () {
+ const resp = await postNegative(
+ `${url}/${upstreamData.id}/targets`,
+ { target: target },
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 409 status code').to.equal(409);
+ expect(resp.message, 'should return correct error message').to.equal(
+ 'UNIQUE violation detected on \'{target="example.com:8000"}\''
+ );
+ });
+
+ it('should see created target associated with upstream using upstream id and /all', async function () {
+ const resp = await axios(`${url}/${upstreamData.id}/targets/all`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data.length, 'should have one target in list').to.equal(1);
+ expect(resp.data.data.id, 'should return id');
+ });
+
+ it('should delete given upstream target by upstream id and target host:port', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}/targets/${targetData.target}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should create target associated with upstream by upstream id and using host only', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}/targets`,
+ method: targetCreationMethod,
+ data: {
+ target: targetNoPort,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ });
+
+ it('should not create duplicate of existing target using host only', async function () {
+ const resp = await postNegative(
+ `${url}/${upstreamData.id}/targets`,
+ { target: targetNoPort },
+ targetCreationMethod
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 409 status code').to.equal(409);
+ expect(resp.data.message, 'should return correct error message').to.contain(
+ 'UNIQUE violation detected on \'{target="example.com:8000"'
+ );
+ });
+
+ it('should see created target associated with upstream by upstream id', async function () {
+ const resp = await axios(`${url}/${upstreamData.id}/targets`);
+ logResponse(resp);
+ targetData.target = resp.data.data[0].target; // returned as host:8000 (or host:port if port given)
+ targetData.id = resp.data.data[0].id;
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data.length, 'should have one target in list').to.equal(1);
+ expect(resp.data.data.id, 'should return id');
+ });
+
+ it('should delete given upstream target by upstream id and target id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}/targets/${targetData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should create target associated with upstream by upstream name', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}/targets`,
+ method: targetCreationMethod,
+ data: {
+ target: target,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ expect(resp.data.target, 'should return correct target').to.equal(target);
+ expect(resp.data, 'should return id in response').to.have.property('id');
+ expect(resp.data, 'should have associated upstream').to.have.property(
+ 'upstream'
+ );
+ expect(resp.data.upstream.id, 'should have correct upstream id').to.equal(
+ upstreamData.id
+ );
+
+ targetData.id = resp.data.id;
+ });
+
+ it('should edit target with PATCH using upstream id and target id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}/targets/${targetData.id}`,
+ method: 'patch',
+ data: {
+ target: updatedTarget,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.id, 'expect id to be in response').to.equal(targetData.id);
+ expect(resp.data.target, 'expect updated target in response').to.equal(
+ updatedTarget
+ );
+
+ targetData.target = updatedTarget;
+ });
+
+ it('should edit target with PATCH using upstream id and target', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}/targets/${targetData.target}`,
+ method: 'patch',
+ data: {
+ tags: [tag],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.id, 'expect id to be in response').to.equal(targetData.id);
+ expect(resp.data.target, 'expect updated target in response').to.equal(
+ targetData.target
+ );
+ expect(resp.data.tags, 'should have updated tags').to.contain(tag);
+ });
+
+ it('should not edit target with PATCH if given invalid target', async function () {
+ const resp = await postNegative(
+ `${url}/${upstreamData.id}/targets/${targetData.target}`,
+ { target: 'not a valid target' },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.equal(
+ "schema violation (target: Invalid target ('not a valid target'); not a valid hostname or ip address)"
+ );
+ });
+
+ it('should see updated target associated with upstream using upstream id', async function () {
+ const resp = await axios(`${url}/${upstreamData.id}/targets`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data.length, 'should have one target in list').to.equal(1);
+ expect(resp.data.data[0].id, 'should return id').to.equal(targetData.id);
+ expect(resp.data.data[0].tags, 'should have updated tags').to.contain(tag);
+ expect(resp.data.data[0].target, 'should have updated target').to.equal(
+ targetData.target
+ );
+ });
+
+ it('should edit target with PATCH using upstream name and target id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}/targets/${targetData.id}`,
+ method: 'patch',
+ data: {
+ target: target,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.id, 'expect id to be in response').to.equal(targetData.id);
+ expect(resp.data.target, 'expect updated target in response').to.equal(
+ target
+ );
+
+ targetData.target = target;
+ });
+
+ it('should edit target with PATCH using upstream name and target', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}/targets/${targetData.target}`,
+ method: 'patch',
+ data: {
+ tags: [],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.id, 'expect id to be in response').to.equal(targetData.id);
+ expect(resp.data.tags, 'expect updated tags in response').to.be.empty;
+ });
+
+ it('should see updated target associated with upstream using upstream name', async function () {
+ const resp = await axios(`${url}/${upstreamData.name}/targets`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data.length, 'should have one target in list').to.equal(1);
+ expect(resp.data.data[0].id, 'should return id').to.equal(targetData.id);
+ expect(resp.data.data[0].tags, 'should have updated tags').to.be.empty;
+ expect(resp.data.data[0].target, 'should have updated target').to.equal(
+ targetData.target
+ );
+ });
+
+ it('should delete target by id associated with upstream by id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}/targets/${targetData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 204 status').to.equal(204);
+ });
+
+ it('should return empty target list', async function () {
+ const resp = await getNegative(`${url}/${upstreamData.id}/targets`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data.length, 'should have no targets').to.equal(0);
+ });
+
+ after(async function () {
+ await axios({
+ method: 'delete',
+ url: `${url}/${upstreamData.id}`,
+ });
+ });
+});
+
+describe('Gateway Admin API: Targets health', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/upstreams`;
+
+ const name = 'test-upstream';
+ // use IP to avoid DNS_ERRORs affecting test result (health status)
+ const targetIp = '127.0.0.1:8123';
+ const targetCreationMethod = 'post';
+ const isHybrid = isGwHybrid();
+
+ let upstreamData: any;
+ let targetData: any;
+
+ before(async function () {
+ if (isHybrid) {
+ this.skip();
+ }
+
+ // Create an upstream to associate with the target
+ const resp = await axios({
+ method: 'post',
+ url: url,
+ data: {
+ name: name,
+ healthchecks: {
+ passive: {
+ unhealthy: {
+ http_failures: 3,
+ },
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ upstreamData = {
+ name: resp.data.name,
+ id: resp.data.id,
+ };
+
+ // Create target
+ const respTarget = await axios({
+ url: `${url}/${upstreamData.id}/targets`,
+ method: targetCreationMethod,
+ data: {
+ target: targetIp,
+ },
+ });
+ logResponse(respTarget);
+ expect(respTarget.status, 'should return 201 status').to.equal(201);
+
+ targetData = {
+ target: respTarget.data.target,
+ id: respTarget.data.id
+ }
+ });
+
+ it('should set target to healthy using target id and address and confirm its status', async function () {
+ // set healthy status
+ const setStatusReq = () => axios({
+ url: `${url}/${upstreamData.id}/targets/${targetData.id}/healthy`,
+ method: 'put',
+ validateStatus: function (status) {
+ return status < 500;
+ }
+ });
+
+ const setStatusAssertions = (res) => {
+ logResponse(res);
+ expect(res.status, 'should return 204 status').to.equal(204);
+ };
+
+ await retryRequest(setStatusReq, setStatusAssertions);
+
+
+ // confirm healthy status
+ const req = () => axios(`${url}/${upstreamData.id}/health`);
+
+ const assertions = (resp) => {
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data[0].id, 'should match target id').to.equal(
+ targetData.id
+ );
+ expect(
+ resp.data.data[0].health,
+ 'should show a status of HEALTHY'
+ ).to.equal('HEALTHY');
+ expect(
+ resp.data.data[0].upstream.id,
+ 'should show associated upstream in response'
+ ).to.equal(upstreamData.id);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should set target status to unhealthy', async function () {
+ // set unhealthy status
+ const setStatusReq = () => axios({
+ url: `${url}/${upstreamData.id}/targets/${targetData.id}/unhealthy`,
+ method: 'put',
+ validateStatus: function (status) {
+ return status < 500;
+ }
+ });
+
+ const setStatusAssertions = (res) => {
+ logResponse(res);
+ expect(res.status, 'should return 204 status').to.equal(204);
+ };
+
+ await retryRequest(setStatusReq, setStatusAssertions);
+ });
+
+ it('should confirm target unhealthy status', async function () {
+ // confirm unhealthy status
+ const req = () => axios(`${url}/${upstreamData.id}/health`);
+
+ const assertions = (resp) => {
+ expect(resp.status, 'should return 200 status').to.equal(200);
+ expect(resp.data.data[0].id, 'should match target id').to.equal(
+ targetData.id
+ );
+ expect(
+ resp.data.data[0].health,
+ 'should show a status of UNHEALTHY'
+ ).to.equal('UNHEALTHY');
+ expect(
+ resp.data.data[0].upstream.id,
+ 'should show associated upstream in response'
+ ).to.equal(upstreamData.id);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+
+ after(async function () {
+ if (!isHybrid) {
+ await axios({
+ method: 'delete',
+ url: `${url}/${upstreamData.id}`,
+ });
+ }
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/upstreams.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/upstreams.spec.ts
new file mode 100644
index 00000000..ffa4c8e8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/upstreams/upstreams.spec.ts
@@ -0,0 +1,614 @@
+import {
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ postNegative,
+ logResponse,
+ isGateway,
+} from '@support';
+import axios from 'axios';
+
+describe('@smoke @gke: Gateway Admin API: Upstreams', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/upstreams`;
+
+ // Setting up test variables
+ const name = 'test-upstream';
+ const putName = 'put-test';
+ const patchName = 'patch-test';
+ const negTestName = 'negative-test';
+
+ const uuid = 'd0ffcef9-a28c-470c-9e6f-e40075a7c179';
+ const invalidName = 'ÅÍÎÏ˝ÓÔÒÚÆ';
+ const tag = 'testtag';
+ const updateTag = 'testtag2';
+
+ let upstreamData: any;
+
+ it('should create upstream by name', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: url,
+ data: {
+ name: name,
+ },
+ });
+ upstreamData = {
+ name: resp.data.name,
+ id: resp.data.id,
+ };
+
+ expect(resp.status, 'should return status 201').to.equal(201);
+ expect(resp.data, 'should return id').to.have.property('id');
+ expect(resp.data.name, 'should return expected name').to.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should get the created upstream', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(
+ resp.data.data.length,
+ 'should have at least one upstream in list'
+ ).to.be.at.least(1);
+ expect(resp.data.data[0].id, 'should have correct id').to.equal(
+ upstreamData.id
+ );
+ expect(resp.data.data[0].name, 'should have expected name').to.equal(
+ upstreamData.name
+ );
+ expect(
+ resp.data.data[0].algorithm,
+ 'should have round-robin algo by default'
+ ).to.equal('round-robin');
+ });
+
+ it('should not create upstream with same name', async function () {
+ const resp = await postNegative(url, { name: upstreamData.name }, 'post');
+ logResponse(resp);
+
+ expect(resp.status, 'should return 409 status').to.equal(409);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ `UNIQUE violation detected on '{name="${upstreamData.name}"}'`
+ );
+ });
+
+ it('should delete upstream by id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it.skip('should return 404 when attempting to delete upstream that does not exist', async function () {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const resp = await postNegative(
+ `${url}/not-an-upstream-at-all`,
+ {},
+ 'delete'
+ );
+ logResponse(resp);
+ // TODO: uncomment this check when FT-2645 is resolved
+ //expect(resp.status, 'should return status 404').to.equal(404);
+ });
+
+ it('should not create upstream with empty body', async function () {
+ const resp = await postNegative(`${url}`, {}, 'post');
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ 'schema violation (name: required field missing)'
+ );
+ });
+
+ it('should not create upstream with invalid name', async function () {
+ const resp = await postNegative(`${url}`, { name: invalidName }, 'post');
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ "schema violation (name: Invalid name ('ÅÍÎÏ˝ÓÔÒÚÆ'); must be a valid hostname)"
+ );
+ });
+
+ // TODO: uncomment headers line and check when FT-2646 is resolved
+ it('should create upstream with valid healthcheck parameters', async function () {
+ const resp = await axios({
+ url: `${url}`,
+ method: 'post',
+ data: {
+ name: upstreamData.name,
+ healthchecks: {
+ active: {
+ timeout: 2,
+ unhealthy: {
+ interval: 1,
+ tcp_failures: 5,
+ timeouts: 1,
+ http_failures: 5,
+ http_statuses: [500],
+ },
+ type: 'http',
+ concurrency: 11,
+ // headers: [{ 'X-Content-Type-Options': ['nosniff'] }],
+ healthy: {
+ interval: 1,
+ successes: 1,
+ http_statuses: [200, 204, 302, 201],
+ },
+ http_path: '/',
+ https_sni: 'example.com',
+ https_verify_certificate: true,
+ },
+ passive: {
+ type: 'http',
+ unhealthy: {
+ http_statuses: [500],
+ http_failures: 3,
+ timeouts: 1,
+ tcp_failures: 1,
+ },
+ healthy: {
+ http_statuses: [200, 201],
+ successes: 2,
+ },
+ },
+ threshold: 23,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ expect(resp.data, 'should have id in response').to.have.property('id');
+ expect(
+ resp.data.healthchecks.active.healthy.successes,
+ 'should have correct healthcheck active success value'
+ ).to.equal(1);
+ expect(
+ resp.data.healthchecks.active.unhealthy.http_failures,
+ 'should have correct healthcheck http failure value'
+ ).to.equal(5);
+ expect(
+ resp.data.healthchecks.passive.healthy.successes,
+ 'should have correct healthcheck passive success value'
+ ).to.equal(2);
+ expect(
+ resp.data.healthchecks.passive.unhealthy.http_failures,
+ 'should have correct healthcheck passive http failure value'
+ ).to.equal(3);
+ expect(
+ resp.data.healthchecks.threshold,
+ 'should have correct healthcheck threshold value'
+ ).to.equal(23);
+ // expect(
+ // resp.data.healthchecks.active.headers,
+ // 'should have correct headers'
+ // ).to.equal(`[{ 'X-Content-Type-Options': ['nosniff'] }]`);
+ });
+
+ it('should delete upstream with healthcheck params by name', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should not create upstream with invalid healthcheck success parameters (active)', async function () {
+ const resp = await postNegative(
+ `${url}`,
+ {
+ name: negTestName,
+ healthchecks: {
+ active: {
+ healthy: {
+ successes: -1,
+ },
+ },
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.contain(
+ 'value should be between 0 and 255'
+ );
+ });
+
+ it('should not create upstream with invalid value passed into http_statuses (active)', async function () {
+ const resp = await postNegative(
+ `${url}`,
+ {
+ name: negTestName,
+ healthchecks: { active: { healthy: { http_statuses: ['200'] } } },
+ },
+ 'post'
+ );
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.contain(
+ 'expected an integer'
+ );
+ });
+
+ it('should not create upstream with invalid value passed into http_statuses (passive)', async function () {
+ const resp = await postNegative(
+ `${url}`,
+ {
+ name: negTestName,
+ healthchecks: { passive: { healthy: { http_statuses: ['200'] } } },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.contain(
+ 'expected an integer'
+ );
+ });
+
+ it('should not create upstream with invalid headers passed into active healthchecks', async function () {
+ const resp = await postNegative(
+ `${url}`,
+ {
+ name: negTestName,
+ healthchecks: {
+ active: { headers: [{ 'not a header': ['test'] }] },
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ //TODO: uncomment when header bug fixed
+ // expect(resp.data.message, 'should return correct error message').to.contain(
+ // 'invalid header'
+ // );
+ });
+
+ // TODO: reenable these tests after FT-2643 is resolved
+ it.skip('should create upstream with valid algorithm parameter', async function () {
+ const resp = await axios({
+ url: `${url}/`,
+ method: 'post',
+ data: {
+ name: upstreamData.name,
+ algorithm: 'consistent-hashing',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ expect(resp.data, 'should have id in response').to.have.property('id');
+ expect(resp.data.name, 'should return correct name').to.equal(
+ upstreamData.name
+ );
+ expect(resp.data.algorithm, 'should have updated algorithm').to.equal(
+ 'consistent-hashing'
+ );
+ });
+
+ it.skip('should delete the upstream with algorithm param by name', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should not create upstream with invalid algorithm', async function () {
+ const resp = await postNegative(
+ `${url}`,
+ {
+ name: negTestName,
+ algorithm: 'round-connections',
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status code').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ 'schema violation (algorithm: expected one of: consistent-hashing, least-connections, round-robin, latency)'
+ );
+ });
+
+ it('should create upstream with valid host header', async function () {
+ const resp = await axios({
+ url: `${url}`,
+ method: 'post',
+ data: {
+ name: upstreamData.name,
+ host_header: 'example.com',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return 201 status').to.equal(201);
+ expect(resp.data, 'should have id in response').to.have.property('id');
+ expect(resp.data.host_header, 'should have correct host header').to.equal(
+ 'example.com'
+ );
+ });
+
+ it('should delete the upstream with host header by name', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should not create upstream with invalid host header', async function () {
+ const resp = await postNegative(
+ `${url}`,
+ { name: negTestName, host_header: 'not a header' },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.equal(
+ 'schema violation (host_header: invalid hostname: not a header)'
+ );
+ });
+
+ it('should create upstream using PUT by name', async function () {
+ // set new expected name
+ upstreamData.name = putName;
+
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}`,
+ method: 'put',
+ data: {
+ name: upstreamData.name,
+ },
+ });
+ logResponse(resp);
+
+ // Commenting until FT-2607 fixed
+ //expect(resp.status, 'should return status 201').to.equal(201);
+ expect(resp.data.name, 'should have expected name').to.equal(
+ upstreamData.name
+ );
+ expect(resp.data, 'should return id in response').to.have.property('id');
+
+ // update current upstreamData
+ upstreamData.id = resp.data.id;
+ });
+
+ it('should delete upstream by id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should create upstream using PUT and a valid uuid', async function () {
+ // set new expected name
+ upstreamData.name = name;
+
+ const resp = await axios({
+ url: `${url}/${uuid}`,
+ method: 'put',
+ data: {
+ name: upstreamData.name,
+ },
+ });
+ logResponse(resp);
+
+ // commenting until FT-2607 fixed
+ //expect(resp.status, 'should return status 201').to.equal(201);
+ expect(resp.data.name, 'should have expected name').to.equal(
+ upstreamData.name
+ );
+ expect(resp.data, 'should return id in response').to.have.property('id');
+
+ // update current upstreamData
+ upstreamData.id = resp.data.id;
+ });
+
+ it('should not create upstream with an invalid name using PUT', async function () {
+ const resp = await postNegative(`${url}/invalid%20name`, {}, 'put');
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ "Invalid name ('invalid name'); must be a valid hostname"
+ );
+ });
+
+ it('should edit the upstream with PUT by id', async function () {
+ // update upstream name
+ upstreamData.name = putName;
+
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}`,
+ method: 'put',
+ data: {
+ name: upstreamData.name,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should get updated information getting by updated name', async function () {
+ const resp = await axios(`${url}/${upstreamData.name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ });
+
+ it('should edit the upstream with PUT by name', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}`,
+ method: 'put',
+ data: {
+ tags: [tag],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.tags, 'should have updated tags').to.contain(tag);
+ });
+
+ it('should get PUT updated information getting by id', async function () {
+ const resp = await axios(`${url}/${upstreamData.id}`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have expected name').to.be.equal(
+ upstreamData.name
+ );
+ expect(resp.data.tags, 'should have updated tags').to.contain(tag);
+ });
+
+ it('should edit the upstream with PATCH by id', async function () {
+ upstreamData.name = patchName;
+
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}`,
+ method: 'patch',
+ data: {
+ name: upstreamData.name,
+ healthchecks: {
+ passive: {
+ unhealthy: {
+ http_failures: 3,
+ },
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ expect(
+ resp.data.healthchecks.passive.unhealthy.http_failures,
+ 'should have set failures to 3'
+ ).to.equal(3);
+ });
+
+ it('should get patched information', async function () {
+ const resp = await axios(`${url}/${upstreamData.id}`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ expect(
+ resp.data.healthchecks.passive.unhealthy.http_failures,
+ 'should have set failures to 3'
+ ).to.equal(3);
+ });
+
+ it('should edit the upstream with PATCH by name', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.name}`,
+ method: 'patch',
+ data: {
+ name: name,
+ tags: [updateTag],
+ },
+ });
+ logResponse(resp);
+
+ upstreamData.name = name;
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.id, 'should include upstream id').to.be.equal(
+ upstreamData.id
+ );
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ expect(resp.data.tags, 'should have updated tags').to.contain(updateTag);
+ });
+
+ it('should get patched information by updated name', async function () {
+ const resp = await axios(`${url}/${upstreamData.name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+ expect(resp.data.name, 'should have updated name').to.be.equal(
+ upstreamData.name
+ );
+ expect(resp.data.tags, 'should still include tags').to.contain(updateTag);
+ });
+
+ it('should not patch information with invalid name', async function () {
+ const resp = await postNegative(
+ `${url}/${upstreamData.id}`,
+ { name: invalidName },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'should return 400 status').to.equal(400);
+ expect(resp.data.message, 'should return correct error message').to.equal(
+ "schema violation (name: Invalid name ('ÅÍÎÏ˝ÓÔÒÚÆ'); must be a valid hostname)"
+ );
+ });
+
+ it('should delete given upstream by id', async function () {
+ const resp = await axios({
+ url: `${url}/${upstreamData.id}`,
+ method: 'delete',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 204').to.equal(204);
+ });
+
+ it('should return 404 for deleted upstream', async function () {
+ const resp = await getNegative(`${url}/${upstreamData.id}`);
+ logResponse(resp);
+
+ expect(resp.status, 'should return status 404').to.equal(404);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/aws-lambda-secret-reference.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/aws-lambda-secret-reference.spec.ts
new file mode 100644
index 00000000..d101b282
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/aws-lambda-secret-reference.spec.ts
@@ -0,0 +1,512 @@
+import axios from 'axios';
+import {
+ expect,
+ getBasePath,
+ Environment,
+ vars,
+ createGatewayService,
+ createRouteForService,
+ randomString,
+ waitForConfigRebuild,
+ createHcvVaultInKong,
+ createHcvVaultWithApproleInKong,
+ createHcvVaultSecrets,
+ enableHcvApproleAuth,
+ createHcvAppRole,
+ getHcvApproleID,
+ createHcvApproleSecretID,
+ createHcvApproleWrappedSecretId,
+ getHcvVaultSecret,
+ createAwsVaultEntity,
+ createEnvVaultEntity,
+ deleteHcvSecret,
+ deleteCache,
+ checkGwVars,
+ logResponse,
+ createGcpVaultEntity,
+ isGateway,
+ clearAllKongResources,
+ eventually
+} from '@support';
+
+// ********* Note *********
+// In order for this test to successfully run you need to have defined the following environment variables in all Kong nodes
+// AWS_REGION: us-east-2
+// AWS_ACCESS_KEY_ID: ${{ actualSecret}}
+// AWS_SECRET_ACCESS_KEY: ${{ actualSecret }}
+// GCP_SERVICE_ACCOUNT: ${{actualGcpAccountKey}}
+// ********* End **********
+
+describe('Vaults: Secret referencing in AWS-Lambda plugin', function () {
+ let serviceId = '';
+ let routeId = '';
+ let awsPluginId = '';
+
+ const path = `/${randomString()}`;
+ const pluginUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+ const gcpProjectId = 'gcp-sdet-test';
+ const hcvPrefix = 'my-hcv'
+ const hcvPrefixWithApprole = 'my-hcv1'
+ const hcvPrefixWithApproleAndResponseWrapping = 'my-hcv2'
+ const hcvMount = 'secret'
+ const hcvSecretPath = 'aws-secret'
+ const hcvApproleAuthPath = "approle"
+ const hcvRoleName = "test-role"
+
+ let hcvApproleID = '';
+ let hcvSecretID = '';
+ let hcvWrappedSecretID = '';
+
+ const awsFunctionName = 'gateway-awsplugin-test';
+ // aws credentials to be created in hcv vault
+ const awsAccessKey = vars.aws.AWS_ACCESS_KEY_ID;
+ const awsSecretKey = vars.aws.AWS_SECRET_ACCESS_KEY;
+ // env secrets (at this point should already exist in gateway/kong, see above Note) > AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
+ // aws secrets > gateway-secret-test/ aws_access_key, aws_secret_key
+ // gcp secrets > aws_access_key, aws_secret_key
+
+ const doBasicRequestCheck = async () => {
+ const resp = await axios(`${proxyUrl}${path}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ const createPlugin = async (serviceId, routeId) => {
+ const pluginPayload = {
+ name: 'aws-lambda',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ config: {
+ aws_key: awsAccessKey,
+ aws_secret: awsSecretKey,
+ aws_region: 'us-east-2',
+ function_name: awsFunctionName,
+ },
+ };
+
+ const resp: any = await axios({
+ method: 'post',
+ url: pluginUrl,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ return resp.data
+ };
+
+ before(async function () {
+ checkGwVars('aws');
+ await clearAllKongResources();
+ const service = await createGatewayService('VaultSecretAwsService');
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ const plugin = await createPlugin(serviceId, routeId)
+ awsPluginId = plugin.id
+ // creating hcv vault secret
+ await createHcvVaultSecrets({
+ aws_access_key: awsAccessKey,
+ aws_secret_key: awsSecretKey,
+ }, hcvMount, hcvSecretPath);
+
+ //creating hcv vault entity
+ await createHcvVaultInKong();
+
+ // creating hcv vault entity with approle;
+ await enableHcvApproleAuth();
+ await createHcvAppRole(hcvApproleAuthPath, hcvRoleName);
+ hcvApproleID = await getHcvApproleID(hcvApproleAuthPath, hcvRoleName);
+ hcvSecretID = await createHcvApproleSecretID(hcvApproleAuthPath, hcvRoleName);
+ hcvWrappedSecretID = await createHcvApproleWrappedSecretId(hcvApproleAuthPath, hcvRoleName);
+
+ // creating hcv vault entity with approle & secret id;
+ await createHcvVaultWithApproleInKong(hcvMount, hcvPrefixWithApprole, hcvApproleID, hcvSecretID, false);
+ // creating hcv vault entity with approle & wrapped secret id;
+ await createHcvVaultWithApproleInKong(hcvMount, hcvPrefixWithApproleAndResponseWrapping, hcvApproleID, hcvWrappedSecretID, true);
+
+ // creating my-env vault entity with varaible reference prefix 'aws_'
+ await createEnvVaultEntity('my-env', { prefix: 'aws_' });
+ // creating my-aws vault entity
+ await createAwsVaultEntity();
+ // creating my-gcp vault entity
+ await createGcpVaultEntity();
+ });
+
+ it('should create hcv vault entity and secrets', async function () {
+ const resp = await getHcvVaultSecret(hcvMount, hcvSecretPath);
+
+ expect(resp.data.aws_secret_key, 'Should see aws secret ket').to.equal(
+ awsSecretKey
+ );
+ expect(resp.data.aws_access_key, 'Should see aws access key').to.equal(
+ awsAccessKey
+ );
+ });
+
+ it('should reference with aws access key hcv vault entity', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://${hcvPrefix}/${hcvSecretPath}/aws_access_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(patchResp.data.config.aws_key, 'Should have aws_key referenced').to.equal(
+ `{vault://${hcvPrefix}/${hcvSecretPath}/aws_access_key}`
+ );
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws access and secret keys hcv vault entity', async function () {
+ // changing aws-lambda plaintext aws_secret to hcv vault enittiy secret reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_secret: `{vault://${hcvPrefix}/${hcvSecretPath}/aws_secret_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.aws_secret,
+ 'Should replace aws_secret with secret key reference'
+ ).to.equal(`{vault://${hcvPrefix}/${hcvSecretPath}/aws_secret_key}`);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws access and secret keys hcv vault secrets', async function () {
+ // changing aws-lambda plaintext aws_secret to hcv vault enittiy secret reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://hcv/${hcvSecretPath}/aws_access_key}`,
+ aws_secret: `{vault://hcv/${hcvSecretPath}/aws_secret_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.aws_secret,
+ 'Should replace aws_secret with hcv secret key reference'
+ ).to.equal(`{vault://hcv/${hcvSecretPath}/aws_secret_key}`);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key hcv and secret_key hcv vault entity secret', async function () {
+ // changing aws-lambda plaintext aws_secret to hcv vault enittiy secret reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://hcv/${hcvSecretPath}/aws_access_key}`,
+ aws_secret: `{vault://${hcvPrefix}/${hcvSecretPath}/aws_secret_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.aws_secret,
+ 'Should replace aws_secret with hcv secret key reference'
+ ).to.equal(`{vault://${hcvPrefix}/${hcvSecretPath}/aws_secret_key}`);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key hcv and secret_key hcv vault entity secret with approle', async function () {
+ // changing aws-lambda plaintext aws_secret to hcv vault enittiy secret reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://${hcvPrefixWithApprole}/${hcvSecretPath}/aws_access_key}`,
+ aws_secret: `{vault://${hcvPrefixWithApprole}/${hcvSecretPath}/aws_secret_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.aws_secret,
+ 'Should replace aws_secret with hcv secret key reference'
+ ).to.equal(`{vault://${hcvPrefixWithApprole}/${hcvSecretPath}/aws_secret_key}`);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key hcv and secret_key hcv vault entity secret with approle and response wrapping', async function () {
+ // changing aws-lambda plaintext aws_secret to hcv vault enittiy secret reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://${hcvPrefixWithApproleAndResponseWrapping}/${hcvSecretPath}/aws_access_key}`,
+ aws_secret: `{vault://${hcvPrefixWithApproleAndResponseWrapping}/${hcvSecretPath}/aws_secret_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.aws_secret,
+ 'Should replace aws_secret with hcv secret key reference'
+ ).to.equal(`{vault://${hcvPrefixWithApproleAndResponseWrapping}/${hcvSecretPath}/aws_secret_key}`);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key env and secret_key hcv vault entity', async function () {
+ // changing aws-lambda aws_key to env secret reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: '{vault://env/aws_access_key_id}',
+ aws_secret: `{vault://${hcvPrefix}/${hcvSecretPath}/aws_secret_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key env and secret_key aws secrets', async function () {
+ // changing aws-lambda aws_secret to aws secret reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_secret: '{vault://aws/gateway-secret-test/aws_secret_key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.aws_secret,
+ 'Should replace aws_secret with secret key reference'
+ ).to.equal('{vault://aws/gateway-secret-test/aws_secret_key}');
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key env and secret_key aws vault entity secrets', async function () {
+ // changing aws-lambda aws_secret to my-aws and aws_key to my-env vault enittiy reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ // note, we are stripping aws_ part from the secret name
+ aws_key: '{vault://my-env/access_key_id}',
+ aws_secret: '{vault://my-aws/gateway-secret-test/aws_secret_key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key hcv and secret_key aws vault entity secrets', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://${hcvPrefix}/${hcvSecretPath}/aws_access_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await eventually(async () => {
+ doBasicRequestCheck();
+ });
+ });
+
+ it('should reference with aws_access_key hcv and secret_key env vault entity secrets', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ // note, we are stripping aws_ part from the secret name
+ aws_secret: '{vault://my-env/secret_access_key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key aws vault and secret key aws vault entity secrets', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: '{vault://aws/gateway-secret-test/aws_access_key}',
+ aws_secret: '{vault://my-aws/gateway-secret-test/aws_secret_key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key aws vault and secret key gcp vault entity secrets', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: '{vault://aws/gateway-secret-test/aws_access_key}',
+ aws_secret: '{vault://my-gcp/aws_secret_key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key gcp vault and secret key gcp vault entity secrets', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://gcp/aws_access_key?project_id=${gcpProjectId}}`,
+ aws_secret: '{vault://my-gcp/aws_secret_key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key gcp vault and secret key hcv vault entity secrets', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_secret: `{vault://${hcvPrefix}/${hcvSecretPath}/aws_secret_key}`,
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ it('should reference with aws_access_key gcp and secret key env vault entity secrets', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${awsPluginId}`,
+ data: {
+ name: 'aws-lambda',
+ config: {
+ aws_key: `{vault://my-env/access_key_id}`,
+ aws_secret: '{vault://my-gcp/aws_secret_key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ await doBasicRequestCheck();
+ });
+
+ after(async function () {
+ await deleteCache();
+ await deleteHcvSecret(hcvMount, hcvSecretPath);
+ await clearAllKongResources();
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/azure-functions-secret-reference.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/azure-functions-secret-reference.spec.ts
new file mode 100644
index 00000000..180f5fe8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/azure-functions-secret-reference.spec.ts
@@ -0,0 +1,190 @@
+import axios from 'axios';
+import {
+ expect,
+ getBasePath,
+ Environment,
+ createGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ randomString,
+ deleteVaultEntity,
+ deleteCache,
+ logResponse,
+ createAzureVaultEntity,
+ waitForConfigRebuild,
+ getNegative,
+ checkGwVars,
+ vars,
+ isGateway
+} from '@support';
+
+// ********* Note *********
+// In order for this test to successfully run you need to
+// successfully staret gateway with AZURE_VAULT=true in https://github.com/Kong/gateway-docker-compose-generator
+//
+// You will also need to have AZURE_FUNCTION_KEY variable defined in your test environment
+// ********* End **********
+
+describe('Vaults: Azure Secret referencing in Azure functions plugin', function () {
+ this.timeout(50000)
+
+ let serviceId = '';
+ let routeId = '';
+ let azurePluginId = '';
+
+ const path = `/${randomString()}`;
+ const pluginUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+
+ const azureFunctionKey = vars.azure.AZURE_FUNCTION_KEY;
+
+
+ before(async function () {
+ checkGwVars('azure');
+ const service = await createGatewayService('AzureVaultFunctionService', { url: 'http://httpbin' });
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ await createAzureVaultEntity('my-azure', { ttl: 60, neg_ttl: 60, resurrect_ttl: 60 })
+ });
+
+ it('should create azure functions plugin', async function () {
+ const pluginPayload = {
+ name: 'azure-functions',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ config: {
+ apikey: azureFunctionKey,
+ appname: 'sdet-function',
+ functionname: 'sdet-http-trigger',
+ },
+ };
+
+ const resp: any = await axios({
+ method: 'post',
+ url: pluginUrl,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ azurePluginId = resp.data.id;
+
+ expect(azurePluginId , 'Plugin Id should be a string').to.be.string;
+ expect(resp.data.config.apikey, 'Should have apikey referenced').to.equal(azureFunctionKey);
+
+ await waitForConfigRebuild()
+ });
+
+ it('should trigger the azure function', async function () {
+ const resp = await axios(`${proxyUrl}${path}?name=azureTest`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data, 'Should have correct response text from function').to.include('azureTest')
+ });
+
+ it('should patch azure-functions plugin and reference apikey as Azure vault secret', async function () {
+ // changing azure-functions apikey to a referenced secret value from Azure Vault
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${azurePluginId}`,
+ data: {
+ name: 'azure-functions',
+ config: {
+ apikey: '{vault://azure/automation-azure-function-key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.apikey,
+ 'Should replace apikey with azure secret reference'
+ ).to.equal('{vault://azure/automation-azure-function-key}');
+
+ await waitForConfigRebuild()
+ });
+
+ it('should trigger the azure function when apikey is referenced as Azure Vault secret', async function () {
+ const resp = await axios(`${proxyUrl}${path}?name=azureTest88`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data, 'Should have correct response text from function').to.include('azureTest88')
+ });
+
+ it('should trigger the azure function when apikey is referenced as Azure Vault entity secret', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${azurePluginId}`,
+ data: {
+ name: 'azure-functions',
+ config: {
+ apikey: '{vault://my-azure/automation-azure-function-key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.apikey,
+ 'Should replace apikey with azure secret reference'
+ ).to.equal('{vault://my-azure/automation-azure-function-key}');
+
+ await waitForConfigRebuild()
+
+ const resp = await axios(`${proxyUrl}${path}?name=azureTestEntity`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data, 'Should have correct response text from function').to.include('azureTestEntity')
+ });
+
+ it('should not trigger the azure function when apikey is referenced as wrong Azure Vault entity secret', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${azurePluginId}`,
+ data: {
+ name: 'azure-functions',
+ config: {
+ apikey: '{vault://my-azure/automation-azure-function-wrong-key}',
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.apikey,
+ 'Should replace apikey with azure secret reference'
+ ).to.equal('{vault://my-azure/automation-azure-function-wrong-key}');
+
+ await waitForConfigRebuild()
+
+ const resp = await getNegative(`${proxyUrl}${path}?name=azureWrongEntity`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ });
+
+
+ after(async function () {
+ // need to delete cache for secret referencing to work with updated secrets
+ await deleteCache();
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteVaultEntity('my-azure')
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/rla-secret-reference.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/rla-secret-reference.spec.ts
new file mode 100644
index 00000000..6d3b2cf2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/rla-secret-reference.spec.ts
@@ -0,0 +1,573 @@
+import axios from 'axios';
+import {
+ expect,
+ getBasePath,
+ Environment,
+ createGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ getNegative,
+ randomString,
+ wait,
+ createHcvVaultInKong,
+ createHcvVaultSecrets,
+ getHcvVaultSecret,
+ deleteVaultEntity,
+ createAwsVaultEntity,
+ createEnvVaultEntity,
+ deleteHcvSecret,
+ deleteCache,
+ checkGwVars,
+ logResponse,
+ createGcpVaultEntity,
+ isGateway
+} from '@support';
+
+// ********* Note *********
+// In order for this test to successfully run you need to have defined the following environment variables in all Kong nodes
+// RLA_REDISU: redisuser
+// RLA_REDISP: redispassword
+// AWS_REGION: us-east-2
+// AWS_ACCESS_KEY_ID: ${{ actualSecret}}
+// AWS_SECRET_ACCESS_KEY: ${{ actualSecret }}
+// GCP_SERVICE_ACCOUNT: ${{actualGcpAccountKey}}
+// ********* End **********
+
+describe('@gke: Vaults: Secret referencing in RLA Plugin', function () {
+ let serviceId = '';
+ let routeId = '';
+ let rlaPluginId = '';
+
+ const path = `/${randomString()}`;
+ const pluginUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+ const gcpProjectId = 'gcp-sdet-test';
+ // hcv secrets
+ const redisHcvPassword = 'redispassword';
+ const redisHcvUser = 'redisuser';
+ // env secrets > RLA_REDISU (redisuser), RLA_REDISP (redispassword)
+ // aws secrets > gateway-secret-test/ rla_redisu (redisuser), rla_redisp (redispassword)
+ // gcp secrets > aws_access_key, aws_secret_key
+ const waitTime = 8000;
+
+ const window_size = 4;
+
+ const doBasicRateLimitCheck = async () => {
+ /**
+ * This test will make 2 requests to the same path.
+ * The first request should be successful.
+ * The second request should be rate limited.
+ *
+ * But there is a unlucky chance could make the previous
+ * assumption is wrong (2nd request will be successful).
+ *
+ * | 1st request | 2nd request |
+ * +-------------+-------------+
+ * 1st Wnd 2nd Wnd
+ *
+ * Wnd: Window
+ *
+ * If the 2nd request hit the 2nd Window,
+ * it will be successful,
+ * and our test will fail.
+ *
+ * If we are facing this situation,
+ * that means the 2nd request was made
+ * at the start of the 2nd Window.
+ * So we can wait for the 3rd Window SAFELY.
+ *
+ * The resolution is that
+ * if the 2nd request is successful,
+ * we will wait for the 3rd Window,
+ * and then make two request immediately.
+ *
+ * | 1st request | 2nd request | 3rd+4th requests |
+ * +------------------+------------------+------------------+
+ * 1st Wnd 2nd Wnd 3rd Wnd
+ *
+ * As the Windows are 4 seconds,
+ * I believe the we can make 3rd+4th requests in 4 seconds.
+ */
+
+ for (let i = 0; i < 2; i++) {
+ const resp: any = await getNegative(`${proxyUrl}/${path}`);
+
+ if (i === 1) {
+ if (resp.status === 429) {
+ return;
+ }
+
+ if (resp.status != 200) {
+ expect(
+ resp.status,
+ 'Status should be 429 meaning secret reference worked'
+ ).to.equal(429);
+ }
+ } else {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+ }
+
+ await wait((window_size + 0.5) * 1000); // eslint-disable-line no-restricted-syntax
+
+ for (let i = 0; i < 2; i++) {
+ const resp: any = await getNegative(`${proxyUrl}/${path}`);
+
+ if (i === 1) {
+ expect(
+ resp.status,
+ 'Status should be 429 meaning secret reference worked'
+ ).to.equal(429);
+ } else {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+ }
+ };
+
+ function skipIfIt(title, test) {
+ const condition = process.env.HCV!=='false';
+ return condition ? it(title, test) : it.skip(title, test);
+ }
+
+ before(async function () {
+ checkGwVars('aws');
+ const service = await createGatewayService('VaultSecretService');
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ });
+
+ skipIfIt('should create hcv vault entity and secrets', async function () {
+ await createHcvVaultInKong();
+
+ await createHcvVaultSecrets({
+ redisHcvPassword: redisHcvPassword,
+ redisHcvUser: redisHcvUser,
+ });
+
+ const resp = await getHcvVaultSecret();
+
+ expect(resp.data.redisHcvUser, 'Should see redis username').to.equal(
+ redisHcvUser
+ );
+ });
+
+ it('should create RLA plugin with aws secret reference and do rate limiting', async function () {
+ const pluginPayload = {
+ name: 'rate-limiting-advanced',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ config: {
+ limit: [1],
+ window_size: [window_size],
+ sync_rate: 0,
+ strategy: 'redis',
+ redis: {
+ host: 'redis',
+ port: 6379,
+ username: '{vault://aws/gateway-secret-test/rla_redisu}',
+ password: '{vault://aws/gateway-secret-test/rla_redisp}'
+ },
+ },
+ };
+
+ const resp: any = await axios({
+ method: 'post',
+ url: pluginUrl,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ rlaPluginId = resp.data.id;
+
+ expect(rlaPluginId, 'Plugin Id should be a string').to.be.string;
+
+ expect(
+ resp.data.config.redis.username,
+ 'Should have redis username referenced'
+ ).to.equal('{vault://aws/gateway-secret-test/rla_redisu}');
+
+ expect(
+ resp.data.config.redis.password,
+ 'Should have passport referenced'
+ ).to.equal('{vault://aws/gateway-secret-test/rla_redisp}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ it('should rate limit with redis password gcp and username gcp vault secret referenced', async function () {
+
+ // changing RLA Plugin plaintext redis password to gcp reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: `{vault://gcp/rla_redisu?project_id=${gcpProjectId}}`,
+ password: `{vault://gcp/rla_redisp?project_id=${gcpProjectId}}`,
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with aws reference'
+ ).to.equal(`{vault://gcp/rla_redisp?project_id=${gcpProjectId}}`);
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should patch RLA plugin with hcv secret reference', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: '{vault://my-hcv/secret/redisHcvUser}',
+ password: redisHcvPassword
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.username,
+ 'Should replace redis user with reference'
+ ).to.equal('{vault://my-hcv/secret/redisHcvUser}');
+
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal(redisHcvPassword);
+ });
+
+ skipIfIt('should rate limit with redis password hcv vault entity referenced', async function () {
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should rate limit with redis password and username hcv vault entity referenced', async function () {
+ // changing RLA Plugin plaintext redis password to hcv reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ password: '{vault://my-hcv/secret/redisHcvPassword}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal('{vault://my-hcv/secret/redisHcvPassword}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should rate limit with redis password and username hcv vault secret referenced', async function () {
+ // changing RLA Plugin plaintext redis password to hcv reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: '{vault://hcv/secret/redisHcvUser}',
+ password: '{vault://hcv/secret/redisHcvPassword}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with hcv reference'
+ ).to.equal('{vault://hcv/secret/redisHcvPassword}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should rate limit with redis password hcv and username hcv vault entity secret referenced', async function () {
+ // changing RLA Plugin plaintext redis password to hcv reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: '{vault://my-hcv/secret/redisHcvUser}',
+ password: '{vault://hcv/secret/redisHcvPassword}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should rate limit with redis password env and username hcv vault entity referenced', async function () {
+ // changing RLA Plugin hcv redis password to env reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ password: '{vault://env/rla_redisp}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal('{vault://env/rla_redisp}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should rate limit with redis password aws and username hcv vault entity referenced', async function () {
+ // changing RLA Plugin hcv redis password to env reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ password: '{vault://aws/gateway-secret-test/rla_redisp}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal('{vault://aws/gateway-secret-test/rla_redisp}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should rate limit with redis password aws and username env vault referenced', async function () {
+ // changing RLA Plugin hcv redis password to env reference
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: '{vault://env/rla_redisu}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.username,
+ 'Should replace redis username with reference'
+ ).to.equal('{vault://env/rla_redisu}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ it('should create env,gcp and aws vault entities', async function () {
+ // creating my-env vault entity with varaible reference prefix 'rla_'
+ await createEnvVaultEntity('my-env', { prefix: 'rla_' });
+ // creating my-aws vault entity
+ await createAwsVaultEntity();
+ // creating my-gcp vault entity
+ await createGcpVaultEntity();
+ });
+
+ it('should rate limit with redis password aws vault entity and username env vault entity referenced', async function () {
+ // changing RLA Plugin aws and env references to the new created backend entities
+
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ // notice that we can strip the rla_ prefix as it is already defined in my-env backend vault config
+ username: '{vault://my-env/redisu}',
+ password: '{vault://my-aws/gateway-secret-test/rla_redisp}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.username,
+ 'Should replace redis username with reference'
+ ).to.equal('{vault://my-env/redisu}');
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal('{vault://my-aws/gateway-secret-test/rla_redisp}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ it('should rate limit with redis password env and username gcp vault entity referenced', async function () {
+ // changing RLA Plugin redis username reference to gcp vault entity
+
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: '{vault://my-gcp/rla_redisu}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.username,
+ 'Should replace redis username with reference'
+ ).to.equal('{vault://my-gcp/rla_redisu}');
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal('{vault://my-aws/gateway-secret-test/rla_redisp}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ skipIfIt('should rate limit with redis password gcp vault and username hcv vault entity referenced', async function () {
+ // changing RLA Plugin redis username reference to hcv vault entity and password to gcp vault reference
+
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: '{vault://my-hcv/secret/redisHcvUser}',
+ password: `{vault://gcp/rla_redisp?project_id=${gcpProjectId}}`,
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.username,
+ 'Should replace redis username with reference'
+ ).to.equal('{vault://my-hcv/secret/redisHcvUser}');
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal(`{vault://gcp/rla_redisp?project_id=${gcpProjectId}}`);
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ it('should rate limit with redis password aws and username gcp vault entity referenced', async function () {
+ // changing RLA Plugin redis username reference to gcp vault entity and password to aws vault entity reference
+
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${pluginUrl}/${rlaPluginId}`,
+ data: {
+ name: 'rate-limiting-advanced',
+ config: {
+ redis: {
+ username: `{vault://my-gcp/rla_redisu}`,
+ password: '{vault://my-aws/gateway-secret-test/rla_redisp}',
+ },
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.redis.username,
+ 'Should replace redis username with reference'
+ ).to.equal(`{vault://my-gcp/rla_redisu}`);
+ expect(
+ patchResp.data.config.redis.password,
+ 'Should replace redis password with reference'
+ ).to.equal('{vault://my-aws/gateway-secret-test/rla_redisp}');
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ await doBasicRateLimitCheck();
+ });
+
+ after(async function () {
+ // need to delete cache for secret referencing to work with updated secrets
+ await deleteCache();
+ ['my-hcv', 'my-env', 'my-aws', 'my-gcp'].forEach(async (backendVault) => {
+ await deleteVaultEntity(backendVault);
+ });
+ // SKIP HCV deletion if HCV is disabled
+ if (process.env.HCV!=='false') {
+ await deleteHcvSecret('secret', 'secret');
+ }
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-aws.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-aws.spec.ts
new file mode 100644
index 00000000..c6cf9a2c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-aws.spec.ts
@@ -0,0 +1,266 @@
+import axios, { AxiosResponse } from 'axios';
+import {
+ expect,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('@gke: Vaults: AWS', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/vaults`;
+
+ const vaultPrefix = 'awsprefix';
+ const vaultPrefix2 = 'awsprefix2';
+ const updatedPrefix = 'updatedawsprefix';
+ const vaultName = 'aws';
+
+ const assertBasicDetails = (
+ resp: AxiosResponse,
+ vaultName: string,
+ vaultPrefix: string
+ ) => {
+ expect(resp.data.name, 'Should have correct vault name').equal(vaultName);
+ expect(resp.data.prefix, 'Should have correct vault prefix').equal(
+ vaultPrefix
+ );
+ expect(resp.data.created_at, 'Should see created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.updated_at, 'Should see updated_at number').to.be.a(
+ 'number'
+ );
+ };
+
+ it('should create a new aws vault', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix,
+ description: 'aws vault',
+ tags: ['awstag'],
+ config: {
+ region: 'us-east-2',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.tags[0], 'Should have correct tags').to.eq('awstag');
+ assertBasicDetails(resp, vaultName, vaultPrefix);
+ expect(resp.data.description, 'Should have correct description').equal(
+ 'aws vault'
+ );
+ expect(resp.data.config.region, 'Should see region config').to.equal(
+ 'us-east-2'
+ );
+ });
+
+ it('should not create aws vault with same prefix', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: vaultPrefix,
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ `UNIQUE violation detected on '{prefix="awsprefix"}'`
+ );
+ });
+
+ it('should not create aws vault with wrong region', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: 'otherprefix',
+ config: {
+ region: 'wrong-east-2',
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.region: expected one of: us-east-2, us-east-1`
+ );
+ });
+
+ it('should patch the aws vault', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${vaultPrefix}`,
+ data: {
+ prefix: updatedPrefix,
+ description: 'my vault',
+ tags: ['aws', 'tag', 'more', 'tags'],
+ config: {
+ region: 'us-east-1',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Should see 4 tags').to.have.lengthOf(4);
+ expect(resp.data.config.region, 'Should see config prefix').to.equal(
+ 'us-east-1'
+ );
+ assertBasicDetails(resp, vaultName, updatedPrefix);
+ });
+
+ it('should not patch the aws vault with wrong region', async function () {
+ const resp = await postNegative(
+ `${url}/${updatedPrefix}`,
+ {
+ prefix: 'someotherprefix',
+ config: {
+ region: 'us-east-23',
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.region: expected one of: us-east-2, us-east-1`
+ );
+ expect(
+ resp.data.fields.config.region,
+ 'Should have correct error message for config'
+ ).to.include(`expected one of: us-east-2, us-east-1, us-west-1`);
+ });
+
+ it('should not patch the aws vault with config.prefix', async function () {
+ const resp = await postNegative(
+ `${url}/${updatedPrefix}`,
+ {
+ prefix: 'someotherprefix',
+ config: {
+ region: 'us-west-2',
+ preifx: 'SECURE_',
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.preifx: unknown field)`
+ );
+ });
+
+ it('should create a vault with put request', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${vaultPrefix2}`,
+ data: {
+ name: vaultName,
+ prefix: 'secondaryprefix',
+ config: {
+ region: 'us-west-2',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.prefix, 'Should see config prefix').to.equal(vaultPrefix2);
+ assertBasicDetails(resp, vaultName, vaultPrefix2);
+ });
+
+ it('should not update the vault with put request and wrong config', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix2}`,
+ {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ prefix: 'bar',
+ },
+ },
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (config.prefix: unknown field)'
+ );
+ });
+
+ it('should update the vault with put request and prefix in uri', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/newprefix`,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ region: 'me-south-1',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.region,
+ 'Should see updated config region'
+ ).to.equal('me-south-1');
+ assertBasicDetails(resp, vaultName, 'newprefix');
+ });
+
+ it('should retrieve the updated aws vault', async function () {
+ const resp = await axios(`${url}/newprefix`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.region, 'Should see config region').to.equal(
+ 'me-south-1'
+ );
+ assertBasicDetails(resp, vaultName, 'newprefix');
+ });
+
+ it('should list all aws vaults', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.data,
+ 'Should see all 3 items in the list'
+ ).to.have.lengthOf(3);
+ expect(
+ resp.data.data.map((vault) => vault.prefix),
+ 'Should see all vault prefixes'
+ ).to.have.members(['newprefix', updatedPrefix, vaultPrefix2]);
+ });
+
+ it('should delete aws vaults', async function () {
+ for (const prefix of ['newprefix', vaultPrefix2, updatedPrefix]) {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${prefix}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ }
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-azure.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-azure.spec.ts
new file mode 100644
index 00000000..13de6c14
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-azure.spec.ts
@@ -0,0 +1,232 @@
+import axios, { AxiosResponse } from 'axios';
+import {
+ expect,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('@gke: Vaults: Azure', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/vaults`;
+
+ const vaultPrefix = 'azureprefix';
+ const vaultPrefix2 = 'azureprefix2';
+ const updatedPrefix = 'updatedazureprefix';
+ const vaultName = 'azure';
+ const vault_uri = 'http://azure-sdet.azure.com';
+
+ const assertBasicDetails = (
+ resp: AxiosResponse,
+ vaultName: string,
+ vaultPrefix: string
+ ) => {
+ expect(resp.data.name, 'Should have correct vault name').equal(vaultName);
+ expect(resp.data.prefix, 'Should have correct vault prefix').equal(
+ vaultPrefix
+ );
+ expect(resp.data.created_at, 'Should see created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.updated_at, 'Should see updated_at number').to.be.a(
+ 'number'
+ );
+ };
+
+ it('should create a new Azure vault', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix,
+ description: 'Azure vault',
+ config: {
+ vault_uri: vault_uri,
+ location: 'eastus',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ assertBasicDetails(resp, vaultName, vaultPrefix);
+ expect(resp.data.description, 'Should have correct description').equal(
+ 'Azure vault'
+ );
+ expect(resp.data.config.vault_uri, 'Should see vault_uri').to.equal(
+ vault_uri
+ );
+ });
+
+ it('should not create Azure vault with same prefix', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: vaultPrefix,
+ config: {
+ vault_uri: vault_uri,
+ location: 'eastus',
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ `UNIQUE violation detected on '{prefix="azureprefix"}'`
+ );
+ });
+
+ it('should not create Azure vault with wrong config key', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: 'someprefix',
+ config: {
+ unknown: 'unknown-keyname',
+ location: "eastus",
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `2 schema violations (config.unknown: unknown field; config.vault_uri: required field missing)`
+ );
+ });
+
+ it('should patch the Azure vault', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${vaultPrefix}`,
+ data: {
+ prefix: updatedPrefix,
+ description: 'my patched vault',
+ tags: ['Azure', 'tag', 'more', 'tags'],
+ config: {
+ vault_uri: 'http://testid.com',
+ location: 'westus',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Should see 4 tags').to.have.lengthOf(4);
+ expect(
+ resp.data.config.vault_uri,
+ 'Should see updated vault_uri'
+ ).to.equal('http://testid.com');
+ assertBasicDetails(resp, vaultName, updatedPrefix);
+ });
+
+ it('should not patch the Azure vault with wrong vault_uri', async function () {
+ const resp = await postNegative(
+ `${url}/${updatedPrefix}`,
+ {
+ prefix: 'someotherprefix',
+ config: {
+ vault_uri: true,
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.vault_uri: expected a string)`
+ );
+ });
+
+ it('should create a vault with put request', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${vaultPrefix2}`,
+ data: {
+ name: vaultName,
+ prefix: 'secondaryprefix',
+ config: {
+ vault_uri: vault_uri,
+ location: 'eastus',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.prefix, 'Should see config prefix').to.equal(vaultPrefix2);
+ assertBasicDetails(resp, vaultName, vaultPrefix2);
+ });
+
+ it('should update the vault with put request and prefix in uri', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/newprefix`,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ location: 'test',
+ vault_uri: 'http://test.com',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.location,
+ 'Should see updated config location'
+ ).to.equal('test');
+ assertBasicDetails(resp, vaultName, 'newprefix');
+ });
+
+ it('should retrieve the updated Azure vault', async function () {
+ const resp = await axios(`${url}/newprefix`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.location,
+ 'Should see config location'
+ ).to.equal('test');
+ assertBasicDetails(resp, vaultName, 'newprefix');
+ });
+
+ it('should list all Azure vaults', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.data,
+ 'Should see all 3 items in the list'
+ ).to.have.lengthOf(3);
+ expect(
+ resp.data.data.map((vault) => vault.prefix),
+ 'Should see all vault prefixes'
+ ).to.have.members(['newprefix', updatedPrefix, vaultPrefix2]);
+ });
+
+ it('should delete Azure vaults', async function () {
+ for (const prefix of ['newprefix', vaultPrefix2, updatedPrefix]) {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${prefix}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ }
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-env.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-env.spec.ts
new file mode 100644
index 00000000..c6d98e66
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-env.spec.ts
@@ -0,0 +1,336 @@
+import axios, { AxiosResponse } from 'axios';
+import {
+ expect,
+ getNegative,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('@gke: Vaults: Environment Variables', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/vaults`;
+
+ const vaultPrefix = 'evprefix';
+ const vaultPrefix2 = 'evprefix2';
+ const otherPrefixes = ['newprefix', 'someprefix'];
+ const updatedPrefix = 'evuprefix';
+ const vaultName = 'env';
+ const uuid = '752dcda0-ee05-45df-b973-301f351c1b6a';
+ let vaultId: string;
+
+ const assertBasicDetails = (
+ resp: AxiosResponse,
+ vaultName: string,
+ vaultPrefix: string
+ ) => {
+ expect(resp.data.name, 'Should have correct vault name').equal(vaultName);
+ expect(resp.data.prefix, 'Should have correct vault prefix').equal(
+ vaultPrefix
+ );
+ };
+
+ it('should create a new env vault', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix,
+ description: 'env vault',
+ tags: ['envtag'],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.tags[0], 'Should have correct tags').to.eq('envtag');
+ assertBasicDetails(resp, vaultName, vaultPrefix);
+ expect(resp.data.description, 'Should have correct description').equal(
+ 'env vault'
+ );
+ });
+
+ it('should not create a new vault with same prefix', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: vaultPrefix,
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ `UNIQUE violation detected on '{prefix="evprefix"}'`
+ );
+ });
+
+ it('should not create a new vault with wrong vaultname', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: 'wrong',
+ prefix: vaultPrefix,
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ "schema violation (name: vault 'wrong' is not installed)"
+ );
+ });
+
+ it('should not create a new vault without vaultname', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ prefix: vaultPrefix,
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (name: required field missing)'
+ );
+ });
+
+ it('should not create a new vault without vaultprefix', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ },
+ 'post'
+ );
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (prefix: required field missing)'
+ );
+ });
+
+ it('should not create a new vault with uppercase letter', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: 'H',
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (prefix: invalid value: H)'
+ );
+ });
+
+ it('should not patch the env vault with invalid tags', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix}`,
+ {
+ tags: 'envtag',
+ config: {
+ prefix: 'SECURE_',
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.eq(
+ 'schema violation (tags: expected a set)'
+ );
+ });
+
+ it('should patch the env vault', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${vaultPrefix}`,
+ data: {
+ prefix: updatedPrefix,
+ description: 'my vault',
+ tags: ['env', 'tag'],
+ config: {
+ prefix: 'SECURE_',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Should see 2 tags').to.have.lengthOf(2);
+ expect(resp.data.config.prefix, 'Should see config prefix').to.equal(
+ 'SECURE_'
+ );
+ assertBasicDetails(resp, vaultName, updatedPrefix);
+ });
+
+ it('should retrieve the updated env vault by prefix', async function () {
+ const resp = await axios(`${url}/${updatedPrefix}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.prefix, 'Should see config prefix').to.equal(
+ 'SECURE_'
+ );
+ assertBasicDetails(resp, vaultName, updatedPrefix);
+ });
+
+ it('should create a vault with put request and given valid uuid', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${uuid}`,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ prefix: 'my',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.prefix, 'Should see config prefix').to.equal('my');
+ expect(resp.data.id, 'Should have the given id').to.equal(uuid);
+ assertBasicDetails(resp, vaultName, vaultPrefix2);
+ vaultId = resp.data.id;
+ });
+
+ it('should list all vaults', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should see 2 items in the list').to.have.lengthOf(
+ 2
+ );
+
+ expect(
+ resp.data.data.map((vault) => vault.prefix),
+ 'Should see all vault prefixes'
+ ).to.have.members([updatedPrefix, vaultPrefix2]);
+ });
+
+ it('should delete the env vault by prefix name', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${updatedPrefix}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not retrieve the deleted vault', async function () {
+ const resp = await getNegative(`${url}/${updatedPrefix}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should not create a vault with put request without name', async function () {
+ const resp = await postNegative(
+ `${url}/${uuid}`,
+ {
+ prefix: vaultPrefix2,
+ },
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (name: required field missing)'
+ );
+ });
+
+ it('should not update a vault with put request with wrong config', async function () {
+ const resp = await postNegative(
+ `${url}/${uuid}`,
+ {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ foo: 'bar',
+ },
+ },
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ 'schema violation (config.foo: unknown field)'
+ );
+ });
+
+ it('should update a vault with put request with valid data', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${uuid}`,
+ data: {
+ name: vaultName,
+ prefix: otherPrefixes[0],
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.prefix, 'Should have correct prefix').to.equal(
+ 'newprefix'
+ );
+ });
+
+ it('should retrieve the env vault by id', async function () {
+ const resp = await axios(`${url}/${vaultId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ assertBasicDetails(resp, vaultName, otherPrefixes[0]);
+ });
+
+ it('should delete the env vault by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${vaultId}`,
+ });
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should create a vault with put request and given prefix', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/someprefix`,
+ data: {
+ name: vaultName,
+ prefix: otherPrefixes[1],
+ },
+ });
+ logResponse(resp);
+
+ assertBasicDetails(resp, vaultName, otherPrefixes[1]);
+ expect(resp.data.id, 'Should have autogenerated an id').to.be.string;
+ });
+
+ after(async function () {
+ await axios({
+ method: 'delete',
+ url: `${url}/${otherPrefixes[1]}`,
+ });
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-gcp.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-gcp.spec.ts
new file mode 100644
index 00000000..fdb31e7b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-gcp.spec.ts
@@ -0,0 +1,266 @@
+import axios, { AxiosResponse } from 'axios';
+import {
+ expect,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('@gke: Vaults: GCP', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/vaults`;
+
+ const vaultPrefix = 'gcpprefix';
+ const vaultPrefix2 = 'gcpprefix2';
+ const updatedPrefix = 'updatedgcpprefix';
+ const vaultName = 'gcp';
+ const gcpProjectId = 'gcp-sdet';
+
+ const assertBasicDetails = (
+ resp: AxiosResponse,
+ vaultName: string,
+ vaultPrefix: string
+ ) => {
+ expect(resp.data.name, 'Should have correct vault name').equal(vaultName);
+ expect(resp.data.prefix, 'Should have correct vault prefix').equal(
+ vaultPrefix
+ );
+ expect(resp.data.created_at, 'Should see created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.updated_at, 'Should see updated_at number').to.be.a(
+ 'number'
+ );
+ };
+
+ it('should create a new gcp vault', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix,
+ description: 'gcp vault',
+ config: {
+ project_id: gcpProjectId,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ assertBasicDetails(resp, vaultName, vaultPrefix);
+ expect(resp.data.description, 'Should have correct description').equal(
+ 'gcp vault'
+ );
+ expect(resp.data.config.project_id, 'Should see project_id').to.equal(
+ gcpProjectId
+ );
+ });
+
+ it('should not create gcp vault with same prefix', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: vaultPrefix,
+ config: {
+ project_id: gcpProjectId,
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should have correct error message').to.equal(
+ `UNIQUE violation detected on '{prefix="gcpprefix"}'`
+ );
+ });
+
+ it('should not create gcp vault with wrong config key', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: 'someprefix',
+ config: {
+ project: 'wrong-project-keyname',
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `2 schema violations (config.project: unknown field; config.project_id: required field missing)`
+ );
+ });
+
+ it('should patch the gcp vault', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${vaultPrefix}`,
+ data: {
+ prefix: updatedPrefix,
+ description: 'my patched vault',
+ tags: ['gcp', 'tag', 'more', 'tags'],
+ config: {
+ project_id: 'testid',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Should see 4 tags').to.have.lengthOf(4);
+ expect(
+ resp.data.config.project_id,
+ 'Should see updated project_id'
+ ).to.equal('testid');
+ assertBasicDetails(resp, vaultName, updatedPrefix);
+ });
+
+ it('should not patch the gcp vault with wrong project_id', async function () {
+ const resp = await postNegative(
+ `${url}/${updatedPrefix}`,
+ {
+ prefix: 'someotherprefix',
+ config: {
+ project_id: true,
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.project_id: expected a string)`
+ );
+ });
+
+ it('should not patch the gcp vault with config.prefix', async function () {
+ const resp = await postNegative(
+ `${url}/${updatedPrefix}`,
+ {
+ prefix: 'someotherprefix',
+ config: {
+ project_id: 'test',
+ preifx: 'SECURE_',
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.preifx: unknown field)`
+ );
+ });
+
+ it('should create a vault with put request', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${vaultPrefix2}`,
+ data: {
+ name: vaultName,
+ prefix: 'secondaryprefix',
+ config: {
+ project_id: gcpProjectId,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.prefix, 'Should see config prefix').to.equal(vaultPrefix2);
+ assertBasicDetails(resp, vaultName, vaultPrefix2);
+ });
+
+ it('should not update the vault with put request and wrong config', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix2}`,
+ {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ prefix: 'bar',
+ },
+ },
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ 'schema violations (config.prefix: unknown field; config.project_id: required field missing)'
+ );
+ });
+
+ it('should update the vault with put request and prefix in uri', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/newprefix`,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ project_id: 'test',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.project_id,
+ 'Should see updated config project_id'
+ ).to.equal('test');
+ assertBasicDetails(resp, vaultName, 'newprefix');
+ });
+
+ it('should retrieve the updated gcp vault', async function () {
+ const resp = await axios(`${url}/newprefix`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.project_id,
+ 'Should see config project_id'
+ ).to.equal('test');
+ assertBasicDetails(resp, vaultName, 'newprefix');
+ });
+
+ it('should list all gcp vaults', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.data,
+ 'Should see all 3 items in the list'
+ ).to.have.lengthOf(3);
+ expect(
+ resp.data.data.map((vault) => vault.prefix),
+ 'Should see all vault prefixes'
+ ).to.have.members(['newprefix', updatedPrefix, vaultPrefix2]);
+ });
+
+ it('should delete gcp vaults', async function () {
+ for (const prefix of ['newprefix', vaultPrefix2, updatedPrefix]) {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${prefix}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ }
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-hcv.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-hcv.spec.ts
new file mode 100644
index 00000000..34967e9a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/vaults/vaults-hcv.spec.ts
@@ -0,0 +1,394 @@
+import axios, { AxiosResponse } from 'axios';
+import {
+ expect,
+ postNegative,
+ getBasePath,
+ Environment,
+ logResponse,
+ isGateway,
+} from '@support';
+
+describe('Vaults: Hashicorp', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/vaults`;
+
+ const vaultPrefix = 'hcvprefix';
+ const vaultPrefix2 = 'hcvprefix2';
+ const vaultPrefix3 = 'hcvprefix3';
+ const updatedPrefix = 'updatedhcvprefix';
+ const vaultName = 'hcv';
+ const hcvHost = 'localhost';
+ const hcvTokens = ['s.xtECOru4kQgJYtuK8GPShdCf', 'someothertoken'];
+
+ const assertBasicDetails = (
+ resp: AxiosResponse,
+ vaultName: string,
+ vaultPrefix: string
+ ) => {
+ expect(resp.data.name, 'Should have correct vault name').equal(vaultName);
+ expect(resp.data.prefix, 'Should have correct vault prefix').equal(
+ vaultPrefix
+ );
+ expect(resp.data.created_at, 'Should see created_at number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.updated_at, 'Should see updated_at number').to.be.a(
+ 'number'
+ );
+ };
+
+ it('should create hcv vault with host config only', async function () {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix,
+ description: 'hcv vault',
+ tags: ['hcvtag'],
+ config: {
+ token: hcvTokens[0],
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.tags[0], 'Should have correct tags').to.eq('hcvtag');
+ assertBasicDetails(resp, vaultName, vaultPrefix);
+ expect(resp.data.description, 'Should have correct description').equal(
+ 'hcv vault'
+ );
+ expect(resp.data.config.host, 'Should see config host').to.equal(
+ '127.0.0.1'
+ );
+ expect(resp.data.config.token, 'Should see config token').to.equal(
+ hcvTokens[0]
+ );
+
+ expect(resp.data.config.protocol, 'Should see config protocol').to.equal(
+ 'http'
+ );
+ expect(resp.data.config.port, 'Should see config port').to.equal(8200);
+ expect(resp.data.config.mount, 'Should see config mount').to.equal(
+ 'secret'
+ );
+ expect(resp.data.config.kv, 'Should see config kv').to.equal('v1');
+ });
+
+ it('should not create hcv vault without configuration', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: vaultPrefix,
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `config.token: required field missing`
+ );
+ });
+
+ it('should not create hcv vault with wrong protocol', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: 'otherprefix',
+ config: {
+ token: 'token',
+ protocol: 'tcp',
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.protocol: expected one of: http, https)`
+ );
+ });
+
+ it('should not create hcv vault with wrong kv', async function () {
+ const resp = await postNegative(
+ url,
+ {
+ name: vaultName,
+ prefix: 'otherprefix',
+ config: {
+ token: 'token',
+ kv: 'v12',
+ },
+ },
+ 'post'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.kv: expected one of: v1, v2)`
+ );
+ expect(
+ resp.data.fields.config.kv,
+ 'Should have correct error text in config'
+ ).to.equal('expected one of: v1, v2');
+ });
+
+ it('should not patch the hcv vault with region config', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix}`,
+ {
+ prefix: 'someotherprefix',
+ config: {
+ region: 'us-east-2',
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.region: unknown field)`
+ );
+ });
+
+ it('should not patch the hcv vault with prefix config', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix}`,
+ {
+ prefix: 'someotherprefix',
+ config: {
+ preifx: 'SECURE_',
+ },
+ },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ `schema violation (config.preifx: unknown field)`
+ );
+ });
+
+ it('should patch the hcv vault', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${vaultPrefix}`,
+ data: {
+ prefix: updatedPrefix,
+ description: 'my vault',
+ tags: ['hcv', 'tag', 'more', 'tags'],
+ config: {
+ host: 'google.com',
+ token: hcvTokens[1],
+ kv: 'v2',
+ mount: 'secret2',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.tags, 'Should see 2 tags').to.have.lengthOf(4);
+ expect(resp.data.config.token, 'Should see config token').to.equal(
+ hcvTokens[1]
+ );
+ expect(resp.data.config.port, 'Should see config port').to.equal(8200);
+ expect(resp.data.config.mount, 'Should see config mount').to.equal(
+ 'secret2'
+ );
+ expect(resp.data.config.kv, 'Should see updated config kv').to.equal('v2');
+ assertBasicDetails(resp, vaultName, updatedPrefix);
+ });
+
+ it('should retrieve the hcv vault', async function () {
+ const resp = await axios(`${url}/${updatedPrefix}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.host, 'Should see config host').to.equal(
+ 'google.com'
+ );
+ assertBasicDetails(resp, vaultName, updatedPrefix);
+ });
+
+ it('should not update the vault with put request and wrong config', async function () {
+ const resp = await postNegative(
+ `${url}/${updatedPrefix}`,
+ {
+ name: vaultName,
+ prefix: 'anotherprefix',
+ config: {
+ foo: 'bar',
+ },
+ },
+ 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ 'config.foo: unknown field'
+ );
+ });
+
+ it('should create hcv vault with put request', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${vaultPrefix2}`,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix2,
+ config: {
+ token: 'bar',
+ kv: 'v1',
+ host: hcvHost,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.port, 'Should see config port').to.equal(8200);
+ expect(resp.data.config.mount, 'Should see config mount').to.equal(
+ 'secret'
+ );
+ expect(resp.data.config.kv, 'Should see config kv').to.equal('v1');
+ expect(resp.data.config.host, 'Should see config host').to.equal(hcvHost);
+ expect(resp.data.config.token, 'Should see correct config token').to.equal(
+ 'bar'
+ );
+ });
+
+ it('should not create hcv vault with approle auth method and without role id', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix3}`,
+ {
+ name: vaultName,
+ prefix: vaultPrefix3,
+ config: {
+ auth_method: 'approle',
+ kv: 'v1',
+ host: hcvHost,
+ approle_secret_id: '00000000-0000-0000-0000-000000000000',
+ },
+ }, 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ 'config.approle_role_id: required field missing'
+ );
+ });
+
+ it('should not create hcv vault with approle auth method and role id but without secret id and secret id file', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix3}`,
+ {
+ name: vaultName,
+ prefix: vaultPrefix3,
+ config: {
+ auth_method: 'approle',
+ kv: 'v1',
+ host: hcvHost,
+ approle_role_id: '00000000-0000-0000-0000-000000000000',
+ },
+ }, 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ "must set one of 'approle_secret_id', 'approle_secret_id_file' when 'auth_method' is 'approle'"
+ );
+ });
+
+ it('should not create hcv vault with approle auth method and role id and secret id made of spaces', async function () {
+ const resp = await postNegative(
+ `${url}/${vaultPrefix3}`,
+ {
+ name: vaultName,
+ prefix: vaultPrefix3,
+ config: {
+ auth_method: 'approle',
+ kv: 'v1',
+ host: hcvHost,
+ approle_role_id: ' ',
+ approle_secret_id: ' ',
+ },
+ }, 'put'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ "config.approle_role_id: invalid value:"
+ );
+ });
+
+ it('should create hcv vault with approle auth method', async function () {
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${vaultPrefix3}`,
+ data: {
+ name: vaultName,
+ prefix: vaultPrefix3,
+ config: {
+ auth_method: 'approle',
+ kv: 'v1',
+ host: hcvHost,
+ approle_role_id: '00000000-0000-0000-0000-000000000000',
+ approle_secret_id: '00000000-0000-0000-0000-000000000000',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.port, 'Should see config port').to.equal(8200);
+ expect(resp.data.config.host, 'Should see config host').to.equal(hcvHost);
+ expect(resp.data.config.auth_method, 'Should see correct config auth method').to.equal(
+ 'approle'
+ );
+ expect(resp.data.config.approle_role_id, 'Should see correct config approle role id').to.equal(
+ '00000000-0000-0000-0000-000000000000'
+ );
+ expect(resp.data.config.approle_secret_id, 'Should see correct config approle secret id').to.equal(
+ '00000000-0000-0000-0000-000000000000'
+ );
+ });
+
+ it('should list all hcv vaults', async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data, 'Should all 3 items in the list').to.have.lengthOf(
+ 3
+ );
+ expect(
+ resp.data.data.map((vault) => vault.prefix),
+ 'Should see all vault prefixes'
+ ).to.have.members([updatedPrefix, vaultPrefix2, vaultPrefix3]);
+ });
+
+ it('should delete hcv vaults', async function () {
+ for (const prefix of [vaultPrefix2, updatedPrefix]) {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${prefix}`,
+ });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ }
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/wasm.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/wasm.spec.ts
new file mode 100644
index 00000000..3c854452
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/wasm.spec.ts
@@ -0,0 +1,554 @@
+import {
+ Environment,
+ createFilterChainForRoute,
+ createFilterChainForService,
+ createGatewayService,
+ createRouteForService,
+ deleteFilterChain,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ expect,
+ getBasePath,
+ getNegative,
+ isGateway,
+ logResponse,
+ postNegative,
+ randomString,
+ waitForConfigRebuild
+} from '@support';
+import axios from 'axios';
+
+describe('WASM filter admin API', function () {
+
+ this.timeout(30000);
+ const adminUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ const routePath = '/wasm/httpbin';
+ const serviceName = randomString();
+ const filterChainName = randomString();
+
+ let serviceDetails: any;
+ let routeId: string;
+
+ before(async function () {
+ const serviceReq = await createGatewayService(serviceName);
+ serviceDetails = {
+ id: serviceReq.id,
+ name: serviceReq.name,
+ };
+
+ const routeReq = await createRouteForService(serviceDetails.id, [routePath]);
+ routeId = routeReq.id;
+ });
+
+
+ describe('Create filter', function () {
+
+ it('should return empty result when there are no filter_chains', async function () {
+
+ const resp = await axios({
+ method: 'get',
+ url: `${adminUrl}/filter-chains`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.data.data).to.be.an('array').that.is.empty;
+
+ });
+
+ it('should fail to create filter_chain with JSON schema errors', async function () {
+
+ const resp = await postNegative(
+ `${adminUrl}/services/${serviceName}/filter-chains`,
+ {
+ name: filterChainName,
+ enabled: true,
+ filters: [
+ {
+ config: {
+ add: {
+ headers: [
+ "hello:world",
+ "foo:bar"
+ ]
+ },
+ invalid: "baz"
+ },
+ enabled: true,
+ name: "response_transformer"
+ }
+ ]
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 400').to.be.equal(400);
+ expect(resp.data.name, 'Name should be "schema violation"').to.be.equal('schema violation');
+ expect(resp.data.fields.filters[0].config, 'config should display schema error"').to.be.equal("additional properties forbidden, found invalid");
+
+
+ });
+
+ it('should succeed to create filter-chain with valid schema', async function () {
+
+ const data = {
+ name: filterChainName,
+ enabled: true,
+ filters: [
+ {
+ config: {
+ add: {
+ headers: [
+ 'hello:world',
+ 'foo:bar'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ }
+ ]
+ }
+
+ const resp = await axios({
+ method: 'post',
+ url: `${adminUrl}/services/${serviceName}/filter-chains`,
+ data: data
+ });
+
+ logResponse(resp);
+ expect(resp.status, "Status should be 201").to.be.equal(201);
+
+ await deleteFilterChain(filterChainName);
+ await waitForConfigRebuild();
+ });
+ });
+
+ describe('filter', function () {
+
+ beforeEach(async function () {
+ const data = {
+ name: filterChainName,
+ enabled: true,
+ filters: [
+ {
+ config: {
+ add: {
+ headers: [
+ 'hello:world',
+ 'foo:bar'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ }
+ ]
+ };
+
+ await createFilterChainForService(data, serviceName);
+ await waitForConfigRebuild();
+ });
+
+ it('should add response header', async function () {
+
+ const resp = await axios({
+ method: 'post',
+ url: `${proxyUrl}${routePath}`,
+ data: {
+ foo: "bar"
+ }
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.headers['hello'], "hello header should be set").is.equal("world");
+ expect(resp.headers['foo'], "foo header should be set").is.equal("bar");
+ });
+
+ it('should return the created filter chain', async function () {
+
+ const resp = await axios({
+ method: 'get',
+ url: `${adminUrl}/filter-chains`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.data.data).to.be.an('array').that.is.not.empty;
+ expect(resp.data.data[0].name).to.be.equal(filterChainName);
+
+ });
+
+ it('should patch filter-chains', async function () {
+
+ const data = {
+ name: filterChainName,
+ enabled: true,
+ filters: [
+ {
+ config: {
+ add: {
+ headers: [
+ 'this:isatest'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ }
+ ]
+ }
+
+ let resp = await axios({
+ method: 'patch',
+ url: `${adminUrl}/filter-chains/${filterChainName}`,
+ data: data
+ });
+
+ logResponse(resp);
+ expect(resp.data.filters[0].config.add.headers[0]).to.be.equal('this:isatest');
+
+ await waitForConfigRebuild();
+
+ resp = await axios({
+ method: 'post',
+ url: `${proxyUrl}${routePath}`,
+ data: {
+ foo: "bar"
+ }
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.headers['hello'], "hello header should not be set").to.be.undefined;
+ expect(resp.headers['foo'], "foo header should not be set").to.be.undefined;
+ expect(resp.headers['this'], "this header should be set").is.equal("isatest");
+
+ });
+
+
+ it('should get filter chain by name', async function () {
+
+ const resp = await axios.get(`${adminUrl}/filter-chains/${filterChainName}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.name).to.be.equal(filterChainName);
+
+ });
+
+ it('should not get a filter chain with non-existant chain name', async function () {
+ const resp = await getNegative(`${adminUrl}/filter-chains/thiswontmatch`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').to.be.equal(404);
+ });
+
+ it('should get service by chain name', async function () {
+
+ const resp = await axios.get(`${adminUrl}/filter-chains/${filterChainName}/service`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.name).to.be.equal(serviceName);
+ });
+
+ it('should not get service with non-existant chain name', async function () {
+ const resp = await getNegative(`${adminUrl}/filter-chains/thiswontmatch/service`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').to.be.equal(404);
+ });
+
+ it('should get chains by service name', async function () {
+
+ const resp = await axios.get(`${adminUrl}/services/${serviceName}/filter-chains`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.data).to.be.an('array').that.is.not.empty;
+ expect(resp.data.data[0].name).to.be.equal(filterChainName);
+ });
+
+ it('should not get filter chains with non-existant service name', async function () {
+ const resp = await getNegative(`${adminUrl}/services/thiswontmatch/filter-chains`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').to.be.equal(404);
+ });
+
+ it('should get chain by service name', async function () {
+
+ const resp = await axios.get(`${adminUrl}/services/${serviceName}/filter-chains/${filterChainName}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.name).to.be.equal(filterChainName);
+ });
+
+ it('should not get a filter chain with non-existant service name', async function () {
+ const resp = await getNegative(`${adminUrl}/services/thiswontmatch/filter-chains/${filterChainName}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').to.be.equal(404);
+ });
+
+ it('should delete the created filter chain', async function () {
+
+ let resp = await axios({
+ method: 'delete',
+ url: `${adminUrl}/filter-chains/${filterChainName}`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.be.equal(204);
+
+ await waitForConfigRebuild();
+
+ resp = await axios({
+ method: 'post',
+ url: `${proxyUrl}${routePath}`,
+ data: {
+ foo: "bar"
+ }
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.headers['this'], "this header should not be set").to.be.undefined;
+ });
+
+ afterEach(async function () {
+ await deleteFilterChain(filterChainName);
+ });
+
+ });
+
+ describe('filters on routes', function () {
+
+ beforeEach(async function () {
+ const data = {
+ name: filterChainName,
+ enabled: true,
+ filters: [
+ {
+ config: {
+ add: {
+ headers: [
+ 'hello:world',
+ 'foo:bar'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ }
+ ]
+ };
+
+ await createFilterChainForRoute(data, routeId);
+ await waitForConfigRebuild();
+ });
+
+ it('should get route by chain name', async function () {
+
+ const resp = await axios.get(`${adminUrl}/filter-chains/${filterChainName}/route`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.id).to.be.equal(routeId);
+ });
+
+ it('should not get a route with non-existant chain name', async function () {
+ const resp = await getNegative(`${adminUrl}/filter-chains/thiswontmatch/route`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').to.be.equal(404);
+ });
+
+ it('should get chains by route id', async function () {
+
+ const resp = await axios.get(`${adminUrl}/routes/${routeId}/filter-chains`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.data).to.be.an('array').that.is.not.empty;
+ expect(resp.data.data[0].name).to.be.equal(filterChainName);
+ });
+
+ it('should not get chains by non-existant route id', async function () {
+ const resp = await getNegative(`${adminUrl}/routes/thiswontmatch/filter-chains`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').to.be.equal(404);
+ });
+
+ it('should get chain by route id', async function () {
+ const resp = await axios.get(`${adminUrl}/routes/${routeId}/filter-chains/${filterChainName}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.name).to.be.equal(filterChainName);
+ });
+
+ it('should not get chain by non-existant route id', async function () {
+ const resp = await getNegative(`${adminUrl}/routes/thiswontmatch/filter-chains/${filterChainName}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 404').to.be.equal(404);
+ });
+
+ afterEach(async function () {
+ await deleteFilterChain(filterChainName);
+ });
+
+ });
+
+ describe('enabling / disabling of filters', function () {
+ beforeEach(async function () {
+ const serviceChain = {
+ name: 'servicechain',
+ enabled: true,
+ filters: [
+ {
+ config: {
+ add: {
+ headers: [
+ 'first:service'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ },
+ {
+ config: {
+ add: {
+ headers: [
+ 'second:service'
+ ]
+ }
+ },
+ enabled: false,
+ name: 'response_transformer'
+ },
+ {
+ config: {
+ add: {
+ headers: [
+ 'third:service'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ }
+ ]
+ };
+ const routeChain = {
+ name: 'routechain',
+ enabled: true,
+ filters: [
+ {
+ config: {
+ add: {
+ headers: [
+ 'fourth:route'
+ ]
+ }
+ },
+ enabled: false,
+ name: 'response_transformer'
+ },
+ {
+ config: {
+ add: {
+ headers: [
+ 'fifth:route'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ },
+ {
+ config: {
+ add: {
+ headers: [
+ 'sixth:route'
+ ]
+ }
+ },
+ enabled: true,
+ name: 'response_transformer'
+ }
+ ]
+ };
+
+ await createFilterChainForService(serviceChain, serviceName);
+ await createFilterChainForRoute(routeChain, routeId);
+ await waitForConfigRebuild();
+ });
+
+ it('should get enabled filters only', async function () {
+ const resp = await axios.get(`${adminUrl}/routes/${routeId}/filters/enabled`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.filters, 'Should be an array of size 4').to.be.an("array").ofSize(4);
+ expect(resp.data.filters[0].config.add.headers[0], 'first service-attached filter should be enabled').to.equal("first:service");
+ expect(resp.data.filters[1].config.add.headers[0], 'third service-attached filter should be enabled').to.equal("third:service");
+ expect(resp.data.filters[2].config.add.headers[0], 'fifth route-attached filter should be enabled').to.equal("fifth:route");
+ expect(resp.data.filters[3].config.add.headers[0], 'sixth route-attached filter should be enabled').to.equal("sixth:route");
+ });
+
+ it('should only execute enabled filters', async function () {
+
+ const resp = await axios({
+ method: 'post',
+ url: `${proxyUrl}${routePath}`,
+ data: {
+ foo: "bar"
+ }
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.headers['first'], "first header should be set").is.equal("service");
+ expect(resp.headers['third'], "third header should be set").is.equal("service");
+ expect(resp.headers['fifth'], "fifth header should be set").is.equal("route");
+ expect(resp.headers['sixth'], "sixth header should be set").is.equal("route");
+
+ expect(resp.headers['second'], "second header should not be set").to.be.undefined;
+ expect(resp.headers['fourth'], "fourth header should not be set").to.be.undefined;
+
+ });
+
+ it('should get disabled filters only', async function () {
+ const resp = await axios.get(`${adminUrl}/routes/${routeId}/filters/disabled`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.filters, 'Should be an array of size 2').to.be.an("array").ofSize(2);
+ expect(resp.data.filters[0].config.add.headers[0], 'second service-attached filter should be disabled').to.equal("second:service");
+ expect(resp.data.filters[1].config.add.headers[0], 'fourth route-attached filter should be disabled').to.equal("fourth:route");
+ });
+
+ it('should get all filters on the route', async function () {
+ const resp = await axios.get(`${adminUrl}/routes/${routeId}/filters/all`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.be.equal(200);
+ expect(resp.data.filters, 'Should be an array of size 6').to.be.an("array").ofSize(6);
+ expect(resp.data.filters[0].config.add.headers[0], 'first service-attached filter should be enabled').to.equal("first:service");
+ expect(resp.data.filters[1].config.add.headers[0], 'second service-attached filter should be disabled').to.equal("second:service");
+ expect(resp.data.filters[2].config.add.headers[0], 'third service-attached filter should be enabled').to.equal("third:service");
+ expect(resp.data.filters[3].config.add.headers[0], 'fourth route-attached filter should be disabled').to.equal("fourth:route");
+ expect(resp.data.filters[4].config.add.headers[0], 'fifth route-attached filter should be enabled').to.equal("fifth:route");
+ expect(resp.data.filters[5].config.add.headers[0], 'sixth route-attached filter should be enabled').to.equal("sixth:route");
+ });
+
+ afterEach(async function () {
+ await deleteFilterChain("servicechain");
+ await deleteFilterChain("routechain");
+ });
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceName);
+ });
+
+});
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket-size-limit-plugin.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket-size-limit-plugin.spec.ts
new file mode 100644
index 00000000..2678b2aa
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket-size-limit-plugin.spec.ts
@@ -0,0 +1,140 @@
+import {
+ createGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ expect,
+ randomString,
+ logResponse,
+ getGatewayBasePath,
+ waitForConfigRebuild,
+} from '@support';
+import axios from 'axios';
+import WebSocket from 'promise-ws';
+
+describe('Websocket Size Limit Plugin Tests', function () {
+ const adminApi = getGatewayBasePath('admin');
+
+ ['ws', 'wss'].forEach(protocol => {
+ const echoServer = `${protocol}://websocket-echo-server:${protocol == 'ws' ? 9000 : 9443}/.ws`;
+
+ describe(`Tests with "${protocol}" protocol using ${echoServer}`, function () {
+ const proxyUrl = getGatewayBasePath(`${protocol}Proxy`) + `/.${protocol}`;
+
+ const servicePayload = {
+ name: randomString(),
+ url: echoServer,
+ };
+
+ const routePayload = {
+ name: randomString(),
+ paths: [`/.${protocol}`],
+ protocols: [protocol],
+ };
+
+ let serviceId: string;
+ let routeId: string;
+ let pluginBasePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ const service = await createGatewayService(servicePayload.name, servicePayload);
+ serviceId = service.id;
+
+ const route = await createRouteForService(serviceId, undefined, routePayload);
+ routeId = route.id;
+
+ pluginBasePayload = {
+ name: 'websocket-size-limit',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+
+ await waitForConfigRebuild({ interval: 1000, timeout: 120000 });
+ });
+
+ it('should be able to add websocket size limit plugin', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${adminApi}/plugins`,
+ data: {
+ ...pluginBasePayload,
+ config: {
+ client_max_payload: 42,
+ upstream_max_payload: 1000,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ pluginId = resp.data.id;
+ await waitForConfigRebuild({ interval: 1000 });
+ });
+
+ it('should send message when the size is below the limit', async function () {
+ const websocket = await WebSocket.create(proxyUrl, {
+ rejectUnauthorized: false,
+ });
+
+ const received = new Promise(resolve => websocket.on('message', data => resolve(data)));
+
+ await websocket.send('345');
+ const data = await received;
+ expect(data).to.equal('345');
+ await websocket.close();
+ });
+
+ it('should be able to patch the plugin with client payload size limited', async function () {
+ const client_max_payload = 3;
+ const resp = await axios({
+ method: 'patch',
+ url: `${adminApi}/plugins/${pluginId}`,
+ data: {
+ ...pluginBasePayload,
+ config: {
+ client_max_payload,
+ upstream_max_payload: 400,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ const checkResponse = await axios({
+ url: `${adminApi}/plugins/${pluginId}`,
+ });
+ expect(checkResponse.data.config.client_max_payload).to.equal(client_max_payload);
+
+ await waitForConfigRebuild({ interval: 1000 });
+ });
+
+ it('should not send data when size is limited', async function () {
+ const websocket = await WebSocket.create(proxyUrl, {
+ rejectUnauthorized: false,
+ });
+
+ const closed = new Promise(resolve => websocket.addEventListener('close', data => resolve(data)));
+
+ websocket.on('message', data => console.log('received data', data.length));
+
+ await websocket.send('X'.repeat(11000));
+ await websocket.close();
+ const data: any = await closed;
+
+ expect(data.code).to.equal(1009);
+ expect(data.reason).to.equal('Payload Too Large');
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+ });
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket-validator-plugin.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket-validator-plugin.spec.ts
new file mode 100644
index 00000000..d4f22aa3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket-validator-plugin.spec.ts
@@ -0,0 +1,295 @@
+import {
+ createGatewayService,
+ createRouteForService,
+ expect,
+ logResponse,
+ waitForConfigRebuild,
+ getGatewayBasePath,
+ clearAllKongResources,
+} from '@support';
+import axios from 'axios';
+import WebSocket from 'promise-ws';
+
+describe('Websocket Validator Plugin Tests', function () {
+ ['ws', 'wss'].forEach(protocol => {
+ let pluginId: string;
+
+ describe(`Tests with "${protocol}" protocol`, function () {
+ const echoServer = `${protocol}://websocket-echo-server:${protocol == 'ws' ? 9000 : 9443}/.${protocol}`;
+ const adminApi = getGatewayBasePath('admin');
+ const proxyUrl = getGatewayBasePath(`${protocol}Proxy`) + '/.' + protocol;
+
+ const basePluginPayload = {
+ name: 'websocket-validator',
+ service: {
+ name: 'test-service',
+ },
+ route: {
+ name: 'test-route',
+ },
+ };
+
+ const closeWebsocket = async (ws: any, code, reason: string | null = null) => {
+ const closed: Promise = new Promise(resolve => ws.addEventListener('close', data => resolve(data)));
+ await ws.close();
+ const data = await closed;
+ expect(data.code).to.equal(code);
+ if (reason) {
+ expect(data.reason).to.equal(reason);
+ }
+ };
+
+ before(async function () {
+ await clearAllKongResources();
+
+ await createGatewayService('test-service', {
+ url: echoServer,
+ });
+
+ await createRouteForService('test-service', undefined, {
+ name: 'test-route',
+ paths: [`/.${protocol}`],
+ protocols: [protocol],
+ });
+
+ await waitForConfigRebuild();
+ });
+
+ it('should send message via ws websocket connection before adding plugin', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send('345');
+ expect(await received).to.equal('345');
+ await ws.close();
+ });
+
+ it('should be able add websocket validator plugin', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${adminApi}/plugins`,
+ data: {
+ ...basePluginPayload,
+ config: {
+ client: {
+ text: {
+ schema: '{"type":"string"}',
+ type: 'draft4',
+ },
+ binary: {
+ schema: '{"type":"string"}',
+ type: 'draft4',
+ },
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ await waitForConfigRebuild();
+
+ pluginId = resp.data.id;
+ });
+
+ it('should send string', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send('"something-ws"');
+ expect(await received).to.equal('"something-ws"');
+ await ws.close();
+ });
+
+ it('should send string in binary format', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const encoder = new TextEncoder();
+ const payloadString = '"something-ws-binary"';
+ const buffer = encoder.encode(payloadString);
+ const received: Promise = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send(buffer);
+ const decoder = new TextDecoder();
+ const data = decoder.decode(await received);
+ expect(data).to.equal(payloadString);
+ await ws.close();
+ });
+
+ it('should not send non string data', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ await ws.send(5);
+ await closeWebsocket(ws, 1007, 'Invalid Frame Payload Data');
+ });
+
+ it('should not patch the plugin when schema is not correct ', async function () {
+ const resp = await axios({
+ validateStatus: null,
+ method: 'patch',
+ url: `${adminApi}/plugins/${pluginId}`,
+ data: {
+ config: {
+ client: {
+ text: {
+ schema: '{"type":"strin**"}',
+ type: 'draft4',
+ },
+ binary: {
+ schema: '{"type":"strin**"}',
+ type: 'defat4',
+ },
+ },
+ },
+ },
+ });
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'should have correct error message').to.contain(`schema violation`);
+ });
+
+ it('should be able to patch the plugin with number ', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${adminApi}/plugins/${pluginId}`,
+ data: {
+ config: {
+ client: {
+ text: {
+ schema: '{"type":"number"}',
+ type: 'draft4',
+ },
+ binary: {
+ schema: '{"type":"number"}',
+ type: 'draft4',
+ },
+ },
+ },
+ },
+ validateStatus: null,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ await waitForConfigRebuild();
+ });
+
+ it('should be able to send number', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send(5);
+ expect(await received).to.equal('5');
+ await closeWebsocket(ws, 1005);
+ });
+
+ it('should send number in binary format ', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const b_number = Number(235).toString(2);
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send(b_number);
+ expect(await received).to.equal('11101011');
+ await closeWebsocket(ws, 1005);
+ });
+
+ it('should not send non number data when plugin is configured for number', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ ws.send('test');
+ await closeWebsocket(ws, 1007, 'Invalid Frame Payload Data');
+ });
+
+ it('should be able to patch the plugin with boolean ', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${adminApi}/plugins/${pluginId}`,
+ data: {
+ config: {
+ client: {
+ text: {
+ schema: `{"type":"boolean"}`,
+ type: 'draft4',
+ },
+ binary: {
+ schema: `{"type":"boolean"}`,
+ type: 'draft4',
+ },
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ await waitForConfigRebuild();
+ });
+
+ it('should send boolean', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send('true');
+ expect(await received).to.equal('true');
+ await closeWebsocket(ws, 1005);
+ });
+
+ it('should send boolean in binary format ', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send('true', { binary: true });
+ expect(await received).to.equalBytes(Buffer.from('true'));
+ await closeWebsocket(ws, 1005);
+ });
+
+ it('should not send non boolean data when plugin is configured for boolean', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ ws.send('test_boolean');
+ await closeWebsocket(ws, 1007, 'Invalid Frame Payload Data');
+ });
+
+ it('should be able to patch the plugin with object', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${adminApi}/plugins/${pluginId}`,
+ data: {
+ config: {
+ client: {
+ text: {
+ schema: `{"type":"object"}`,
+ type: 'draft4',
+ },
+ binary: {
+ schema: `{"type":"object"}`,
+ type: 'draft4',
+ },
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ await waitForConfigRebuild();
+ });
+
+ it('should send object', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ const json = JSON.stringify({
+ id: 'client1',
+ });
+ ws.send(json);
+ expect(await received).to.equal(json);
+ await closeWebsocket(ws, 1005);
+ });
+
+ it('should send object in binary format', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const bjson_arr = [123, 34, 107, 101, 121, 34, 58, 34, 118, 97, 108, 117, 101, 34, 125];
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send(bjson_arr);
+ expect(await received).to.equalBytes(Buffer.from('{"key":"value"}'));
+ await closeWebsocket(ws, 1005);
+ });
+
+ it('should not send non object data when plugin is configured for object', async function () {
+ const ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ ws.send(1245);
+ await closeWebsocket(ws, 1007, 'Invalid Frame Payload Data');
+ });
+
+ after(clearAllKongResources);
+ });
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket.spec.ts
new file mode 100644
index 00000000..2862f83b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/admin-api/websocket/websocket.spec.ts
@@ -0,0 +1,216 @@
+import {
+ createGatewayService,
+ createRouteForService,
+ expect,
+ waitForConfigRebuild,
+ postNegative,
+ randomString,
+ getGatewayBasePath,
+ clearAllKongResources,
+} from '@support';
+import WebSocket from 'promise-ws';
+
+describe('Gateway Websocket Tests', function () {
+ ['ws', 'wss'].forEach(protocol => {
+ const echoServer = `websocket-echo-server:${protocol == 'ws' ? 9000 : 9443}/.ws`;
+
+ describe(`websocket ${protocol} related tests`, function () {
+ const proxyUrl = getGatewayBasePath(`${protocol}Proxy`) + `/.${protocol}`;
+
+ let ws: any;
+
+ before(async function () {
+ await createGatewayService('test-service', {
+ url: `${protocol}://${echoServer}`,
+ });
+ await createRouteForService('test-service', undefined, {
+ name: randomString(),
+ paths: ['/.ws'],
+ protocols: ['ws'],
+ });
+ await waitForConfigRebuild();
+ });
+
+ it('should send text message', async function () {
+ ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send('something-ws');
+ expect(await received).to.equal('something-ws');
+ });
+
+ it('should send binary message', async function () {
+ const buffer = Buffer.alloc(1, 210);
+
+ ws = await WebSocket.create(proxyUrl, { rejectUnauthorized: false });
+ const received: Promise = new Promise(resolve => ws.on('message', data => resolve(data)));
+ ws.send(buffer, { binary: true });
+ const data = await received;
+ expect(data.constructor).to.equal(Buffer);
+ expect(data[0]).to.equal(210);
+ });
+
+ it('prefix routes should work', async function () {
+ ws = await WebSocket.create(`${proxyUrl}12345`, {
+ rejectUnauthorized: false,
+ });
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ await ws.send('send ws to prefix routes');
+ expect(await received).to.equal('send ws to prefix routes');
+ });
+
+ afterEach(async function () {
+ await ws.close();
+ });
+
+ after(clearAllKongResources);
+ });
+ });
+
+ describe('service/routes mismatch negative tests', function () {
+ before(async function () {
+ await createGatewayService('ws-service', {
+ url: 'ws://websocket-echo-server:9000',
+ });
+ await createGatewayService('wss-service', {
+ url: 'wss://websocket-echo-server:9443',
+ });
+ await createGatewayService('http-service', {
+ url: 'http://websocket-echo-server:9000',
+ });
+ await createGatewayService('https-service', {
+ url: 'https://websocket-echo-server:9443',
+ });
+
+ await waitForConfigRebuild({ interval: 1000 });
+ });
+
+ it('should not create http route when service is websocket service', async function () {
+ const wsResp = await postNegative(`${getGatewayBasePath('admin')}/services/ws-service/routes`, {
+ name: randomString(),
+ paths: ['/.wshttp'],
+ protocols: ['http'],
+ });
+
+ expect(wsResp.status, 'Status should be 400').to.equal(400);
+
+ expect(wsResp.data.message, 'Should have correct error message').to.contain(
+ 'schema violation (protocols: route/service protocol mismatch)',
+ );
+ });
+
+ it('should not create http route when service is websocket secure service', async function () {
+ const wssResp = await postNegative(`${getGatewayBasePath('admin')}/services/wss-service/routes`, {
+ name: randomString(),
+ paths: ['/.wsshttp'],
+ protocols: ['http'],
+ });
+
+ expect(wssResp.status, 'Status should be 400').to.equal(400);
+
+ expect(wssResp.data.message, 'Should have correct error message').to.contain(
+ 'schema violation (protocols: route/service protocol mismatch)',
+ );
+ });
+
+ it('should not create https route when service is websocket service', async function () {
+ const wsResp = await postNegative(`${getGatewayBasePath('admin')}/services/ws-service/routes`, {
+ name: randomString(),
+ paths: ['/.wshttps'],
+ protocols: ['https'],
+ });
+
+ expect(wsResp.status, 'Status should be 400').to.equal(400);
+
+ expect(wsResp.data.message, 'Should have correct error message').to.contain(
+ 'schema violation (protocols: route/service protocol mismatch)',
+ );
+ });
+
+ it('should not create https route when service is websocket secure service', async function () {
+ const wssResp = await postNegative(`${getGatewayBasePath('admin')}/services/wss-service/routes`, {
+ name: randomString(),
+ paths: ['/.wsshttps'],
+ protocols: ['https'],
+ });
+
+ expect(wssResp.status, 'Status should be 400').to.equal(400);
+
+ expect(wssResp.data.message, 'Should have correct error message').to.contain(
+ 'schema violation (protocols: route/service protocol mismatch)',
+ );
+ });
+
+ it('should not create ws route when service is http', async function () {
+ const resp = await postNegative(`${getGatewayBasePath('admin')}/services/http-service/routes`, {
+ name: randomString(),
+ paths: ['/httptows'],
+ protocols: ['ws'],
+ });
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'schema violation (protocols: route/service protocol mismatch)',
+ );
+ });
+
+ it('should not create wss route when service is https', async function () {
+ const resp = await postNegative(`${getGatewayBasePath('admin')}/services/https-service/routes`, {
+ name: randomString(),
+ paths: ['/httpstowss'],
+ protocols: ['wss'],
+ });
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'schema violation (protocols: route/service protocol mismatch)',
+ );
+ });
+
+ after(clearAllKongResources);
+ });
+
+ describe('http/https service/route can still proxy ws/wss traffic', function () {
+ before(async function () {
+ await createGatewayService('http-service', {
+ url: 'http://websocket-echo-server:9000',
+ });
+ await createRouteForService('http-service', undefined, {
+ name: randomString(),
+ paths: ['/.httpws'],
+ protocols: ['http'],
+ });
+ await createGatewayService('https-service', {
+ url: 'https://websocket-echo-server:9443',
+ });
+ await createRouteForService('https-service', undefined, {
+ name: randomString(),
+ paths: ['/.httpswss'],
+ protocols: ['https'],
+ });
+ await waitForConfigRebuild({ interval: 1000 });
+ });
+
+ it('should route ws traffic when service and route is http', async function () {
+ const ws = await WebSocket.create(getGatewayBasePath('wsProxy') + `/.httpws`);
+
+ await ws.send('ws as http');
+ const received = new Promise(resolve => ws.on('message', data => resolve(data)));
+ expect(await received).to.equal('ws as http');
+ ws.close();
+ });
+
+ it('should route wss traffic when service and route is https', async function () {
+ const wss = await WebSocket.create(getGatewayBasePath('wssProxy') + `/.httpswss`, {
+ rejectUnauthorized: false,
+ });
+ await wss.send('wss as https');
+ const received = new Promise(resolve => wss.on('message', data => resolve(data)));
+ expect(await received).to.equal('wss as https');
+ wss.close();
+ });
+
+ after(clearAllKongResources);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/deck.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/deck.spec.ts
new file mode 100644
index 00000000..6561c2f3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/deck.spec.ts
@@ -0,0 +1,191 @@
+import axios from 'axios';
+import * as fs from 'fs';
+
+import {
+ Environment,
+ expect,
+ createGatewayService,
+ createRouteForService,
+ createConsumer,
+ createBasicAuthCredentialForConsumer,
+ randomString,
+ getBasePath,
+ logResponse,
+ constructDeckCommand,
+ createPlugin,
+ execCustomCommand,
+ getNegative,
+ read_deck_config,
+ isGateway
+} from '@support';
+
+const adminUrl = `${getBasePath({ environment: isGateway() ? Environment.gateway.admin : undefined })}`;
+const deckFileName = 'kong.json';
+
+describe('decK: Sanity Tests', function () {
+ const name = randomString();
+
+ before(async function () {
+ /* create some known entities in the gateway */
+
+ const svc = await createGatewayService(name);
+ const route = await createRouteForService(svc.id, ['/apitest'], {
+ name: name,
+ });
+
+ const consumer = await createConsumer(name);
+ await createBasicAuthCredentialForConsumer(consumer.username, name);
+
+ await createPlugin({
+ name: 'basic-auth',
+ service: {
+ id: svc.id,
+ },
+ route: {
+ id: route.id,
+ },
+ config: {
+ hide_credentials: true,
+ },
+ });
+ });
+
+ it('should do a ping', async function () {
+ const result = execCustomCommand(constructDeckCommand('ping'));
+ expect(result.stderr, 'deck ping error').to.be.undefined;
+ });
+
+ it('should do a dump', async function () {
+ const result = execCustomCommand(
+ constructDeckCommand('dump --format json --yes "-o=kong"')
+ );
+ expect(result.stderr, 'deck ping error').to.be.undefined;
+
+ const conf = read_deck_config(deckFileName);
+
+ let service_matched = false;
+ let route_matched = false;
+
+ /* check if a known service & route is found */
+
+ expect(
+ conf.services && conf.services.length > 0,
+ 'deck dump does not contain any services'
+ ).to.be.true;
+
+ for (const service of conf.services) {
+ if (service.name === name) {
+ service_matched = true;
+
+ if (service.routes.length) {
+ for (const route of service.routes) {
+ if (route.name === name) {
+ route_matched = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ expect(
+ service_matched,
+ 'deck dump did not contain the expected service'
+ ).equals(true);
+ expect(
+ route_matched,
+ 'deck dump did not contain the expected route'
+ ).equals(true);
+
+ /* check if known consumer is found */
+
+ expect(
+ conf.consumers && conf.consumers.length > 0,
+ 'deck dump does not contain any consumers'
+ ).to.be.true;
+
+ let consumer_matched = false;
+ let cred_matched = false;
+
+ for (const consumer of conf.consumers) {
+ if (consumer.username === name) {
+ consumer_matched = true;
+
+ if (consumer.basicauth_credentials.length) {
+ for (const basicAuthCred of consumer.basicauth_credentials) {
+ if (basicAuthCred.username === name) {
+ cred_matched = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ expect(
+ consumer_matched,
+ 'deck dump did not contain the expected consumer'
+ ).equals(true);
+ expect(
+ cred_matched,
+ 'deck dump did not contain the expected credential'
+ ).equals(true);
+ });
+
+ it('should reset the db', async function () {
+ const result = execCustomCommand(constructDeckCommand('reset --force'));
+ expect(result.stderr, 'deck ping error').to.be.undefined;
+
+ /* check if our previously created service is really gone */
+ const resp = await getNegative(`${adminUrl}/services/${name}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404 - no service found').equal(404);
+ });
+
+ it('should sync the db', async function () {
+ const result = execCustomCommand(
+ constructDeckCommand(`sync ./${deckFileName}`)
+ );
+ expect(result.stderr, 'deck ping error').to.be.undefined;
+
+ /* check if our previously created service is really back */
+ const resp = await axios(`${adminUrl}/services/${name}`);
+ logResponse(resp);
+
+ expect(resp.status, `Service ${name} should exist`).equal(200);
+ });
+
+ it('should detect drift', async function () {
+ let result = execCustomCommand(
+ constructDeckCommand(`diff ./${deckFileName} --non-zero-exit-code`)
+ );
+
+ expect(result.stderr, 'deck ping error').to.be.undefined;
+
+ /* introduce drift */
+ const config = read_deck_config(deckFileName);
+ config.services[0].port = 4242;
+
+ fs.writeFileSync(`./${deckFileName}`, JSON.stringify(config));
+
+ result = execCustomCommand(
+ constructDeckCommand(`diff ./${deckFileName} --non-zero-exit-code`)
+ );
+
+ expect(result.stderr, 'deck diff failed').to.be.not.undefined;
+ expect(
+ result.status,
+ 'deck diff did not terminate with right exit code'
+ ).equals(2);
+ });
+
+ after(async function () {
+ // after test run use deck reset to remove all of the created entities from kong and remove deck file
+ execCustomCommand(constructDeckCommand('reset --force'));
+
+ if (fs.existsSync(`./${deckFileName}`)) {
+ execCustomCommand(`rm ./${deckFileName}`);
+ }
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/expressions-router.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/expressions-router.spec.ts
new file mode 100644
index 00000000..34e098b6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/expressions-router.spec.ts
@@ -0,0 +1,305 @@
+import axios from 'axios';
+import {
+ expect,
+ createGatewayService,
+ Environment,
+ getBasePath,
+ logResponse,
+ resetGatewayContainerEnvVariable,
+ waitForConfigRebuild,
+ getRouterFlavor,
+ isGwHybrid,
+ isGateway,
+ getKongContainerName,
+} from '@support';
+
+describe('Expressions Router Tests', function () {
+ const serviceName = 'expressionsService';
+ const simpleExpression = '(http.path == "/first_path") || (http.path == "/second_path")';
+ const simpleExpressionUpdate = encodeURIComponent('(http.path == "/third_path" && http.method == "PUT")');
+ const complexExpression = encodeURIComponent('(http.path == "/only_path" && http.method == "GET" && http.headers.x_required_header == "present")');
+ const languageExpression = encodeURIComponent('(http.path ^= "/prefix") && (http.path =^ "/suffix")');
+
+ const timeout = 10000;
+ const interval = 1000;
+
+ // router flavors
+ const expressions = 'expressions'
+ const traditional = 'traditional_compatible'
+ const gwContainerName = getKongContainerName();
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ let serviceId: string;
+ let routeId: string;
+
+ async function waitForTargetRouterFlavor(flavor, timeout, interval) {
+ while(await getRouterFlavor() !== flavor && timeout > 0) {
+ await new Promise((resolve) => setTimeout(resolve, interval));
+ timeout -= interval;
+ }
+ }
+
+ before(async function () {
+ await resetGatewayContainerEnvVariable({ KONG_ROUTER_FLAVOR: expressions }, gwContainerName);
+ if (isGwHybrid()) {
+ await resetGatewayContainerEnvVariable({ KONG_ROUTER_FLAVOR: expressions }, 'kong-dp1');
+ }
+
+
+ await waitForTargetRouterFlavor(expressions, timeout, interval);
+
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+
+ await waitForConfigRebuild({timeout: 50000});
+ });
+
+ it('should not create route without mandatory fields required by the expression flavor', async function () {
+ const resp = await axios({
+ url: `${url}/services/${serviceId}/routes`,
+ method: 'post',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ validateStatus: null,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should return correct error message').to.equal(
+ "schema violation (must set one of 'methods', 'hosts', 'headers', 'paths', 'snis', 'expression' when 'protocols' is 'https')",
+ );
+ });
+
+ it('should not create route with malformed expression route', async function () {
+ const resp = await axios({
+ url: `${url}/services/${serviceId}/routes`,
+ method: 'post',
+ data: `expression=> axios(`https://${domain}:8443${path}`);;
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').equal(200);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ if(isGwHybrid()) {
+ it('should see the new certificate generated by acme plugin in redis', async function () {
+ await waitForConfigRebuild();
+ await eventually(async () => {
+ const allKeys: any = await getAllKeys();
+
+ expect(allKeys.length, 'Should see the certificate').to.be.gte(3);
+ expect(allKeys, 'Should see domain in redis').to.include.members([`kong_acme:cert_key:${domain}`, `kong_acme:account:${apiUri}:dGVzdEBrb25naHEuY29t`])
+ });
+ });
+ } else {
+ it('should see the new certificate generated by acme plugin in /certificates', async function () {
+ await eventually(async () => {
+ const resp = await axios(`${url.split('plugins')[0]}certificates`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data.length, 'Should see the certificate').to.equal(1);
+ expect(resp.data.data[0].tags[0], 'Should see correct tag in certificate').to.equal('managed-by-acme');
+ expect(resp.data.data[0].snis[0], 'Should see the domain in sni list').to.equal('domain.test');
+ });
+ });
+ }
+
+ // below 2 tests are skipped due to https://konghq.atlassian.net/browse/KAG-4175 bug
+ it.skip('should see the new certificate generated by acme plugin in /acme/certificates', async function () {
+ await eventually(async () => {
+ const resp = await axios(`${url.split('plugins')[0]}acme/certificates`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data.length, 'Should see the certificate').to.equal(1);
+ });
+ });
+
+ it.skip('should get the new certificate generated by acme plugin by host', async function () {
+ await eventually(async () => {
+ const resp = await axios(`${url.split('plugins')[0]}acme/certificates/${domain}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data.length, 'Should see the certificate').to.equal(1);
+ });
+ });
+
+ it('should manually renew the certificate', async function () {
+ const resp = await postNegative(`${url.split('plugins')[0]}acme`, { host: 'test'}, 'patch')
+
+ expect(resp.status, 'Status should be 202').to.equal(202);
+ expect(resp.data.message, 'Should see correct update message').to.equal('Renewal process started successfully');
+ });
+
+ it('should not see error in the logs after manual update of certificates', async function () {
+ await eventually(async () => {
+ const currentLogs = getGatewayContainerLogs(
+ kongContainerName, 5
+ );
+
+ const isLogFound = findRegex('\\[error\\]', currentLogs);
+ expect(
+ isLogFound,
+ 'Should not see error log after manually updating the acme certificates'
+ ).to.be.false;
+
+ let isTargetErrorMessageDetected = findRegex('failed to run timer unix_timestamp=', currentLogs);
+ expect(
+ isTargetErrorMessageDetected,
+ 'Should not see timer-ng error after manually updating the acme certificate'
+ ).to.be.false;
+
+ isTargetErrorMessageDetected = findRegex("attempt to index local 'config' \\(a boolean value\\), context: ngx.timer", currentLogs);
+ expect(
+ isTargetErrorMessageDetected,
+ 'Should not see attempt to index local config error after manually updating the acme certificate'
+ ).to.be.false;
+ });
+ });
+
+ // TODO - enable the below test after hybrid mode certificate creation issue is resolved
+ // if(isGwHybrid()) {
+ // // before the below test, add test to PATCH the plugin storage_config to kong and remove the existing certificate
+ // it('should apply certificate in hybrid mode using api', async function () {
+ // const resp = await postNegative(`${url.split('plugins')[0]}acme`, { host: 'domain.test', test_http_challenge_flow: false });
+ // logResponse(resp);
+
+ // console.log(`${url.split('plugins')[0]}acme`, resp)
+
+ // // expect(resp.status, 'Status should be 400').to.equal(400);
+ // // expect(resp.data.message, 'Should have correct error message').to.contain(
+ // // 'schema violation ("shm" storage can nott be used in Hybrid mode)'
+ // // );
+ // });
+ // }
+
+ it('should delete the ACME plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ // set KONG_LUA_SSL_TRUSTED_CERTIFICATE value back to its deafult 'system'
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_LUA_SSL_TRUSTED_CERTIFICATE: 'system', KONG_KEYRING_ENABLED: `${isKongNative ? 'on' : 'off'}`
+ },
+ kongContainerName
+ );
+ if (isGwHybrid()) {
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_LUA_SSL_TRUSTED_CERTIFICATE: 'system', KONG_KEYRING_ENABLED: `${isKongNative ? 'on' : 'off'}`
+ },
+ 'kong-dp1'
+ );
+ }
+ if(isGwHybrid()) {
+ client.quit();
+ }
+ await clearAllKongResources()
+ });
+});
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/app-dynamics.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/app-dynamics.spec.ts
new file mode 100644
index 00000000..0ba99649
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/app-dynamics.spec.ts
@@ -0,0 +1,201 @@
+import axios from 'axios';
+import {
+ createGatewayService,
+ createRouteForService,
+ Environment,
+ expect,
+ getBasePath,
+ logResponse,
+ waitForConfigRebuild,
+ isGateway,
+ clearAllKongResources,
+ queryAppdynamicsMetrics,
+ randomString,
+ createConsumer,
+ createKeyAuthCredentialsForConsumer,
+ wait,
+ checkGwVars,
+ checkForArm64
+} from '@support'
+
+
+(checkForArm64() ? describe.skip : describe)('Gateway Plugins: AppDynamics', function () {
+ let serviceName = randomString()
+ let consumerName = randomString()
+
+ const consumerServiceName = randomString()
+ const nonScopedConsumerName = randomString()
+ const routeName = 'appD-route'
+ const routePath = '/app-dynamics'
+ const appName = 'SDET'
+
+ let serviceId: string
+ let consumerServiceId: string
+ let routeId: string
+ let consumerId: string
+ let nonScopedConsumerId: string
+ let pluginId: string
+ let url: string
+ let proxyUrl: string
+ let pluginPayload: object
+ let apiKeyScoped: string
+ let apiKeyUnscoped: string
+
+ const sendRequestsAndReturnLastResp = async function (numRequests, key) {
+ let resp
+ for (let i = 0; i < numRequests; i++) {
+ // eslint-disable-next-line no-restricted-syntax
+ await wait(2000)
+ resp = await axios({
+ method: 'get',
+ // if key is not an empty object, add key to the URL
+ url: `${proxyUrl}${routePath}`,
+ headers: key != '' ? {apiKey: key} : {},
+ validateStatus: null,
+ });
+ logResponse(resp)
+
+ expect(resp.status, 'Status should be 200').to.equal(200)
+ }
+ return resp
+ }
+
+ before(async function () {
+ checkGwVars('app_dynamics')
+
+ url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ const service = await createGatewayService(serviceName)
+ serviceId = service.id
+ serviceName = service.name
+ const route = await createRouteForService(serviceId, [routePath], { name: routeName})
+ routeId = route.id
+ const consumerService = await createGatewayService(consumerServiceName)
+ consumerServiceId = consumerService.id
+ const consumer = await createConsumer(consumerName)
+ consumerId = consumer.id
+ consumerName = consumer.username
+ const nonScopedConsumer = await createConsumer(nonScopedConsumerName)
+ nonScopedConsumerId = nonScopedConsumer.id
+
+ const pluginResp = await axios({
+ method: 'post',
+ url: `${url}/services/${consumerServiceId}/plugins`,
+ data: {
+ name: 'key-auth-enc',
+ },
+ });
+ logResponse(pluginResp);
+ expect(pluginResp.status, 'Status should be 201').to.equal(201);
+
+ const keyAuth = await createKeyAuthCredentialsForConsumer(consumerId)
+ apiKeyScoped = keyAuth.key
+ const keyAuthUnscoped = await createKeyAuthCredentialsForConsumer(nonScopedConsumerId)
+ apiKeyUnscoped = keyAuthUnscoped.key
+
+ pluginPayload = {
+ name: 'app-dynamics',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ }
+ });
+
+ it('should create the app-dynamics plugin successfully', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ validateStatus: null,
+ });
+ logResponse(resp)
+ expect(resp.status, 'Status should be 201').to.equal(201)
+ pluginId = resp.data.id
+
+ await waitForConfigRebuild({ proxyReqHeader: { 'apiKey': apiKeyScoped } })
+ });
+
+ it('should send request and see the Singularityheader when AppDynamics plugin is enabled', async function () {
+ let resp;
+
+ // send 150 requests to seed data for the plugin
+ // it appears the Sigularityheader is not always present in the first 5 requests, so try 30 times
+ for (let i = 0; i < 30; i++) {
+ // eslint-disable-next-line no-restricted-syntax
+ resp = await sendRequestsAndReturnLastResp(5, '')
+
+ if (resp.data.headers.Singularityheader) {
+ break
+ }
+ }
+
+ expect(resp.data.headers.Singularityheader, 'Should see the Singularityheader').to.exist
+ });
+
+ // skipping until we can set up mocking
+ // TODO: mock appdynamics response
+ it.skip('should be able to access app-dynamics metrics', async function () {
+ await queryAppdynamicsMetrics(serviceName, appName, 5)
+ });
+
+ it('should scope plugin to consumer', async function() {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${pluginId}`,
+ data: {
+ consumer: {
+ id: consumerId,
+ },
+ service: {
+ id: consumerServiceId,
+ },
+ },
+ })
+ logResponse(resp)
+
+ await waitForConfigRebuild({ proxyReqHeader: { 'apiKey': apiKeyScoped } })
+ })
+
+ // skipping next 3 tests until https://konghq.atlassian.net/browse/KAG-3803 is resolved
+ it.skip('should send requests as scoped consumer and see Singularityheader', async function () {
+ const resp = await sendRequestsAndReturnLastResp(5, apiKeyScoped)
+ expect(resp.data.headers.Singularityheader, 'Should see the Singularityheader').to.exist
+ })
+
+
+ it.skip('should send requests as different consumer and not see Singularityheader', async function () {
+ const resp = await sendRequestsAndReturnLastResp(5, apiKeyUnscoped)
+ logResponse(resp)
+
+ expect(resp.status, 'Status should be 200').to.equal(200)
+ expect(resp.data.headers, 'Should not see the Singularityheader').to.not.have.property('Singularityheader')
+ })
+
+ it.skip('should see data only from the scoped consumer in AppDynamics', async function () {
+ // although 10 total requests were sent under this service, only 5 should appear
+ await queryAppdynamicsMetrics(consumerServiceId, appName, 5)
+ })
+
+ it('should delete the app-dynamics plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/plugins/${pluginId}`,
+ })
+ logResponse(resp)
+
+ expect(resp.status, 'Status should be 204').to.equal(204)
+ })
+
+ after(async function () {
+ await clearAllKongResources()
+ })
+})
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/basic-auth-instance-name.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/basic-auth-instance-name.spec.ts
new file mode 100644
index 00000000..6261273a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/basic-auth-instance-name.spec.ts
@@ -0,0 +1,366 @@
+import {
+ createBasicAuthCredentialForConsumer,
+ createConsumer,
+ createGatewayService,
+ createRouteForService,
+ deleteConsumer,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ isGateway,
+ isGwHybrid,
+ logResponse,
+ postNegative,
+ randomString,
+ wait,
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway Plugins: basic-auth using plugin instance name', function () {
+ const path = '/basic-auth';
+ const serviceName = 'basic-auth-service';
+ const isHybrid = isGwHybrid();
+ const hybridWaitTime = 8000;
+ const waitTime = 5000;
+ const shortWaitTime = 2500;
+ const consumerName = 'iggy';
+ const basicAuthPassword = randomString();
+ const instanceName = 'My-Plugin_720.~';
+ const invalidInstanceName = 'MoMonay$$$!';
+ const patchInstanceNameOnPlugin = 'Plugin-Patch-My-Plugin_720.~';
+ const patchInstanceNameOnService = 'Service-Patch-My-Plugin_720.~';
+ const patchInstanceNameOnRoute = 'Route-Patch-My-Plugin_720.~';
+ const plugin = 'basic-auth';
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ let serviceId: string;
+ let routeId: string;
+ let consumerDetails: any;
+ let consumerId: string;
+ let base64credentials: string;
+
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ const consumer = await createConsumer(consumerName);
+ consumerId = consumer.id;
+ consumerDetails = {
+ id: consumer.id,
+ username: consumer.username,
+ };
+ await wait(isHybrid ? hybridWaitTime : waitTime); // eslint-disable-line no-restricted-syntax
+
+ basePayload = {
+ name: plugin,
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should not enable basic-auth plugin using invalid instance name ', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ instance_name: invalidInstanceName,
+ };
+
+ const resp = await postNegative(`${url}/plugins`, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should indicate invalid values supplied').to.eq(
+ `schema violation (instance_name: invalid value '${invalidInstanceName}': the only accepted ascii characters are alphanumerics or ., -, _, and ~)`
+ );
+ expect(
+ resp.data.fields.instance_name,
+ 'Should indicate which field is in violation'
+ ).to.eq(
+ `invalid value '${invalidInstanceName}': the only accepted ascii characters are alphanumerics or ., -, _, and ~`
+ );
+ expect(resp.data.name, 'Should indicate schema violation').to.eq(
+ 'schema violation'
+ );
+ });
+
+ it('should enable basic-auth plugin using valid instance name', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ instance_name: instanceName,
+ config: { anonymous: consumerId },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.instance_name, 'Default value is True').to.eq(
+ instanceName
+ );
+
+ pluginId = resp.data.id;
+ await wait(isHybrid ? hybridWaitTime : waitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should not enable basic-auth plugin if instance name exists', async function () {
+ const pluginPayload = {
+ name: plugin,
+ instance_name: instanceName,
+ };
+
+ const resp = await postNegative(`${url}/plugins`, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 409').to.equal(409);
+ expect(resp.data.message, 'Should indicate a violation').to.eq(
+ `UNIQUE violation detected on '{instance_name="${instanceName}"}'`
+ );
+ });
+
+ it('should provision new credentials for consumer under-test', async function () {
+ const resp = await createBasicAuthCredentialForConsumer(
+ consumerDetails.id,
+ consumerDetails.username,
+ basicAuthPassword
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ // base64 encode credentials
+ base64credentials = Buffer.from(
+ `${consumerDetails.username}:${basicAuthPassword}`
+ ).toString('base64');
+
+ await wait(isHybrid ? hybridWaitTime : waitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should proxy request using consumer credentials', async function () {
+ const validTokenHeaders = {
+ Authorization: `Basic ${base64credentials}`,
+ };
+ const resp = await getNegative(`${proxyUrl}${path}`, validTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.headers.Authorization,
+ 'Should see credentials in headers'
+ ).to.eq(`Basic ${base64credentials}`);
+ });
+
+ it('should get plugin using instance name', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${url}/plugins/${instanceName}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should equal plugin name (basic-auth) ').to.equal(
+ plugin
+ );
+ expect(
+ resp.data.instance_name,
+ 'Should equal plugin instance name'
+ ).to.equal(instanceName);
+ });
+
+ it('should get plugin using id', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${url}/plugins/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should equal plugin name (basic-auth) ').to.equal(
+ plugin
+ );
+ expect(
+ resp.data.instance_name,
+ `Should equal plugin instance name`
+ ).to.equal(instanceName);
+ });
+
+ it('should not get plugin using incorrect instance name', async function () {
+ const resp = await getNegative(`${url}/plugins/my-plugin`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ });
+
+ it('should patch the plugin by updating the instance name', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${instanceName}`,
+ data: { instance_name: patchInstanceNameOnPlugin },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.instance_name,
+ 'Should equal plugin patch instance name'
+ ).to.eq(patchInstanceNameOnPlugin);
+ await wait(isHybrid ? hybridWaitTime : shortWaitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should get plugin using updated instance name', async function () {
+ const resp = await axios(`${url}/plugins/${patchInstanceNameOnPlugin}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.id, 'Should retain existing plugin id').to.eq(pluginId);
+ expect(resp.data.name, 'Should equal plugin name (basic-auth) ').to.equal(
+ plugin
+ );
+ expect(
+ resp.data.instance_name,
+ `Should equal plugin instance name`
+ ).to.equal(patchInstanceNameOnPlugin);
+ });
+
+ it('should patch the plugin by updating the instance name via the service', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/services/${serviceId}/plugins/${patchInstanceNameOnPlugin}`,
+ data: { instance_name: patchInstanceNameOnService },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.instance_name,
+ 'Should equal plugin patch instance name'
+ ).to.eq(patchInstanceNameOnService);
+ await wait(isHybrid ? hybridWaitTime : shortWaitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should get plugin using updated instance name on service', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${url}/plugins/${patchInstanceNameOnService}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.id, 'Should retain existing plugin id').to.eq(pluginId);
+ expect(resp.data.name, 'Should equal plugin name (basic-auth) ').to.equal(
+ plugin
+ );
+ expect(
+ resp.data.instance_name,
+ `Should equal plugin instance name`
+ ).to.equal(patchInstanceNameOnService);
+ });
+
+ it('should patch plugin by updating the instance name via the route', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/routes/${routeId}/plugins/${patchInstanceNameOnService}`,
+ data: { instance_name: patchInstanceNameOnRoute },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.instance_name,
+ 'Should equal plugin patch instance name'
+ ).to.eq(patchInstanceNameOnRoute);
+ await wait(isHybrid ? hybridWaitTime : shortWaitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should get plugin using updated instance name on route', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${url}/plugins/${patchInstanceNameOnRoute}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.id, 'Should retain existing plugin id').to.eq(pluginId);
+ expect(resp.data.name, 'Should equal plugin name (basic-auth) ').to.equal(
+ plugin
+ );
+ expect(
+ resp.data.instance_name,
+ `Should equal plugin instance name`
+ ).to.equal(patchInstanceNameOnRoute);
+ });
+
+ it('should continue to proxy request after multiple updates', async function () {
+ const validTokenHeaders = {
+ Authorization: `Basic ${base64credentials}`,
+ };
+ const resp = await getNegative(`${proxyUrl}${path}`, validTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should patch the basic-auth plugin enabling the hide credentials flag', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/routes/${routeId}/plugins/${patchInstanceNameOnRoute}`,
+ data: { config: { hide_credentials: true } },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.instance_name,
+ 'Should equal plugin patch instance name'
+ ).to.eq(patchInstanceNameOnRoute);
+ expect(resp.data.config.hide_credentials, 'Should be true').to.be.true;
+ await wait(isHybrid ? hybridWaitTime : shortWaitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should proxy request while hiding credentials from upstream service', async function () {
+ const validTokenHeaders = {
+ Authorization: `Basic ${base64credentials}`,
+ };
+ const resp = await getNegative(`${proxyUrl}${path}`, validTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers).to.not.have.key('Authorization');
+ });
+
+ it('should delete the basic-auth plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/plugins/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteConsumer(consumerId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/cors.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/cors.spec.ts
new file mode 100644
index 00000000..8d798da0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/cors.spec.ts
@@ -0,0 +1,279 @@
+import axios from 'axios';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ deleteGatewayService,
+ randomString,
+ deleteGatewayRoute,
+ createRouteForService,
+ logResponse,
+ waitForConfigRebuild,
+ postNegative,
+ isGateway,
+ eventually,
+} from '@support';
+
+describe('@gke: Gateway Plugins: CORS', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+ const path = `/${randomString()}`;
+ let serviceId: string;
+ let routeId: string;
+ let pluginId: string;
+ let basePayload: any;
+
+ before(async function () {
+ const service = await createGatewayService(randomString());
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ });
+
+ it('should not create the CORS plugin with incorrect config', async function () {
+ basePayload = {
+ name: 'cors',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ const payload = {
+ ...basePayload,
+ config: {
+ origins: ['https://example.com'],
+ methods: ['FAKEMETHOD'],
+ },
+ };
+
+ const resp = await postNegative(url, payload);
+ logResponse(resp);
+ expect(resp.status).to.equal(400);
+ expect(resp.data.message).to.contain(
+ 'expected one of: GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS, TRACE, CONNECT'
+ );
+ });
+
+ it('should create CORS plugin with valid config and default values', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ origins: ['https://example.com'],
+ },
+ };
+
+ const resp = await axios.post(url, payload);
+ logResponse(resp);
+ expect(resp.status).to.equal(201);
+ pluginId = resp.data.id;
+
+ await waitForConfigRebuild();
+ });
+
+ it('should send request with appropriate CORS headers', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ method: 'OPTIONS',
+ headers: {
+ Origin: 'https://example.com',
+ 'access-control-request-method': 'OPTIONS',
+ },
+ });
+ logResponse(resp);
+ expect(resp.status).to.equal(200);
+ expect(resp.headers['access-control-allow-origin']).to.equal(
+ 'https://example.com'
+ );
+ expect(resp.headers).to.not.have.key('access-control-allow-credentials');
+ });
+
+ it('should set and send CORS Access-Control-Allow-Headers and Access-Control-Expose-Headers', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ origins: ['https://example.com'],
+ headers: ['x-public-header', 'x-private-header'],
+ exposed_headers: ['x-public-header'],
+ },
+ };
+
+ const resp = await axios({
+ url: `${url}/${pluginId}`,
+ method: 'PATCH',
+ data: payload,
+ });
+
+ logResponse(resp);
+ expect(resp.status).to.equal(200);
+ expect(resp.data.config.headers)
+ .to.contain('x-public-header')
+ .and.contain('x-private-header');
+ expect(resp.data.config.exposed_headers).to.contain('x-public-header');
+
+ await waitForConfigRebuild();
+
+ const proxyResp = await axios({
+ url: `${proxyUrl}${path}`,
+ method: 'OPTIONS',
+ headers: {
+ Origin: 'https://example.com',
+ 'access-control-request-method': 'OPTIONS',
+ },
+ });
+
+ logResponse(proxyResp);
+ expect(proxyResp.status).to.equal(200);
+ expect(proxyResp.headers['access-control-allow-origin']).to.equal(
+ 'https://example.com'
+ );
+ expect(proxyResp.headers['access-control-allow-headers'])
+ .to.contain('x-public-header')
+ .and.contain('x-private-header');
+
+ const proxyResp2 = await axios({
+ url: `${proxyUrl}${path}`,
+ method: 'GET',
+ headers: {
+ Origin: 'https://example.com',
+ },
+ });
+
+ logResponse(proxyResp2);
+ expect(proxyResp2.status).to.equal(200);
+ expect(proxyResp2.headers['access-control-allow-origin']).to.equal(
+ 'https://example.com'
+ );
+ expect(proxyResp2.headers['access-control-expose-headers']).to.equal(
+ 'x-public-header'
+ );
+ });
+
+ it('should toggle CORS Access-Control-Allow-Credentials header', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ origins: ['https://example.com'],
+ credentials: true,
+ },
+ };
+
+ const resp = await axios({
+ url: `${url}/${pluginId}`,
+ method: 'PATCH',
+ data: payload,
+ });
+
+ logResponse(resp);
+ expect(resp.status).to.equal(200);
+ expect(resp.data.config.credentials).to.equal(true);
+
+ await waitForConfigRebuild();
+
+ const proxyResp = await axios({
+ url: `${proxyUrl}${path}`,
+ method: 'OPTIONS',
+ headers: {
+ Origin: 'https://example.com',
+ 'access-control-request-method': 'OPTIONS',
+ },
+ });
+
+ logResponse(proxyResp);
+ expect(proxyResp.status).to.equal(200);
+ expect(proxyResp.headers['access-control-allow-origin']).to.equal(
+ 'https://example.com'
+ );
+ expect(proxyResp.headers['access-control-allow-credentials']).to.equal(
+ 'true'
+ );
+
+ const payload2 = {
+ ...basePayload,
+ config: {
+ origins: ['https://example.com'],
+ credentials: false,
+ },
+ };
+
+ const resp2 = await axios({
+ url: `${url}/${pluginId}`,
+ method: 'PATCH',
+ data: payload2,
+ });
+
+ logResponse(resp2);
+ expect(resp2.status).to.equal(200);
+ expect(resp2.data.config.credentials).to.equal(false);
+
+ await waitForConfigRebuild();
+
+ const proxyResp2 = await axios({
+ url: `${proxyUrl}${path}`,
+ method: 'OPTIONS',
+ headers: {
+ Origin: 'https://example.com',
+ 'access-control-request-method': 'OPTIONS',
+ },
+ });
+
+ logResponse(proxyResp2);
+ expect(proxyResp2.status).to.equal(200);
+ expect(proxyResp2.headers['access-control-allow-origin']).to.equal(
+ 'https://example.com'
+ );
+ expect(proxyResp2.headers).to.not.have.key(
+ 'access-control-allow-credentials'
+ );
+ });
+
+ it('should set and send CORS access-control-max-age header', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ origins: ['https://example.com'],
+ max_age: 999,
+ },
+ };
+ const resp = await axios({
+ url: `${url}/${pluginId}`,
+ method: 'PATCH',
+ data: payload,
+ });
+
+ logResponse(resp);
+ expect(resp.status).to.equal(200);
+
+ await waitForConfigRebuild();
+
+ await eventually(async () => {
+ const proxyResp = await axios({
+ url: `${proxyUrl}${path}`,
+ method: 'OPTIONS',
+ headers: {
+ Origin: 'https://example.com',
+ 'access-control-request-method': 'OPTIONS',
+ },
+ });
+
+ logResponse(proxyResp);
+ expect(proxyResp.status).to.equal(200);
+ expect(proxyResp.headers['access-control-allow-origin']).to.equal(
+ 'https://example.com'
+ );
+ expect(proxyResp.headers['access-control-max-age']).to.equal('999');
+ });
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/custom-plugins.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/custom-plugins.spec.ts
new file mode 100644
index 00000000..fde1dc65
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/custom-plugins.spec.ts
@@ -0,0 +1,243 @@
+import axios from 'axios';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ deleteGatewayService,
+ randomString,
+ deleteGatewayRoute,
+ createRouteForService,
+ logResponse,
+ waitForConfigRebuild,
+ postNegative,
+ isGateway,
+ getGatewayHost,
+ getControlPlaneDockerImage,
+ isGwNative
+} from '@support';
+
+const isPackageTest = isGwNative();
+const currentDockerImage = getControlPlaneDockerImage();
+
+// skip custom plugin tests for amazonlinux-2 distro and for package tests
+((currentDockerImage?.endsWith('amazonlinux-2') || isPackageTest) ? describe.skip : describe)('@smoke: Gateway Custom Plugins: js-hello, go-hello', function () {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ const pluginsData = {
+ 'js-hello': {
+ shortName: 'js',
+ fullName: 'Javascript',
+ path: '/js-path',
+ serviceId: '',
+ routeId: ''
+ },
+ 'go-hello': {
+ shortName: 'go',
+ fullName: 'Go',
+ path: '/go-path',
+ serviceId: '',
+ routeID: ''
+ }
+ }
+
+ const host = getGatewayHost()
+
+ let message: string;
+
+ ['js-hello', 'go-hello'].forEach(customPlugin => {
+
+ before(async function () {
+ const service = await createGatewayService(randomString());
+ pluginsData[customPlugin].serviceId = service.id;
+ const route = await createRouteForService(pluginsData[customPlugin].serviceId, [pluginsData[customPlugin].path]);
+ pluginsData[customPlugin].routeId = route.id;
+ });
+
+ if(customPlugin === 'js-hello') {
+ // I couldn't figure out how to make the message field mandatory for go plugin
+ it(`should not create ${customPlugin} custom plugin with incorrect config`, async function () {
+ const payload = {
+ name: customPlugin,
+ service: {
+ id: pluginsData[customPlugin].serviceId,
+ },
+ route: {
+ id: pluginsData[customPlugin].routeId,
+ },
+ };
+
+ const resp = await postNegative(url, payload );
+ logResponse(resp);
+ expect(resp.status, 'should see 400 status').to.equal(400);
+ expect(resp.data.message, 'should see correct error message').to.contain(
+ 'schema violation (config.message: required field missing)'
+ );
+ });
+ }
+
+ it(`should not create ${customPlugin} custom plugin for a service without mandatory protocols list`, async function () {
+ message = `hey ${customPlugin}`;
+ const payload = {
+ name: customPlugin,
+ service: {
+ id: pluginsData[customPlugin].serviceId,
+ },
+ config: {
+ message
+ },
+ protocols: []
+ };
+
+ const resp = await postNegative(url, payload);
+ logResponse(resp);
+ expect(resp.status, 'should see 400 status').to.equal(400);
+ });
+
+ // unskip after https://konghq.atlassian.net/browse/KAG-3948 is fixed
+ it.skip(`should not create ${customPlugin} custom plugin globally without mandatory protocols list`, async function () {
+ message = `hey ${customPlugin}`;
+ const payload = {
+ name: customPlugin,
+ config: {
+ message
+ },
+ protocols: []
+ };
+
+ const resp = await postNegative(url, payload);
+ logResponse(resp);
+ expect(resp.status, 'should see 400 status').to.equal(400);
+ });
+
+ it(`should create ${customPlugin} custom plugin with valid config`, async function () {
+ message = `hey ${customPlugin}`;
+
+ const payload = {
+ service: {
+ id: pluginsData[customPlugin].serviceId,
+ },
+ route: {
+ id: pluginsData[customPlugin].routeId,
+ },
+ name: customPlugin,
+ config: {
+ message
+ },
+ };
+
+ const resp = await axios.post(url, payload);
+ expect(resp.status, 'should see 201 status').to.equal(201);
+ expect(resp.data.config.message, 'should see correct plugin configuration').to.equal(message);
+ expect(resp.data.name, 'should see correct plugin name').to.equal(customPlugin);
+ pluginsData[customPlugin].id = resp.data.id;
+
+ await waitForConfigRebuild()
+ });
+
+ it(`should see the ${customPlugin} custom plugin in /plugins list`, async function () {
+ const resp = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ for (const plugin of resp.data.data) {
+ if (plugin.name === customPlugin) {
+ expect(plugin.name, 'should see correct plugin name in the plugins list').to.equal(customPlugin);
+ expect(plugin.config.message, 'should see correct message in the plugins list').to.equal(message)
+ expect(plugin.protocols, 'should see correct protocols list for the plugin').to.have.lengthOf(4)
+ }
+ }
+ });
+
+ it('should send request to upstream and see the custom plugin header', async function () {
+ const headerName = `x-hello-from-${pluginsData[customPlugin].fullName.toLowerCase()}`;
+
+ const resp = await axios({
+ url: `${proxyUrl}${[pluginsData[customPlugin].path]}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ if(customPlugin === 'js-hello') {
+ expect(resp.headers['x-javascript-pid']).to.be.a('string');
+ expect(resp.headers[headerName], `should see correct header for ${customPlugin}`).to.equal(
+ `${pluginsData[customPlugin].fullName} says ${message}`
+ );
+ } else {
+ expect(resp.headers[headerName], `should see correct header for ${customPlugin}`).to.contain(
+ `${pluginsData[customPlugin].fullName} says ${message} to ${host}`
+ );
+ }
+ });
+
+ it(`should not patch the ${customPlugin} custom plugin with empty protocols configuration`, async function () {
+ const resp = await postNegative(`${url}/${pluginsData[customPlugin].id}`, { protocols: []}, 'patch')
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should see correct error mesasge for protocol schema error').to.contain(`protocols: must match the associated route's protocols`)
+ });
+
+ it(`should patch the ${customPlugin} custom plugin message field`, async function () {
+ message = `new updated message for ${customPlugin}`;
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginsData[customPlugin].id}`,
+ data: {
+ config: {
+ message
+ }
+ }
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.message, 'Should see the updated mesasge configuration field').to.equal(message)
+
+ await waitForConfigRebuild()
+ });
+
+ it('should send request to upstream and see the custom plugin header with updated message', async function () {
+ const headerName = `x-hello-from-${pluginsData[customPlugin].fullName.toLowerCase()}`;
+
+ const resp = await axios({
+ url: `${proxyUrl}${[pluginsData[customPlugin].path]}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ if(customPlugin === 'js-hello') {
+ expect(resp.headers['x-javascript-pid']).to.be.a('string');
+ expect(resp.headers[headerName], `should see correct header for ${customPlugin}`).to.equal(
+ `${pluginsData[customPlugin].fullName} says ${message}`
+ );
+ } else {
+ expect(resp.headers[headerName], `should see correct header for ${customPlugin}`).to.contain(
+ `${pluginsData[customPlugin].fullName} says ${message} to ${host}`
+ );
+ }
+ });
+
+ it(`should delete the ${customPlugin} custom plugin`, async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginsData[customPlugin].id}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(pluginsData[customPlugin].routeId);
+ await deleteGatewayService(pluginsData[customPlugin].serviceId);
+ });
+ })
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/custom-rla.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/custom-rla.spec.ts
new file mode 100644
index 00000000..3562312a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/custom-rla.spec.ts
@@ -0,0 +1,195 @@
+import {
+ createGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ isGwHybrid,
+ logResponse,
+ postNegative,
+ isGateway,
+ waitForConfigRebuild,
+ randomString,
+ getControlPlaneDockerImage
+} from '@support';
+import axios from 'axios';
+
+const currentDockerImage = getControlPlaneDockerImage();
+
+// skip tests for amazonlinux-2 distribution
+(currentDockerImage?.endsWith('amazonlinux-2') ? describe.skip : describe)('@smoke: Gateway Custom RLA Plugin Tests', function () {
+ let basePayload: any;
+ let pluginId: string;
+ let serviceId: string;
+ let routeId: string;
+
+ const path = `/${randomString()}`;
+ const isHybrid = isGwHybrid();
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined })}/${path}`;
+
+
+ before(async function () {
+ const service = await createGatewayService('custom-rla-service');
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ basePayload = {
+ name: 'zendesk-rate-limiting-advanced',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ }
+ };
+ });
+
+ it('should not create custom RLA plugin with missing limit and window size', async function () {
+ const resp = await postNegative(url, basePayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ '2 schema violations'
+ );
+ });
+
+ if (isHybrid) {
+ it('should not create custom RLA plugin with strategy cluster in hybrid mode', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ strategy: 'cluster',
+ limit: [52],
+ window_size: [52],
+ sync_rate: 0,
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ "strategy 'cluster' is not supported with Hybrid deployments"
+ );
+ });
+ }
+
+ it('should create custom RLA plugin with correct config', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ limit: [1],
+ window_size: [3600],
+ window_type: 'fixed',
+ strategy: 'local',
+ disable_soft_limit: true,
+ },
+ };
+
+ const resp: any = await axios({
+ method: 'post',
+ url,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.name, 'Should have correct plugin name').to.equal(
+ basePayload.name
+ );
+ pluginId = resp.data.id;
+
+ expect(pluginId, 'Plugin Id should be a string').to.be.string;
+ expect(resp.data.created_at, 'created_at should be a number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.enabled, 'Should have enabled=true').to.be.true;
+ expect(resp.data.config.sync_rate, 'sync_rate should be -1').to.eq(-1);
+ expect(resp.data.config.strategy, 'Should have strategy cluster').to.eq(
+ 'local'
+ );
+ expect(
+ resp.data.config.window_size,
+ 'window_size should be 3600'
+ ).to.be.equalTo([3600]);
+ expect(resp.data.config.limit, 'Should have correct limit').to.be.equalTo([
+ 1,
+ ]);
+ expect(resp.data.config.disable_soft_limit, 'Should have disable_soft_limit=true').to.be.true;
+
+ if (resp.data.config.enforce_consumer_groups) {
+ console.log('Checking also consumer groups');
+ expect(
+ resp.data.config.enforce_consumer_groups,
+ 'Should have consumer groups disabled'
+ ).to.be.false;
+ }
+
+ await waitForConfigRebuild()
+ });
+
+ it('should rate limit on 2nd request when disable_soft_limit is true', async function () {
+ for (let i = 0; i < 2; i++) {
+ const resp: any = await getNegative(proxyUrl);
+ logResponse(resp);
+
+ if (i === 1) {
+ expect(resp.status, 'Status should be 429').to.equal(429);
+ } else {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+ }
+ });
+
+ it('should patch the custom RLA plugin', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ disable_soft_limit: false
+ },
+ };
+
+ const resp = await postNegative(`${url}/${pluginId}`, pluginPayload, 'patch');
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.disable_soft_limit, 'Should have disable_soft_limit=false').to.be.false;
+
+ await waitForConfigRebuild()
+ });
+
+ it('should not rate limit when disable_soft_limit is false', async function () {
+ for (let i = 0; i < 2; i++) {
+ const resp: any = await getNegative(proxyUrl);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+ });
+
+ it('should delete the custom RLA plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/grpc-gateway.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/grpc-gateway.spec.ts
new file mode 100644
index 00000000..4e232be9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/grpc-gateway.spec.ts
@@ -0,0 +1,263 @@
+import axios from 'axios';
+import * as https from 'https';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ wait,
+ updateGatewayService,
+ logResponse,
+ isLocalDatabase,
+ isGateway,
+} from '@support';
+
+describe('Gateway Plugins: gRPC-gateway', function () {
+ const grpcUrl = 'grpc://grpcbin:9000';
+ const grpcSecureUrl = 'grpcs://grpcbin:9001';
+ const protoFile = 'hello-gateway.proto';
+ const protoPath = '/usr/local/kong/protos/';
+ const alternateProtoFile = 'hello-gateway-2.proto';
+ const path = '/grpcgateway';
+ const grpcMapping1 = '/messages/';
+ const grpcMapping2 = '/messages/legacy/';
+ const addPaths = '/more/paths';
+ const grpcResponse = 'hello ';
+ const grpcMessage = 'Kong3.0.x.x.x';
+ const protocols = ['grpc', 'grpcs', 'http', 'https'];
+ const serviceName = 'grpcbin-gateway-service';
+
+ const isLocalDb = isLocalDatabase();
+ const shortWait = 5000;
+ const longWait = 10000;
+
+ let serviceId: string;
+ let routeId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+ const proxyUrlSecure = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxySec,
+ })}`;
+ // This is needed in order to send a secure request to the upstream
+ const agent = new https.Agent({
+ rejectUnauthorized: false,
+ });
+
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ const service = await createGatewayService(serviceName, {
+ url: `${grpcUrl}`,
+ });
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ basePayload = {
+ name: 'grpc-gateway',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should create grpc-gateway plugin', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ proto: `${protoPath}${protoFile}`,
+ },
+ };
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.proto, 'Should have correct path').to.eq(
+ `${protoPath}${protoFile}`
+ );
+ expect(resp.data.protocols, 'Should have correct protocols').to.eql(
+ protocols
+ );
+ pluginId = resp.data.id;
+ await wait(isLocalDb ? 7000 : longWait); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should validate mapping rule 1', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${grpcMapping1}${grpcMessage}`,
+ headers: {
+ 'x-grpc': true,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}`
+ );
+ });
+
+ it('should validate mapping rule 2', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${grpcMapping2}${grpcMessage}`,
+ headers: {
+ 'x-grpc': true,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}`
+ );
+ });
+
+ it('should validate additional pathing', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${grpcMapping2}${grpcMessage}${addPaths}`,
+ headers: {
+ 'x-grpc': true,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}${addPaths}`
+ );
+ });
+
+ //This test is failing due to https://konghq.atlassian.net/browse/FTI-3335
+ xit('should validate bool datatype can be set', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${grpcMapping2}${grpcMessage}`,
+ params: {
+ test: true,
+ },
+ headers: {
+ 'x-grpc': true,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}`
+ );
+ expect(resp.data.test, 'Bool should be set to True').to.be.true;
+ });
+
+ it('should update and set grpc-gateway plugin to select an alternate proto', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ proto: `${protoPath}${alternateProtoFile}`,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.proto, 'Should have correct path').to.eq(
+ `${protoPath}${alternateProtoFile}`
+ );
+ expect(resp.data.protocols, 'Should have correct protocols').to.eql(
+ protocols
+ );
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ await wait(isLocalDb ? shortWait : longWait); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should validate new proto POST bindings', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${grpcMapping1}`,
+ method: 'post',
+ headers: {
+ 'x-grpc': true,
+ },
+ data: {
+ name: `${grpcMessage}`,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}`
+ );
+ });
+
+ it('should validate new proto POST bindings where message is optional', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${grpcMapping1}`,
+ method: 'post',
+ headers: {
+ 'x-grpc': true,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}noname`
+ );
+ });
+
+ it('should patch the service to allow ssl', async function () {
+ const patchService = await updateGatewayService(serviceName, {
+ url: `${grpcSecureUrl}`,
+ });
+
+ expect(patchService.id, 'Service id should match').to.equal(serviceId);
+ });
+
+ it('should validate new proto POST bindings using secure connection', async function () {
+ const resp = await axios({
+ url: `${proxyUrlSecure}${path}${grpcMapping1}`,
+ method: 'post',
+ httpsAgent: agent,
+ headers: {
+ 'x-grpc': true,
+ },
+ data: {
+ name: `${grpcMessage}`,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}`
+ );
+ });
+
+ it('should delete the gRPC-gateway plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/grpc-web.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/grpc-web.spec.ts
new file mode 100644
index 00000000..09a589c1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/grpc-web.spec.ts
@@ -0,0 +1,198 @@
+import axios from 'axios';
+import * as https from 'https';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ waitForConfigRebuild,
+ updateGatewayService,
+ logResponse,
+ eventually,
+ isGateway,
+} from '@support';
+
+describe('Gateway Plugins: gRPC-web', function () {
+ const grpcUrl = 'grpc://grpcbin:9000';
+ const grpcSecureUrl = 'grpcs://grpcbin:9001';
+ const protoFile = 'hello.proto';
+ const protoPath = '/usr/local/kong/protos/';
+ const path = '/grpcweb';
+ const unaryRpc = '/hello.HelloService/SayHello';
+ const serverStreaming = '/hello.HelloService/LotsOfReplies';
+ const grpcResponse = 'hello ';
+ const grpcMessage = 'Kong3.0.x.x.x';
+ const protocols = ['grpc', 'grpcs', 'http', 'https'];
+ const serviceName = 'grpcbin-web-service';
+
+ let serviceId: string;
+ let routeId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+ const proxyUrlSecure = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxySec,
+ })}`;
+ // This is needed in order to send a secure request to the upstream
+ const agent = new https.Agent({
+ rejectUnauthorized: false,
+ });
+
+ let basePayload: any;
+ let pluginId: string;
+
+ //We need to use a gRPC service
+ before(async function () {
+ const service = await createGatewayService(serviceName, {
+ url: `${grpcUrl}`,
+ });
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ basePayload = {
+ name: 'grpc-web',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+
+ await waitForConfigRebuild();
+ });
+
+ it('should create grpc-web plugin', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ proto: `${protoPath}${protoFile}`,
+ pass_stripped_path: true,
+ },
+ };
+
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.proto, 'Should have correct path').to.eq(
+ `${protoPath}${protoFile}`
+ );
+ expect(resp.data.protocols, 'Should have correct protocols').to.eql(
+ protocols
+ );
+ pluginId = resp.data.id;
+ await waitForConfigRebuild();
+ });
+
+ it('should validate unary response with greeting', async function () {
+ await eventually(async () => {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${unaryRpc}`,
+ method: 'post',
+ headers: {
+ 'x-grpc': true,
+ },
+ data: { greeting: `${grpcMessage}`, test: true },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}`
+ );
+ });
+ });
+
+ it('should validate unary response w/o greeting', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${unaryRpc}`,
+ method: 'post',
+ headers: {
+ 'x-grpc': true,
+ },
+ data: { greeting: '', test: true },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}`
+ );
+ });
+
+ it('should validate server streaming response', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}${serverStreaming}`,
+ method: 'post',
+ headers: {
+ 'x-grpc': true,
+ },
+ data: { greeting: `${grpcMessage}`, test: true },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data, 'Should have correct message').to.contain(
+ `${grpcResponse}${grpcMessage}`
+ );
+ // the strategy here is to ensure the stream returns more than a single response
+ expect(
+ resp.data,
+ 'Should have more than one message response'
+ ).length.above(11);
+ });
+
+ it('should patch the service to allow ssl', async function () {
+ const patchService = await updateGatewayService(serviceName, {
+ url: `${grpcSecureUrl}`,
+ });
+
+ expect(patchService.id, 'Service id should match').to.equal(serviceId);
+
+ await waitForConfigRebuild();
+ });
+
+ it('should validate unary response with greeting using secure connection', async function () {
+ const resp = await axios({
+ url: `${proxyUrlSecure}${path}${unaryRpc}`,
+ httpsAgent: agent,
+ method: 'post',
+ headers: {
+ 'x-grpc': true,
+ },
+ data: { greeting: `${grpcMessage}`, test: true },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.reply, 'Should have correct message').to.eq(
+ `${grpcResponse}${grpcMessage}`
+ );
+ });
+
+ it('should delete the gRPC-web plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/http-log.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/http-log.spec.ts
new file mode 100644
index 00000000..27c38eae
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/http-log.spec.ts
@@ -0,0 +1,290 @@
+import axios from 'axios';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ postNegative,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ randomString,
+ logResponse,
+ getHttpLogServerLogs,
+ eventually,
+ waitForConfigRebuild,
+ deleteHttpLogServerLogs,
+ isGateway
+} from '@support';
+
+describe('Gateway Plugins: http-log', function () {
+ const path = `/${randomString()}`;
+
+ let serviceId: string;
+ let routeId: string;
+ let pluginId: string;
+ let basePayload: any;
+ let httpLogServerUrl: string;
+ let serverLogs: any;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ const forbiddenHeaders = ['Host', 'Content-Length', 'Content-Type'];
+ const pluginConfigHeaders = ['PUT', 'PATCH'];
+ const customHeaderName = 'X-Custom-Myheader';
+
+ before(async function () {
+ const service = await createGatewayService(randomString());
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ await deleteHttpLogServerLogs()
+
+ httpLogServerUrl = 'http://http-log-server:9300/logs';
+
+ basePayload = {
+ name: 'http-log',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should not create http-log plugin without config.http_endpoint', async function () {
+ const resp = await postNegative(url, basePayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `schema violation (config.http_endpoint: required field missing)`
+ );
+ });
+
+ it('should not create http-log plugin with both Authorization header and userinfo in URL', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ http_endpoint: 'http://hi:there@myservice.com/path',
+ // must also validate casing
+ headers: { AuthoRIZATion: 'test' },
+ },
+ };
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `specifying both an 'Authorization' header and user info in 'http_endpoint' is not allowed`
+ );
+ });
+
+ forbiddenHeaders.forEach((header) => {
+ it(`should not create http-log plugin with ${header} header`, async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ http_endpoint: httpLogServerUrl,
+ headers: { [header]: 'test' },
+ },
+ };
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `schema violation (config.headers: cannot contain '${header}' header)`
+ );
+ });
+ });
+
+ it('should create http-log plugin with http-log endpoint', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ http_endpoint: httpLogServerUrl,
+ },
+ };
+
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.config.http_endpoint,
+ 'Should have correct http_endpoint'
+ ).to.eq(httpLogServerUrl);
+
+ pluginId = resp.data.id;
+ await waitForConfigRebuild()
+ });
+
+ it('should send http request logs to http_endpoint', async function () {
+ const resp = await axios(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+
+ await eventually(async () => {
+ serverLogs = await getHttpLogServerLogs()
+ expect(
+ serverLogs[0]?.reqMethod,
+ 'Should use POST method to log request data'
+ ).to.eq('POST');
+ });
+ });
+
+ it('should send http request random header details to http_endpoint', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ headers: { test: 'test' },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await eventually(async () => {
+ serverLogs = await getHttpLogServerLogs();
+ // always take the last item of http-log server entries as it represents the last request logs
+ const requestDetails = serverLogs?.pop()?.reqBody
+
+ expect(
+ requestDetails?.request.method,
+ 'Should see correct kong request method in logs'
+ ).to.eq('GET');
+ expect(
+ requestDetails?.request.headers.test,
+ 'Should see correct kong request header in logs'
+ ).to.eq('test');
+ expect(
+ requestDetails?.route.paths[0],
+ 'Should see correct kong request route path in logs'
+ ).to.eq(path);
+ expect(
+ requestDetails?.service.path,
+ 'Should see correct kong request service path in logs'
+ ).to.eq('/anything');
+ });
+ });
+
+ pluginConfigHeaders.forEach((pluginConfigHeader) => {
+ it(`should patch the http-log plugin method to ${pluginConfigHeader}`, async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ method: pluginConfigHeader,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.method,
+ 'Should see correct patched method'
+ ).to.eq(pluginConfigHeader);
+
+ await waitForConfigRebuild()
+ });
+
+ it(`should see request logs in http_endpoint with the new ${pluginConfigHeader} method`, async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await eventually(async () => {
+ serverLogs = await getHttpLogServerLogs();
+
+ expect(
+ serverLogs?.pop()?.reqMethod,
+ `Should use ${pluginConfigHeader} method to log request data`
+ ).to.eq(pluginConfigHeader);
+ });
+ });
+ });
+
+ it('should send resolved custom_fields_by_lua value to http_endpoint', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ custom_fields_by_lua: {
+ kong: `return 'http-log plugin api test'`,
+ },
+ headers: { [customHeaderName]: 'test2' },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(
+ resp.data.config.custom_fields_by_lua.kong,
+ 'Should see correct patched method'
+ ).to.eq(`return 'http-log plugin api test'`);
+
+ await waitForConfigRebuild()
+
+ resp = await axios(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await eventually(async () => {
+ serverLogs = await getHttpLogServerLogs();
+ const requestDetails = serverLogs?.pop()?.reqBody
+
+ expect(
+ requestDetails?.kong,
+ 'Should see return value of custom_fields_by_lua'
+ ).to.eq('http-log plugin api test');
+ });
+ });
+
+ // skipped due to https://konghq.atlassian.net/browse/KAG-503
+ it.skip('should see http-log plugin X-header log in http_endpoint', async function () {
+ serverLogs = await getHttpLogServerLogs();
+ // always take the last item of http-log entries as it represents the last request logs
+ const logHeaders = serverLogs[serverLogs.length - 1]?.reqBody.request.headers;
+
+ expect(
+ logHeaders?.some((headerObject) => {
+ return (
+ headerObject.name === customHeaderName.toLowerCase() &&
+ headerObject.value === 'test2'
+ );
+ }),
+ `Should see the correct plugin custom x header in request logs`
+ ).to.be.true;
+ });
+
+ it('should delete the http-log plugin by id', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteHttpLogServerLogs()
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwe-decrypt-jwk.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwe-decrypt-jwk.spec.ts
new file mode 100644
index 00000000..0e1e2737
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwe-decrypt-jwk.spec.ts
@@ -0,0 +1,199 @@
+import { authDetails } from '@fixtures';
+import {
+ createEncryptedKeysForJweDecryptPlugin,
+ createGatewayService,
+ createKeySetsForJweDecryptPlugin,
+ createRouteForService,
+ deleteEncryptedKeysForJweDecryptPlugin,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ deleteKeySetsForJweDecryptPlugin,
+ Environment,
+ eventually,
+ expect,
+ getBasePath,
+ getNegative,
+ isGateway,
+ logResponse,
+ postNegative,
+ waitForConfigRebuild,
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway Plugins: jwe-decrypt JWK', function () {
+ const jwkPath = '/jwedecryptjwk';
+ const serviceName = 'jwe-decrypt-service';
+ const jwkKeySetsName = 'jwk-key-sets';
+ const invalidTokenHeaders = {
+ Authorization: `${authDetails.jwe['invalid-token']}`,
+ };
+ const validTokenHeaders = {
+ Authorization: authDetails.jwe['valid-token'],
+ };
+
+ let serviceId: string;
+ let jwkRouteId: string;
+ let jwkKeySetsId: string;
+ let keysId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ let basePayload: any;
+ let pluginId: string;
+
+ const jwkKeys = {
+ name: 'jwk_key',
+ set: { name: jwkKeySetsName },
+ jwk: authDetails.jwe.jwk,
+ kid: '42',
+ };
+
+ before(async function () {
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const routeJwk = await createRouteForService(serviceId, [jwkPath]);
+ jwkRouteId = routeJwk.id;
+ const jwkKeySets = await createKeySetsForJweDecryptPlugin(jwkKeySetsName);
+ jwkKeySetsId = jwkKeySets.id;
+ await createEncryptedKeysForJweDecryptPlugin(jwkKeys);
+
+ basePayload = {
+ name: 'jwe-decrypt',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: jwkRouteId,
+ },
+ };
+ });
+
+ it('JWK: should not create jwe-decrypt plugin when config.key_sets is not supplied', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {},
+ };
+ const resp = await postNegative(`${url}/plugins`, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should indicate schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.config.key_sets,
+ 'Should indicate key-sets is a required field'
+ ).to.equal('required field missing');
+ });
+
+ it('JWK: should enable jwt-decrypt plugin with valid jwk config', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: { key_sets: [jwkKeySetsName] },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.key_sets[0], 'Should list key-sets').to.equal(
+ jwkKeySetsName
+ );
+
+ pluginId = resp.data.id;
+
+ await waitForConfigRebuild();
+ });
+
+ it('JWK: should not proxy request without a token', async function () {
+ await eventually(async () => {
+ const resp = await getNegative(`${proxyUrl}${jwkPath}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ expect(resp.data.message, 'Should indicate token missing').to.equal(
+ 'could not find token'
+ );
+ });
+ });
+
+ it('JWK: should not proxy request with invalid token', async function () {
+ console.log(invalidTokenHeaders, validTokenHeaders);
+ const resp = await getNegative(
+ `${proxyUrl}${jwkPath}`,
+ invalidTokenHeaders
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+
+ expect(
+ resp.data.message,
+ 'Should indicate token cannot be decrypted'
+ ).to.equal('failed to decrypt token');
+ });
+
+ //skipped until https://konghq.atlassian.net/browse/KAG-390 is fixed
+ xit('JWK: should proxy request with valid token', async function () {
+ const resp = await getNegative(`${proxyUrl}${jwkPath}`, validTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+
+ it('JWK: should patch jwe-decrypt plugin to disable auth and allow requests', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${pluginId}`,
+ data: {
+ config: {
+ strict: false,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ });
+
+ it('JWK: should proxy request without supplying a token', async function () {
+ await eventually(async () => {
+ const resp = await axios({
+ url: `${proxyUrl}${jwkPath}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+ });
+
+ it('should delete the jwe-decrypt plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/plugins/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(jwkRouteId);
+ await deleteGatewayService(serviceId);
+ await deleteEncryptedKeysForJweDecryptPlugin(keysId);
+ await deleteKeySetsForJweDecryptPlugin(jwkKeySetsId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwe-decrypt-pem.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwe-decrypt-pem.spec.ts
new file mode 100644
index 00000000..88ac1b5a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwe-decrypt-pem.spec.ts
@@ -0,0 +1,196 @@
+import { authDetails } from '@fixtures';
+import {
+ createEncryptedKeysForJweDecryptPlugin,
+ createGatewayService,
+ createKeySetsForJweDecryptPlugin,
+ createRouteForService,
+ deleteEncryptedKeysForJweDecryptPlugin,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ deleteKeySetsForJweDecryptPlugin,
+ Environment,
+ eventually,
+ expect,
+ getBasePath,
+ getNegative,
+ isGateway,
+ logResponse,
+ postNegative,
+ waitForConfigRebuild,
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway Plugins: jwe-decrypt PEM', function () {
+ const pemPath = '/jwedecryptpem';
+ const serviceName = 'jwe-decrypt-service';
+ const pemKeySetsName = 'pem-key-sets';
+ const invalidTokenHeaders = {
+ Authorization: `${authDetails.jwe['invalid-token']}`,
+ };
+ const validTokenHeaders = {
+ Authorization: authDetails.jwe['valid-token'],
+ };
+
+ let serviceId: string;
+ let pemRouteId: string;
+ let pemKeySetsId: string;
+ let keysId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ let basePayload: any;
+ let pluginId: string;
+ const pemKeys = {
+ name: 'pem_key',
+ set: { name: pemKeySetsName },
+ pem: {
+ public_key: authDetails.jwe.public,
+ private_key: authDetails.jwe.private,
+ },
+ kid: '42',
+ };
+
+ before(async function () {
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const routePem = await createRouteForService(serviceId, [pemPath]);
+ pemRouteId = routePem.id;
+ const pemKeySets = await createKeySetsForJweDecryptPlugin(pemKeySetsName);
+ pemKeySetsId = pemKeySets.id;
+ await createEncryptedKeysForJweDecryptPlugin(pemKeys);
+
+ basePayload = {
+ name: 'jwe-decrypt',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: pemRouteId,
+ },
+ };
+ });
+
+ it('PEM: should not create jwe-decrypt plugin when config.key_sets is not supplied', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {},
+ };
+ const resp = await postNegative(`${url}/plugins`, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should indicate schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.config.key_sets,
+ 'Should indicate key-sets is a required field'
+ ).to.equal('required field missing');
+ });
+
+ it('PEM: should enable jwt-decrypt plugin with valid pem config on a service', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: { key_sets: [pemKeySetsName] },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.key_sets[0], 'Should list key-sets').to.equal(
+ pemKeySetsName
+ );
+
+ pluginId = resp.data.id;
+ await waitForConfigRebuild();
+ });
+
+ it('PEM: should proxy request with valid token', async function () {
+ const resp = await getNegative(`${proxyUrl}${pemPath}`, validTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('PEM: should not proxy request without a token', async function () {
+ await eventually(async () => {
+ const resp = await getNegative(`${proxyUrl}${pemPath}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+ expect(resp.data.message, 'Should indicate token missing').to.equal(
+ 'could not find token'
+ );
+ });
+ });
+
+ it('PEM: should not proxy request with invalid token', async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${pemPath}`,
+ invalidTokenHeaders
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 403').to.equal(403);
+
+ expect(
+ resp.data.message,
+ 'Should indicate token cannot be decrypted'
+ ).to.equal('failed to decrypt token');
+ });
+
+ it('PEM: should patch jwe-decrypt plugin to disable auth and allow requests', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${pluginId}`,
+ data: {
+ config: {
+ strict: false,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ await waitForConfigRebuild();
+ });
+
+ it('PEM: should proxy request without supplying a token', async function () {
+ await eventually(async () => {
+ const resp = await axios({
+ url: `${proxyUrl}${pemPath}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+ });
+
+ it('PEM: should delete the jwe-decrypt plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/plugins/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(pemRouteId);
+ await deleteGatewayService(serviceId);
+ await deleteEncryptedKeysForJweDecryptPlugin(keysId);
+ await deleteKeySetsForJweDecryptPlugin(pemKeySetsId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwt-signer.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwt-signer.spec.ts
new file mode 100644
index 00000000..89363c1a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwt-signer.spec.ts
@@ -0,0 +1,653 @@
+import axios from 'axios';
+import { authDetails } from '@fixtures';
+import { jwtDecode } from 'jwt-decode';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ isLocalDatabase,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ wait,
+ logResponse,
+ createConsumer,
+ deleteConsumer,
+ getNegative,
+ retryRequest,
+ isGateway,
+ waitForConfigRebuild,
+ postNegative,
+ deletePlugin,
+} from '@support';
+
+describe('Gateway Plugins: jwt-signer', function () {
+ this.timeout(30000);
+ const path = '/jwtsigner';
+ const serviceName = 'jwt-signer-service';
+ const consumerName = 'demo';
+ const upstreamConsumerHeaderName = 'X-Consumer-Username';
+ const upstreamConsumerHeaderId = 'X-Consumer-Id';
+ const islocalDb = isLocalDatabase();
+ const waitTime = 5000;
+ const expiredTokenHeaders = {
+ Authorization: `bearer ${authDetails.expired_token}`,
+ };
+ const validTokenHeaders = {
+ Authorization: `bearer ${authDetails.valid_token}`,
+ };
+
+ const channelValidTokenHeaders = {
+ Authorization: `bearer ${authDetails.valid_token}`,
+ 'Channel-Authorization': `bearer ${authDetails.valid_token}`
+ };
+
+ const testClaims = [
+ {
+ description: 'should be able to add extra claims in the resigned token',
+ config: {
+ add_claims: {
+ a: 'test_a',
+ b: '[true, false]',
+ c: '{ ccc: 123 }',
+ d: '12345.99',
+ }
+ },
+ expectedToken: {
+ a: 'test_a',
+ b: [ true, false ],
+ iss: 'kong',
+ c: '{ ccc: 123 }',
+ d: 12345.99,
+ iat: 1665610129,
+ original_iss: 'demo',
+ username: 'demo'
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should be able to add access token claims in the resigned token',
+ config: {
+ add_access_token_claims: {
+ e: 'test_e',
+ f: '[true, false]',
+ g: '{ ggg: 123 }',
+ d: '12345.99999999999999999999999999999',
+ }
+ },
+ expectedToken: {
+ f: [ true, false ],
+ c: '{ ccc: 123 }',
+ e: 'test_e',
+ username: 'demo',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ a: 'test_a',
+ b: [ true, false ],
+ iat: 1665610129,
+ d: 12346,
+ original_iss: 'demo'
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should be able to set access token claims in the resigned token',
+ config: {
+ set_access_token_claims: {
+ i: 'test_i',
+ j: '[true, false]',
+ k: '{ kkk: 123 }',
+ l: '12345.999998888',
+ }
+ },
+ expectedToken: {
+ a: 'test_a',
+ k: '{ kkk: 123 }',
+ c: '{ ccc: 123 }',
+ b: [ true, false ],
+ e: 'test_e',
+ f: [ true, false ],
+ username: 'demo',
+ i: 'test_i',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ d: 12346,
+ j: [ true, false ],
+ iat: 1665610129,
+ original_iss: 'demo',
+ l: 12345.999998888
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should be able to update claims with add_claims in the resigned token',
+ config: {
+ add_claims: {
+ a: 'test_a_new'
+ }
+ },
+ expectedToken: {
+ k: '{ kkk: 123 }',
+ a: 'test_a_new',
+ e: 'test_e',
+ f: [ true, false ],
+ username: 'demo',
+ i: 'test_i',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ d: 12346,
+ j: [ true, false ],
+ iat: 1665610129,
+ original_iss: 'demo',
+ l: 12345.999998888
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should be able to update claims with set_claims in the resigned token',
+ config: {
+ set_claims: {
+ a: 'test_a_new_v2'
+ }
+ },
+ expectedToken: {
+ k: '{ kkk: 123 }',
+ a: 'test_a_new_v2',
+ e: 'test_e',
+ f: [ true, false ],
+ username: 'demo',
+ i: 'test_i',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ d: 12346,
+ j: [ true, false ],
+ iat: 1665610129,
+ l: 12345.999998888,
+ original_iss: 'demo'
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should not be able to remove claims if added with add_claims or add_access_token_claims in the resigned token',
+ config: {
+ remove_access_token_claims: ['a', 'e']
+ },
+ expectedToken: {
+ k: '{ kkk: 123 }',
+ a: 'test_a_new_v2',
+ e: 'test_e',
+ f: [ true, false ],
+ username: 'demo',
+ i: 'test_i',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ d: 12346,
+ j: [ true, false ],
+ iat: 1665610129,
+ l: 12345.999998888,
+ original_iss: 'demo'
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should be able to remove claims if it was pre-existed from the resigned token',
+ config: {
+ remove_access_token_claims: ['username']
+ },
+ expectedToken: {
+ k: '{ kkk: 123 }',
+ a: 'test_a_new_v2',
+ e: 'test_e',
+ f: [ true, false ],
+ i: 'test_i',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ d: 12346,
+ j: [ true, false ],
+ iat: 1665610129,
+ original_iss: 'demo',
+ l: 12345.999998888
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should set_claims takes higher priority than add_claims',
+ config: {
+ add_claims: {
+ b: "{\"key\": 123}"
+ },
+ set_claims: {
+ b: "[\"str1\",\"str2\", true, 1234.009999]"
+ }
+ },
+ expectedToken: {
+ b: [ 'str1', 'str2', true, 1234.009999 ],
+ e: 'test_e',
+ f: [ true, false ],
+ d: 12346,
+ i: 'test_i',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ original_iss: 'demo',
+ j: [ true, false ],
+ iat: 1665610129,
+ k: '{ kkk: 123 }',
+ l: 12345.999998888
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should set_access_token_claims takes higher priority than set_claims',
+ config: {
+ add_claims: {
+ b: "{\"key\": 123}"
+ },
+ set_claims: {
+ b: "[\"str1\",\"str2\", true, 1234.009999]"
+ }
+ },
+ expectedToken: {
+ b: [ 'str1', 'str2', true, 1234.009999 ],
+ e: 'test_e',
+ f: [ true, false ],
+ d: 12346,
+ i: 'test_i',
+ g: '{ ggg: 123 }',
+ iss: 'kong',
+ original_iss: 'demo',
+ j: [ true, false ],
+ iat: 1665610129,
+ k: '{ kkk: 123 }',
+ l: 12345.999998888
+ },
+ headerKey: 'Abc',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should see original access token when set original_access_token_upstream_header',
+ config: {
+ original_access_token_upstream_header: "og_token"
+ },
+ expectedToken: { iat: 1665610129, iss: 'demo', username: 'demo' },
+ headerKey: 'Og-Token',
+ tokenHeaders: validTokenHeaders
+ },
+ {
+ description: 'should be able to add channel token',
+ config: {
+ channel_token_upstream_header: "DEF",
+ verify_channel_token_signature: false,
+ channel_token_request_header: "Channel-Authorization"
+ },
+ expectedToken: {
+ iss: 'kong',
+ b: [ 'str1', 'str2', true, 1234.009999 ],
+ iat: 1665610129,
+ original_iss: 'demo',
+ username: 'demo'
+ },
+ headerKey: 'Def',
+ tokenHeaders: channelValidTokenHeaders
+ },
+ {
+ description: 'should be able to add_claims and set_claims for channel token',
+ config: {
+ add_claims: {
+ c: "{ \"ccc\": 12309.9999 }"
+ },
+ set_claims: {
+ c: "{ \"ccc\": true }"
+ }
+ },
+ expectedToken: {
+ iss: 'kong',
+ c: { ccc: true },
+ iat: 1665610129,
+ original_iss: 'demo',
+ username: 'demo'
+ },
+ headerKey: 'Def',
+ tokenHeaders: channelValidTokenHeaders
+ },
+ {
+ description: 'should set_channel_token_claims take the higher priority for channel token',
+ config: {
+ add_claims: {
+ d: "{ \"ddd\": 12309.9999 }"
+ },
+ set_claims: {
+ d: "{ \"ddd\": true }"
+ },
+ add_access_token_claims: {
+ d: "1234"
+ },
+ set_channel_token_claims: {
+ d: "{\"dd\": 1234, \"ddd\": true}"
+ }
+ },
+ expectedToken: {
+ iss: 'kong',
+ d: { dd: 1234, ddd: true },
+ iat: 1665610129,
+ original_iss: 'demo',
+ username: 'demo'
+ },
+ headerKey: 'Def',
+ tokenHeaders: channelValidTokenHeaders
+ },
+ {
+ description: 'should set original channel token',
+ config: {
+ original_channel_token_upstream_header: "og-channel-token"
+ },
+ expectedToken: { iat: 1665610129, iss: 'demo', username: 'demo' },
+ headerKey: 'Og-Channel-Token',
+ tokenHeaders: channelValidTokenHeaders
+ }
+ ]
+
+
+ let serviceId: string;
+ let routeId: string;
+ let consumerId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ // json server running in the same docker network as kong
+ const jwksUri = `http://json-server:3000/db`;
+
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ const consumer = await createConsumer(consumerName);
+ consumerId = consumer.id;
+
+ basePayload = {
+ name: 'jwt-signer',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should create jwt-signer plugin with default parameters when config payload is not supplied by the user', async function () {
+ basePayload = {
+ name: 'jwt-signer',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ const pluginPayload = {
+ ...basePayload,
+ config: {},
+ };
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.verify_access_token_expiry, 'Expiry should be true')
+ .to.true;
+ expect(
+ resp.data.config.access_token_issuer,
+ 'Issuer should be kong'
+ ).to.equal('kong');
+ expect(
+ resp.data.config.access_token_signing_algorithm,
+ 'Algorithm should be RS256'
+ ).to.equal('RS256');
+ expect(resp.data.config.access_token_jwks_uri, 'Jwks uri should be null').to
+ .be.null;
+ expect(resp.data.config.access_token_optional, 'Should be false').to.false;
+ expect(resp.data.config.channel_token_optional, 'Should be false').to.false;
+
+ pluginId = resp.data.id;
+ });
+
+ it('should not proxy request when JWKS_URI parameter is null preventing token validation', async function () {
+ const req = () => getNegative(`${proxyUrl}${path}`);
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Message should be unauthorized').to.equal(
+ 'Unauthorized'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should patch jwt-signer plugin to set JWKS_URI allowing token validation', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ channel_token_optional: true,
+ access_token_jwks_uri: jwksUri,
+ access_token_consumer_claim: ['username'],
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.access_token_jwks_uri,
+ 'Jwks uri should not be null'
+ ).to.equal(jwksUri);
+
+ await waitForConfigRebuild()
+ });
+
+ it('should proxy request with a valid token', async function () {
+ const resp = await axios({
+ headers: validTokenHeaders,
+ url: `${proxyUrl}${path}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.headers[upstreamConsumerHeaderName],
+ 'Should see consumer username in upstream request'
+ ).to.equal(consumerName);
+ expect(
+ resp.data.headers[upstreamConsumerHeaderId],
+ 'Should see consumer id in upstream request'
+ ).to.equal(consumerId);
+ });
+
+ it('should not proxy request with a expired token ', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`, expiredTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should be Unauthorized').to.equal(
+ 'Unauthorized'
+ );
+ });
+
+ it('should patch jwt-signer plugin to disable auth and allow requests', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ channel_token_optional: true,
+ access_token_optional: true,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.access_token_optional, 'Should be true').to.be.true;
+ expect(resp.data.config.channel_token_optional, 'Should be true').to.be
+ .true;
+ await wait(waitTime + (islocalDb ? 0 : waitTime)); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should proxy request without token', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions, 20000, 4000);
+ });
+
+ it('should delete the jwt-signer plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should not create a jwt-signer plugin when original channel token and original access token headers are same', async function () {
+ basePayload = {
+ name: 'jwt-signer',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ original_channel_token_upstream_header: 'AAA',
+ verify_access_token_signature: false,
+ verify_channel_token_signature: false,
+ channel_token_optional: true,
+ original_access_token_upstream_header: 'AAA'
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload, 'post');
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should indicate schema violation').to.equal(
+ 'schema violation'
+ );
+ logResponse(resp);
+
+ });
+
+ it('should recreate jwt-signer plugin with the access token upstream header', async function () {
+ basePayload = {
+ name: 'jwt-signer',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ access_token_upstream_header: 'ABC',
+ verify_access_token_signature: false,
+ verify_channel_token_signature: false,
+ channel_token_optional: true,
+ },
+ };
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ pluginId = resp.data.id;
+ await waitForConfigRebuild();
+ });
+
+ it('should proxy request with a valid token and verify the resigned token', async function () {
+ const expectedToken = {
+ username: 'demo',
+ iat: 1665610129,
+ iss: 'kong',
+ original_iss: 'demo'
+ }
+ const resp = await axios({
+ headers: validTokenHeaders,
+ url: `${proxyUrl}${path}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ const decodedToken = jwtDecode(resp.data.headers['Abc']);
+ expect(
+ decodedToken, 'should see the decoded token in the response',
+ ).to.deep.equal(expectedToken);
+ });
+
+ testClaims.forEach(({ description, config, expectedToken, headerKey, tokenHeaders }) => {
+ it(description, async function() {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: config
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild()
+
+ resp = await axios({
+ headers: tokenHeaders,
+ url: `${proxyUrl}${path}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ const decodedToken = jwtDecode(resp.data.headers[headerKey]);
+ expect(
+ decodedToken, 'should see the decoded token in the response',
+ ).to.deep.equal(expectedToken);
+ });
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteConsumer(consumerId);
+ await deletePlugin(pluginId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwt.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwt.spec.ts
new file mode 100644
index 00000000..a9019e9c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/jwt.spec.ts
@@ -0,0 +1,329 @@
+import axios from 'axios';
+import * as jwt from 'jsonwebtoken';
+import { authDetails } from '@fixtures';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ logResponse,
+ createConsumer,
+ deleteConsumer,
+ getNegative,
+ retryRequest,
+ isGateway,
+ waitForConfigRebuild,
+} from '@support';
+
+describe('Gateway Plugins: jwt', function () {
+ const path = '/jwt';
+ const serviceName = 'jwt-service';
+ const algorithms = ['ES512', 'PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512', 'ES256', 'ES384'];
+
+ let serviceId: string;
+ let routeId: string;
+ let consumerId: string;
+ let iss: string;
+ let secret: string;
+ let basePayload: any;
+ let pluginId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+ const consumerUrl = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/consumers`;
+
+
+ before(async function () {
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ const consumerName = 'jwt-consumer';
+ const consumer = await createConsumer(consumerName);
+ consumerId = consumer.id;
+
+ basePayload = {
+ name: 'jwt',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should create jwt plugin with default parameters', async function () {
+ basePayload = {
+ name: 'jwt',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ const pluginPayload = {
+ ...basePayload,
+ config: {},
+ };
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ pluginId = resp.data.id;
+ });
+
+ it('should not proxy request without token', async function () {
+ await waitForConfigRebuild();
+
+ const req = () => getNegative(`${proxyUrl}${path}`);
+
+ const assertions = (resp) => {
+ logResponse(resp);
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Message should be unauthorized').to.equal(
+ 'Unauthorized'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not proxy request with iss claim absent from token', async function () {
+ let resp;
+
+ resp = await axios({
+ method: 'post',
+ url: `${consumerUrl}/${consumerId}/jwt`,
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'}
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ iss = resp.data.key;
+ secret = resp.data.secret;
+
+ const claims = {
+ 'name': `${consumerId}`,
+ };
+ const jwt_token = jwt.sign(claims, secret, { algorithm: 'HS256' });
+
+ resp = await getNegative(`${proxyUrl}${path}`, {
+ 'Authorization': `Bearer ${jwt_token}`
+ });
+
+ logResponse(resp);
+ expect(resp.data.message, 'Message should be unauthorized').to.equal(
+ "No mandatory 'iss' in claims"
+ );
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ });
+
+ it('should create HS256 JWT credential and proxy with the HS256 token', async function () {
+ let resp;
+
+ resp = await axios({
+ method: 'post',
+ url: `${consumerUrl}/${consumerId}/jwt`,
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'}
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ iss = resp.data.key;
+ secret = resp.data.secret;
+
+ const claims = {
+ 'iss': iss,
+ };
+ const jwt_token = jwt.sign(claims, secret, { algorithm: 'HS256' });
+
+ await waitForConfigRebuild();
+
+ resp = await axios({
+ headers: {'Authorization': `Bearer ${jwt_token}`},
+ url: `${proxyUrl}${path}`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should be able to add exp to the claims', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ name: 'jwt',
+ config: {
+ claims_to_verify: ['exp'],
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+ expect(
+ patchResp.data.config.claims_to_verify[0],
+ 'Should add exp to the claims'
+ ).to.equal('exp');
+ await waitForConfigRebuild();
+ });
+
+ it('should not proxy traffic if exp is absent from token', async function () {
+ const claims = {
+ 'iss': `${iss}`,
+ };
+ const jwt_token = jwt.sign(claims, secret, { algorithm: 'HS256' });
+
+ const resp = await getNegative(`${proxyUrl}${path}`, {
+ 'Authorization': `Bearer ${jwt_token}`
+ });
+
+ logResponse(resp);
+ expect(resp.data.exp, 'exp should be a number').to.equal(
+ 'must be a number'
+ );
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ });
+
+ it('should not proxy traffic if invalid exp is present in token', async function () {
+ const now = new Date();
+ // Calculate the date 30 days away from now
+ const daysToSubstract = 30;
+ const futureDate = new Date(now.getTime() - daysToSubstract * 24 * 60 * 60 * 1000);
+
+ const claims = {
+ 'iss': `${iss}`,
+ 'exp': Math.floor(futureDate.getTime() / 1000)
+ };
+ const jwt_token = jwt.sign(claims, secret, { algorithm: 'HS256' });
+
+ const resp = await getNegative(`${proxyUrl}${path}`, {
+ 'Authorization': `Bearer ${jwt_token}`
+ });
+
+ logResponse(resp);
+ expect(resp.data.exp, 'Token is expired').to.equal('token expired');
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ });
+
+ it('should proxy traffic if valid exp is present in token', async function () {
+ const now = new Date();
+ // Calculate the date 30 days from now
+ const daysToAdd = 30;
+ const futureDate = new Date(now.getTime() + daysToAdd * 24 * 60 * 60 * 1000);
+
+ const claims = {
+ 'iss': `${iss}`,
+ 'exp': Math.floor(futureDate.getTime() / 1000)
+ };
+
+ const jwt_token = jwt.sign(claims, secret, { algorithm: 'HS256' });
+
+ const resp = await axios({
+ headers: {'Authorization': `Bearer ${jwt_token}`},
+ url: `${proxyUrl}${path}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await deleteConsumer(consumerId);
+ });
+
+ it('should be able to remove exp from the claims', async function () {
+ const patchResp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ name: 'jwt',
+ config: {
+ claims_to_verify: [],
+ },
+ },
+ });
+ logResponse(patchResp);
+
+ expect(patchResp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+ });
+
+ algorithms.forEach((algorithm) => {
+ it(`should proxy traffic with ${algorithm} algorithm`, async function () {
+ const publicKey = authDetails.jwt[`${algorithm}`]['public_key']
+ const privateKey = authDetails.jwt[`${algorithm}`]['private_key']
+
+ // create a consumer
+ const consumerName = `jwt-consumer-${algorithm}`;
+ const consumer = await createConsumer(consumerName);
+ consumerId = consumer.id;
+
+ // add the public key to consumer with the algorithm
+ let resp = await axios({
+ method: 'post',
+ url: `${consumerUrl}/${consumerId}/jwt`,
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'},
+ data: {
+ algorithm: algorithm,
+ rsa_public_key: publicKey
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ iss = resp.data.key;
+ const claims = {
+ 'iss': iss,
+ };
+ const jwt_token = jwt.sign(claims, privateKey, { algorithm: algorithm });
+
+ await waitForConfigRebuild();
+
+ resp = await axios({
+ headers: {'Authorization': `Bearer ${jwt_token}`},
+ url: `${proxyUrl}${path}`,
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ await deleteConsumer(consumerId);
+ });
+ });
+
+ it('should delete the jwt plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/key-auth-enc.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/key-auth-enc.spec.ts
new file mode 100644
index 00000000..7ff46df5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/key-auth-enc.spec.ts
@@ -0,0 +1,247 @@
+import {
+ createConsumer,
+ createGatewayService,
+ createRouteForService,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ logResponse,
+ wait,
+ waitForConfigRebuild,
+ retryRequest,
+ clearAllKongResources,
+ isGateway,
+ isLocalDatabase,
+ isGwHybrid,
+ eventually
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway Plugins: key-auth-enc', function () {
+ const path = '/key-auth-enc';
+ const serviceName = 'key-auth-enc-service';
+ const waitTime = 5000;
+ const consumerName = 'ted';
+ const key = 'apiKey';
+ // add extra delay when database is remote and gateway in hybrid mode
+ const keyTtl = isGwHybrid() && !isLocalDatabase() ? 35 : 10;
+ const tagAndTtlPayload = { tags: ['tag1'], ttl: keyTtl };
+ const plugin = 'key-auth-enc';
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+ const inValidTokenHeaders = {
+ apiKey: 'ZnBckx2rSLCccbnCKRp3BEqzYbyRYTAX',
+ };
+
+ let serviceId: string;
+ let routeId: string;
+ let keyId: string;
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ await clearAllKongResources();
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ await createConsumer(consumerName);
+
+ basePayload = {
+ name: plugin,
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+
+ await waitForConfigRebuild();
+ });
+
+ it('key-auth-enc: should proxy request without supplying apiKey', async function () {
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+ const req = () => getNegative(`${proxyUrl}${path}`);
+ await retryRequest(req, assertions);
+ });
+
+ it('key-auth-enc: should enable key-auth-enc plugin with apiKey in header', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: { key_names: ['apiKey'] },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.key_in_header, 'Default value is True').to.be.true;
+ expect(resp.data.config.key_in_body, 'Default value is False').to.be.false;
+ expect(resp.data.config.key_in_query, 'Default value is True').to.be.true;
+ expect(resp.data.enabled, 'Should be true').to.be.true;
+ expect(resp.data.config.key_names[0], 'Header key is apiKey').to.contain(
+ key
+ );
+
+ pluginId = resp.data.id;
+ await waitForConfigRebuild();
+ });
+
+ // This test case captures:
+ // https://konghq.atlassian.net/browse/FTI-4084
+ // https://konghq.atlassian.net/browse/FTI-4066#icft=FTI-4066
+ it('key-auth-enc: should create key and add tag using consumer under-test', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/consumers/${consumerName}/${plugin}`,
+ data: tagAndTtlPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.tags, 'Should contain tags').to.contain('tag1');
+ expect(resp.data.ttl, 'Should contain ttl value').to.be.a('number');
+
+ keyId = resp.data.key;
+ if (!process.env.GKE) {
+ await waitForConfigRebuild(); // add dynamic wait for test stability and skip for GKE
+ }
+ });
+
+ it('key-auth-enc: should not proxy request without supplying apiKey', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate no api key found').to.equal(
+ 'No API key found in request'
+ );
+ });
+
+ it('key-auth-enc: should not proxy request with invalid apiKey', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`, inValidTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate invalid credentials').to.equal(
+ 'Unauthorized'
+ );
+ });
+
+ it('key-auth-enc: should proxy request with apiKey in header', async function () {
+ const validTokenHeaders = {
+ apiKey: keyId,
+ };
+
+ const req = () => getNegative(`${proxyUrl}${path}`, validTokenHeaders);
+
+ const assertions = (resp) => {
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions);
+
+ });
+
+ it('key-auth-enc: should proxy request with apiKey in query param', async function () {
+ const queryUrl = `${proxyUrl}${path}?apiKey=${keyId}`;
+
+ const req = () =>
+ axios({
+ method: 'get',
+ url: `${queryUrl}`,
+ });
+
+ const assertions = (resp) => {
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions);
+
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ // This test case captures:
+ // https://konghq.atlassian.net/browse/FTI-4512
+ it('key-auth-enc: should not proxy request with apiKey in header after ttl expiration', async function () {
+ const validTokenHeaders = {
+ apiKey: keyId,
+ };
+ await eventually(async () => {
+ const resp = await getNegative(`${proxyUrl}${path}`, validTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate invalid credentials').to.equal(
+ 'Unauthorized'
+ );
+ });
+ });
+
+ // This test case captures:
+ // https://konghq.atlassian.net/browse/FTI-4512
+ it('key-auth-enc: should not proxy request with apiKey in query param after ttl expiration', async function () {
+ const queryUrl = `${proxyUrl}${path}?apiKey=${keyId}`;
+ await eventually(async () => {
+ const resp = await getNegative(`${queryUrl}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate invalid credentials').to.equal(
+ 'Unauthorized'
+ );
+ });
+ });
+
+ it('key-auth-enc: should patch key-auth-enc plugin to disable auth and allow requests', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${pluginId}`,
+ data: {
+ enabled: false,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.enabled, 'Should be false').to.be.false;
+ await waitForConfigRebuild();
+ });
+
+ it('key-auth-enc: should proxy request without supplying apiKey after disabling plugin', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('key-auth-enc: should delete the key-auth-enc plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/plugins/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await clearAllKongResources();
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/key-auth.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/key-auth.spec.ts
new file mode 100644
index 00000000..54492d4e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/key-auth.spec.ts
@@ -0,0 +1,257 @@
+import {
+ createConsumer,
+ createGatewayService,
+ createRouteForService,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ logResponse,
+ clearAllKongResources,
+ isGateway,
+ isKoko,
+ waitForConfigRebuild,
+ retryRequest,
+ eventually,
+ isLocalDatabase,
+ isGwHybrid
+} from '@support';
+import axios from 'axios';
+
+describe('@smoke @koko: Gateway Plugins: key-auth', function () {
+ const path = '/key-auth';
+ const serviceName = 'key-auth-service';
+ const consumerName = 'bill';
+ const key = 'api_key';
+ // add extra delay when database is remote and gateway in hybrid mode
+ const keyTtl = isGwHybrid() && !isLocalDatabase() ? 40 : 10;
+ const keyAuthPayload = { tags: ['tag2'], ttl: keyTtl };
+ const keyAuthPayloadKonnect = { tags: ['tag2']};
+ const plugin = 'key-auth';
+
+ const inValidTokenHeaders = {
+ api_key: 'ZnBckx2rSLCccbnCKRp3BEqzYbyRYTAX',
+ };
+
+ let url: string
+ let proxyUrl: string
+ let serviceId: string;
+ let routeId: string;
+ let keyId: string;
+ let basePayload: any;
+ let pluginId: string;
+ let consumerId: string
+
+ before(async function () {
+ url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ await clearAllKongResources();
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ const consumer = await createConsumer(consumerName);
+ consumerId = consumer.id
+ await waitForConfigRebuild()
+
+ basePayload = {
+ name: plugin,
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should proxy request without supplying apiKey', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should enable key-auth plugin with apiKey in header', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: { key_names: [key] },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.key_in_header, 'Default value is True').to.be.true;
+ expect(resp.data.config.key_in_body, 'Default value is False').to.be.false;
+ expect(resp.data.config.key_in_query, 'Default value is True').to.be.true;
+ expect(resp.data.enabled, 'Should be true').to.be.true;
+ expect(resp.data.config.key_names[0], 'Header key is apiKey').to.contain(
+ key
+ );
+
+ pluginId = resp.data.id;
+ await waitForConfigRebuild()
+ });
+
+ it('should create key and add tag using consumer under-test', async function () {
+ // *** KOKO DOES NOT ALLOW SETTING TTL VALUE AND USING CONSUMER NAME ***
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/consumers/${isKoko() ? consumerId : consumerName}/${plugin}`,
+ data: isKoko() ? keyAuthPayloadKonnect : keyAuthPayload,
+ });
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.tags, 'Should contain tags').to.contain('tag2');
+ if(isGateway()) {
+ expect(resp.data.ttl, 'Should contain ttl value').to.be.a('number');
+ }
+
+ keyId = resp.data.key;
+ await waitForConfigRebuild()
+ });
+
+ it('should not proxy request without supplying apiKey', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate no api key found').to.equal(
+ 'No API key found in request'
+ );
+ });
+
+ it('should not proxy request with invalid apiKey', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`, inValidTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate invalid credentials').to.equal(
+ 'Unauthorized'
+ );
+ });
+
+ it('should proxy request with apiKey in header', async function () {
+ const validTokenHeaders = {
+ api_key: keyId,
+ };
+
+ const req = () => getNegative(`${proxyUrl}${path}`, validTokenHeaders);
+
+ const assertions = (resp) => {
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions);
+
+ });
+
+ it('should proxy request with apiKey in query param', async function () {
+ const queryUrl = `${proxyUrl}${path}?api_key=${keyId}`;
+
+ const req = () =>
+ axios({
+ method: 'get',
+ url: `${queryUrl}`,
+ });
+
+ const assertions = (resp) => {
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions);
+
+ });
+
+ if(isGateway()) {
+ // This test case captures:
+ // https://konghq.atlassian.net/browse/FTI-4512
+ it('should not proxy request with apiKey in header after ttl expiration', async function () {
+ await waitForConfigRebuild()
+
+ const validTokenHeaders = {
+ api_key: keyId,
+ };
+
+ await eventually(async () => {
+ const resp = await getNegative(`${proxyUrl}${path}`, validTokenHeaders);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate invalid credentials').to.equal(
+ 'Unauthorized'
+ );
+ });
+ });
+
+ // This test case captures:
+ // https://konghq.atlassian.net/browse/FTI-4512
+ it('should not proxy request with apiKey in query param after ttl expiration', async function () {
+ const queryUrl = `${proxyUrl}${path}?api_key=${keyId}`;
+
+ await eventually(async () => {
+ const resp = await getNegative(`${queryUrl}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate invalid credentials').to.equal(
+ 'Unauthorized'
+ );
+ });
+ });
+ }
+
+ it('should update key-auth plugin to disable auth and allow requests', async function () {
+ // *** KOKO DOES NOT PATCH PLUGIN ***
+ const resp = await axios({
+ method: isKoko() ? 'put' : 'patch',
+ url: `${url}/plugins/${pluginId}`,
+ data: {
+ name: 'key-auth',
+ enabled: false,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.enabled, 'Should be false').to.be.false;
+ });
+
+ it('should proxy request without supplying apiKey after disabling plugin', async function () {
+ const req = () => getNegative(`${proxyUrl}${path}`);
+
+ const assertionsSuccess = (resp) => {
+ logResponse(resp)
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+
+ await retryRequest(req, assertionsSuccess)
+ });
+
+ it('should delete the key-auth plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/plugins/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await clearAllKongResources()
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/mocking.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/mocking.spec.ts
new file mode 100644
index 00000000..d30e17c3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/mocking.spec.ts
@@ -0,0 +1,346 @@
+import { spaceApi } from '@fixtures';
+import {
+ createGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ isLocalDatabase,
+ isGwHybrid,
+ logResponse,
+ postNegative,
+ wait,
+ isGateway,
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Mocking Plugin Tests', function () {
+ this.timeout(25000);
+ const isHybrid = isGwHybrid();
+ const isLocalDb = isLocalDatabase();
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ });
+ const longWait = 8000;
+ const shortWait = isHybrid ? 3000 : 1000;
+
+ const validSpec = JSON.stringify(spaceApi);
+ const statusCodeList = [200, 500, 404];
+ const minDelay = 0.1;
+ const maxDelay = 3;
+ const exactDelay = 2000;
+ let pluginId: string;
+ let serviceId: string;
+ let planetRouteId: string;
+ let randomRouteId: string;
+ let moonRouteId: string;
+
+ let basePayload;
+
+ before(async function () {
+ const service = await createGatewayService('MockingService');
+ serviceId = service.id;
+ const planetRoute = await createRouteForService(serviceId, ['/planets']);
+ planetRouteId = planetRoute.id;
+ const randomRoute = await createRouteForService(serviceId, [
+ '/planets/random',
+ ]);
+ randomRouteId = randomRoute.id;
+ const moonRoute = await createRouteForService(serviceId, ['/moons']);
+ moonRouteId = moonRoute.id;
+
+ basePayload = {
+ name: 'mocking',
+ service: {
+ id: serviceId,
+ },
+ };
+ });
+
+ it('should not create mocking plugin without config', async function () {
+ const resp = await postNegative(url, basePayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ "at least one of these fields must be non-empty: 'config.api_specification_filename', 'config.api_specification'"
+ );
+ });
+
+ it('should not create mocking plugin with invalid config', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ api_specification: 'not valid',
+ },
+ };
+
+ const resp = await postNegative(url, payload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ "api specification is neither valid json ('Expected value but found invalid token at character 1') nor valid yaml ('not valid')"
+ );
+ });
+
+ // Skipping until resolved: https://konghq.atlassian.net/browse/FT-3568
+ it.skip('should not create mocking plugin with max_delay_time less than min_delay_time', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ api_specification: validSpec,
+ random_delay: true,
+ min_delay_time: 5,
+ max_delay_time: 1,
+ },
+ };
+
+ const resp = await postNegative(url, payload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'max_delay_time must be greater than min_delay_time'
+ );
+ });
+
+ it('should create mocking plugin with valid config', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ api_specification: validSpec,
+ },
+ };
+
+ const resp = await axios({
+ url: url,
+ method: 'post',
+ data: payload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.config.api_specification,
+ 'Response should contain API Specification'
+ ).to.contain('"title":"Space API"');
+
+ pluginId = resp.data.id;
+
+ // give plugin time to take effect
+ await wait(longWait); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should return expected mock response', async function () {
+ await wait(isLocalDb ? shortWait : longWait); // eslint-disable-line no-restricted-syntax
+ const resp = await axios(`${proxyUrl}/planets`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.planets, 'Should include expected data').to.have.length(8);
+ expect(resp.headers, 'Should include mocking header').to.include({
+ 'x-kong-mocking-plugin': 'true',
+ });
+ });
+
+ it('should return expected examples from random_examples set', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ random_examples: true,
+ },
+ };
+
+ const update = await axios({
+ url: `${url}/${pluginId}`,
+ method: 'patch',
+ data: payload,
+ });
+
+ expect(update.status, 'Status should be 200 when updating plugin').to.equal(
+ 200
+ );
+
+ await wait(shortWait); // eslint-disable-line no-restricted-syntax
+
+ // send 5 requests and ensure each time we get an expected example
+ for (let i = 0; i < 5; i++) {
+ const resp = await axios(`${proxyUrl}/planets/random`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Should be one of expected examples').to.be.oneOf([
+ 'Earth',
+ 'Neptune',
+ 'Venus',
+ ]);
+ }
+ });
+
+ it('should mock request with expected delay between min and max_delay_time', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ api_specification: validSpec,
+ random_delay: true,
+ min_delay_time: minDelay,
+ max_delay_time: maxDelay,
+ },
+ };
+
+ const update = await axios({
+ url: `${url}/${pluginId}`,
+ method: 'patch',
+ data: payload,
+ });
+
+ expect(update.status, 'Status should be 200 when updating plugin').to.equal(
+ 200
+ );
+
+ await wait(longWait); // eslint-disable-line no-restricted-syntax
+
+ // send 5 requests and ensure each has expected delay
+ for (let i = 0; i < 5; i++) {
+ const resp = await axios(`${proxyUrl}/planets/random`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ // convert latency from ms to s and check if within 10% of expected delays
+ expect(parseInt(resp.headers['x-kong-response-latency']) / 1000)
+ .to.be.lessThan(maxDelay + maxDelay * 0.1)
+ .and.greaterThan(minDelay - minDelay * 0.1);
+ }
+ });
+
+ // below tests address FT-3178 - https://konghq.atlassian.net/browse/FT-3178
+ it('should mock response with given delay when using delay behavioral header', async function () {
+ // send 5 requests and ensure each has expected delay
+ // min and max delay should be ignored
+ for (let i = 0; i < 5; i++) {
+ const resp = await axios({
+ url: `${proxyUrl}/planets/random`,
+ headers: { 'X-Kong-Mocking-Delay': exactDelay },
+ });
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ // check if latency is within 10% of expected delay
+ expect(parseInt(resp.headers['x-kong-response-latency']))
+ .to.be.lessThan(exactDelay + exactDelay * 0.1)
+ .and.greaterThan(exactDelay - exactDelay * 0.1);
+ }
+ });
+
+ it('should mock response with specific example when using example id behavioral header', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}/planets/random`,
+ headers: { 'X-Kong-Mocking-Example-Id': 'earth' },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.name, 'Data should match expected example').to.equal(
+ 'Earth'
+ );
+ });
+
+ it('should return error when using example id behavioral header with wrong id', async function () {
+ const resp = await getNegative(`${proxyUrl}/planets/random`, {
+ 'X-Kong-Mocking-Example-Id': 'jupiter',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have expected error message').to.contain(
+ "could not find the example id 'jupiter'"
+ );
+ });
+
+ it('should mock response with specific status code when using status code behavioral header', async function () {
+ const resp = await getNegative(`${proxyUrl}/planets/random`, {
+ 'X-Kong-Mocking-Status-Code': '404',
+ });
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.error, 'Data should match expected example').to.contain(
+ 'Your planets are in another universe!'
+ );
+ });
+
+ it('should mock response with different status codes', async function () {
+ const payload = {
+ ...basePayload,
+ config: {
+ included_status_codes: statusCodeList,
+ random_status_code: true,
+ },
+ };
+
+ const update = await axios({
+ url: `${url}/${pluginId}`,
+ method: 'patch',
+ data: payload,
+ });
+
+ expect(update.status, 'Should update plugin successfully').to.equal(200);
+
+ await wait(shortWait); // eslint-disable-line no-restricted-syntax
+
+ for (let i = 0; i < 5; i++) {
+ const resp = await getNegative(`${proxyUrl}/planets/random`);
+ logResponse(resp);
+
+ expect(resp.status, 'Should return expected status').to.be.oneOf(
+ statusCodeList
+ );
+ if (resp.status === 200) {
+ expect(
+ resp.data,
+ 'Should return planet info with status 200'
+ ).to.have.property('day_length_earth_days');
+ } else if (resp.status === 404) {
+ expect(
+ resp.data.error,
+ 'Should return error message with status 404'
+ ).to.equal('Your planets are in another universe!');
+ } else if (resp.status === 500) {
+ expect(
+ resp.data.error,
+ 'Should return error message with status 500'
+ ).to.equal('Bzzzzzt. We need to reboot the universe.');
+ }
+ }
+ });
+
+ it('should mock random example response using schemas', async function () {
+ const resp = await axios(`${proxyUrl}/moons`);
+ logResponse(resp);
+
+ expect(resp.status, 'Should return 200 response').to.equal(200);
+ expect(
+ resp.data.orbiting,
+ 'Should have expected type for "orbiting"'
+ ).to.be.a('string');
+ expect(
+ resp.data.diameter_mi,
+ 'Should have expected type for "diameter_mi"'
+ ).to.be.an('number');
+ expect(resp.data.name, 'Should have expected type for "name"').to.be.a(
+ 'string'
+ );
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(moonRouteId);
+ await deleteGatewayRoute(planetRouteId);
+ await deleteGatewayRoute(randomRouteId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/mtls-auth.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/mtls-auth.spec.ts
new file mode 100644
index 00000000..7631c1fe
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/mtls-auth.spec.ts
@@ -0,0 +1,518 @@
+import { authDetails } from '@fixtures';
+import * as https from 'https';
+import axios from 'axios';
+import {
+ createConsumer,
+ createGatewayService,
+ createRouteForService,
+ Environment,
+ expect,
+ getBasePath,
+ getNegative,
+ logResponse,
+ clearAllKongResources,
+ isGateway,
+ waitForConfigRebuild,
+ uploadCaCertificate,
+ Consumer,
+ postNegative,
+ patchConsumer,
+ retryRequest,
+} from '@support';
+
+describe('@smoke: Gateway Plugins: mtls-auth', function () {
+ this.timeout(40000)
+ const path = '/mtls-auth';
+ const serviceName = 'mtls-auth-service';
+ const root1CertDn = 'emailAddress=kong@konghq.com,CN=KongSDET,OU=Gateway,O=Kong,L=Toronto,ST=ON,C=CA'
+ const root2CertDn = 'CN=apitest,OU=Gateway,O=Kong,L=Toronto,ST=ON,C=CA'
+
+ const consumer1Details: Consumer = {
+ username: 'KongSDET',
+ custom_id: '1234'
+ };
+
+ const consumer2Details: Consumer = {
+ username: 'consumer2',
+ custom_id: 'apitest'
+ };
+
+ const consumer3Details: Consumer = {
+ username: 'consumer3'
+ };
+
+ const rootCertIds = {
+ cert1: '',
+ cert2: ''
+ }
+
+ let url: string
+ let proxyUrl: string
+ let serviceId: string;
+ let routeId: string;
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxySec,
+ })}`;
+
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ // Create 3 consumers
+ let consumer = await createConsumer(consumer1Details.username, consumer1Details);
+ consumer1Details.id = consumer.id
+ consumer = await createConsumer(consumer2Details.username, consumer2Details);
+ consumer2Details.id = consumer.id
+ consumer = await createConsumer(consumer3Details.username, consumer3Details);
+ consumer3Details.id = consumer.id
+
+ // upload the root certificate for mtls request verifications with consumers
+ let resp = await uploadCaCertificate(authDetails.mtls_certs.root1)
+ // cert1 has CN of KongSDET
+ rootCertIds.cert1 = resp.id
+ resp = await uploadCaCertificate(authDetails.mtls_certs.root2)
+ // cert2 has CN of apitest
+ rootCertIds.cert2 = resp.id
+
+ basePayload = {
+ name: 'mtls-auth',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+
+ await waitForConfigRebuild()
+ });
+
+ it('should not create mtls-auth plugin without ca_certificates', async function () {
+ const resp = await postNegative(url, basePayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ "schema violation (config.ca_certificates: required field missing)"
+ );
+ });
+
+ it('should create mtls-auth plugin with ca_certificate', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ ca_certificates: [rootCertIds.cert1]
+ },
+ };
+
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.config.ca_certificates[0], 'Should see correct ca_cert id in plugin').to.equal(rootCertIds.cert1);
+ expect(resp.data.config.consumer_by, 'Should see correct consumer_by configuration').to.eql(['username', 'custom_id']);
+ pluginId = resp.data.id;
+
+ await waitForConfigRebuild();
+ });
+
+ it('should not proxy request without supplying certificates', async function () {
+ const req = () => getNegative(`${proxyUrl}${path}`, {}, {}, { rejectUnauthorized: true });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate no api key found').to.equal(
+ 'No required TLS certificate was sent'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not proxy request with supplying non-matching certificates', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity2_cert,
+ key: authDetails.mtls_certs.entity2_key,
+ }),
+ validateStatus: null
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate no api key found').to.equal(
+ 'TLS certificate failed verification'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ // as long as at least one consumer exists with matching CN this test will fail (meaning request will go through which is illogical)
+ // see this conversation https://kongstrong.slack.com/archives/C03CTMSHP6C/p1710440701434039
+ // see this related closed ticket https://konghq.atlassian.net/browse/FTI-3284
+ // to be checked if this is expected or not
+ it.skip('should proxy request with supplying certificates but with non-matching consumer CN', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity1_cert,
+ key: authDetails.mtls_certs.entity1_key,
+ }),
+ validateStatus: null
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ // expect(resp.data.message, 'Should indicate no api key found').to.equal(
+ // 'TLS certificate failed verification'
+ // );
+ });
+
+ it('should proxy request with supplying certificates', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity1_cert,
+ key: authDetails.mtls_certs.entity1_key,
+ })
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Consumer-Custom-Id'], 'Should see X-Consumer-Custom-Id header').to.equal(consumer1Details.custom_id);
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.equal(consumer1Details.id);
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.equal(consumer1Details.username);
+ expect(resp.data.headers['X-Client-Cert-Dn'], 'Should not see X-Client-Cert-Dn header').to.not.exist;
+ expect(resp.data.headers['X-Client-Cert-San'], 'Should not see X-Client-Cert-San header').to.not.exist;
+ });
+
+ it('should patch mtls-auth plugin consumer_by', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ consumer_by: ['custom_id'],
+ ca_certificates: [rootCertIds.cert1, rootCertIds.cert2]
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.consumer_by, 'Should see correct consumer_by configuration').to.eql(['custom_id']);
+ expect(resp.data.config.ca_certificates, 'Should see correct ca_certificates configuration').to.have.lengthOf(2)
+ await waitForConfigRebuild();
+ });
+
+ it('should not proxy request with CN username match when consumer_by is custom_id only', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity1_cert,
+ key: authDetails.mtls_certs.entity1_key,
+ }),
+ validateStatus: null
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should indicate no api key found').to.equal(
+ 'TLS certificate failed verification'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should proxy request with supplying matching certificates for the new root certificate', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity2_cert,
+ key: authDetails.mtls_certs.entity2_key,
+ }),
+ validateStatus: null
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Consumer-Custom-Id'], 'Should see X-Consumer-Custom-Id header').to.equal(consumer2Details.custom_id);
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.equal(consumer2Details.id);
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.equal(consumer2Details.username);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should patch mtls-auth plugin skip_consumer_lookup', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ skip_consumer_lookup: true
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.consumer_by, 'Should see correct consumer_by configuration').to.eql(['custom_id']);
+ expect(resp.data.config.skip_consumer_lookup, 'Should see skip_consumer_lookup enabled').to.be.true
+ expect(resp.data.config.ca_certificates, 'Should see correct ca_certificates configuration').to.have.lengthOf(2)
+
+ await waitForConfigRebuild();
+ });
+
+ it('should proxy request with CN matching username when consumer_by is only custom_id and skip_consumer_lookup is enabled', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity1_cert,
+ key: authDetails.mtls_certs.entity1_key,
+ }),
+ validateStatus: null
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Client-Cert-Dn'], 'Should see X-Client-Cert-Dn header').to.equal(root1CertDn);
+ expect(resp.data.headers['X-Client-Cert-San'], 'Should see X-Client-Cert-San header').to.equal(consumer1Details.username);
+ expect(resp.data.headers['X-Consumer-Custom-Id'], 'Should see X-Consumer-Custom-Id header').to.not.exist
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.not.exist
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.not.exist
+ });
+
+ it('should patch mtls-auth plugin anonymous consumer', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ anonymous: consumer3Details.id
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.consumer_by, 'Should see correct consumer_by configuration').to.eql(['custom_id']);
+ expect(resp.data.config.skip_consumer_lookup, 'Should see skip_consumer_lookup enabled').to.be.true
+ expect(resp.data.config.anonymous, 'Should see correct anonymous configuration').to.equal(consumer3Details.id)
+
+ await waitForConfigRebuild();
+ });
+
+ it('should fallback to the given anonymous consumer when authentication fails', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.keycloak.invalid_cert,
+ key: authDetails.keycloak.invalid_key,
+ }),
+ validateStatus: null
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.equal(consumer3Details.id);
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.equal(consumer3Details.username);
+ expect(resp.data.headers['X-Anonymous-Consumer'], 'Should see X-Anonymous-Consumer header').to.equal('true')
+ });
+
+ it('should patch mtls-auth plugin default consumer', async function () {
+ // update consumer custom_id to non-matching to the cert CN
+ await patchConsumer(consumer2Details.username, { custom_id: 'notapitest' })
+ consumer2Details.custom_id = 'notapitest'
+
+ const req = () =>
+ axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ default_consumer: consumer3Details.id,
+ skip_consumer_lookup: false,
+ consumer_by: ['custom_id', 'username'],
+ },
+ },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.consumer_by, 'Should see correct consumer_by configuration').to.eql(['custom_id','username']);
+ expect(resp.data.config.skip_consumer_lookup, 'Should see skip_consumer_lookup enabled').to.be.false
+ expect(resp.data.config.anonymous, 'Should see correct anonymous configuration').to.equal(consumer3Details.id)
+ expect(resp.data.config.default_consumer, 'Should see correct default_consumer configuration').to.equal(consumer3Details.id)
+ };
+
+ await retryRequest(req, assertions);
+
+ await waitForConfigRebuild();
+ });
+
+ it('should fallback to default_consumer with valid cert when no matching consumer is found', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity2_cert,
+ key: authDetails.mtls_certs.entity2_key,
+ }),
+ validateStatus: null
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);''
+ expect(resp.data.headers['X-Client-Cert-Dn'], 'Should see X-Client-Cert-Dn header').to.equal(root2CertDn);
+ expect(resp.data.headers['X-Client-Cert-San'], 'Should see X-Client-Cert-San header').to.equal('apitest');
+ expect(resp.data.headers['X-Consumer-Custom-Id'], 'Should see X-Consumer-Custom-Id header').to.equal(consumer3Details.custom_id)
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.equal(consumer3Details.id)
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.equal(consumer3Details.username)
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should fallback to anonymous when using invalid cert with default consumer enabled', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.keycloak.invalid_cert,
+ key: authDetails.keycloak.invalid_key,
+ }),
+ validateStatus: null
+ });
+ logResponse(resp);
+
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.equal(consumer3Details.id);
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.equal(consumer3Details.username);
+ });
+
+ it('should successfully send request with matching consumer and cert', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity1_cert,
+ key: authDetails.mtls_certs.entity1_key,
+ }),
+ validateStatus: null
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Consumer-Custom-Id'], 'Should see X-Consumer-Custom-Id header').to.equal(consumer1Details.custom_id);
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.equal(consumer1Details.id);
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.equal(consumer1Details.username);
+ expect(resp.data.headers['X-Client-Cert-Dn'], 'Should not see X-Client-Cert-Dn header').to.not.exist;
+ expect(resp.data.headers['X-Client-Cert-San'], 'Should not see X-Client-Cert-San header').to.not.exist;
+ });
+
+ it('should fallback to default_consumer with valid cert using default consumer username', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ default_consumer: consumer3Details.username
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.default_consumer, 'Should see correct default_consumer configuration').to.equal(consumer3Details.username)
+
+ await waitForConfigRebuild();
+
+ resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity2_cert,
+ key: authDetails.mtls_certs.entity2_key,
+ }),
+ validateStatus: null
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Client-Cert-Dn'], 'Should see X-Client-Cert-Dn header').to.equal(root2CertDn);
+ expect(resp.data.headers['X-Client-Cert-San'], 'Should see X-Client-Cert-San header').to.equal('apitest');
+ expect(resp.data.headers['X-Consumer-Custom-Id'], 'Should see X-Consumer-Custom-Id header').to.equal(consumer3Details.custom_id)
+ expect(resp.data.headers['X-Consumer-Id'], 'Should see X-Consumer-Id header').to.equal(consumer3Details.id)
+ expect(resp.data.headers['X-Consumer-Username'], 'Should see X-Consumer-Username header').to.equal(consumer3Details.username)
+ });
+
+ it('should see 401 when using invalid default consumer uuid', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ default_consumer: '454554687912casdasd456'
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ await waitForConfigRebuild();
+
+ const req = () => axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.mtls_certs.entity2_cert,
+ key: authDetails.mtls_certs.entity2_key,
+ }),
+ validateStatus: null
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'Should see Unauthorized error message').to.equal('Unauthorized')
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should delete the mtls-auth plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await clearAllKongResources()
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/oas-validation.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/oas-validation.spec.ts
new file mode 100644
index 00000000..b146029c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/oas-validation.spec.ts
@@ -0,0 +1,389 @@
+import axios from 'axios';
+import { openApiSchemas } from '@fixtures';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ wait,
+ logResponse,
+ postNegative,
+ randomString,
+ waitForConfigRebuild,
+ eventually,
+ isGateway,
+} from '@support';
+
+describe('Gateway Plugins: oas-validation', function () {
+ const apiSpecUrl = 'http://swagger:8080/api/v3/';
+ const resourcePet = 'pet';
+ const path = '/oas-validation';
+ const serviceName = randomString();
+ const apiSpec = JSON.stringify(openApiSchemas);
+ const hybridWaitTime = 7000;
+ let serviceId: string;
+ let routeId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ let basePayload: any;
+ let missingNamePayload: any;
+ let missingPhotoUrlsPayload: any;
+ let petPayload: any;
+ let pluginId: string;
+ let petId: string;
+
+ before(async function () {
+ const service = await createGatewayService(serviceName, {
+ url: `${apiSpecUrl}`,
+ });
+ serviceId = service.id;
+
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ await wait(hybridWaitTime); // eslint-disable-line no-restricted-syntax
+
+ petPayload = {
+ id: 1,
+ category: {
+ id: 1,
+ name: 'Reptiles',
+ },
+ name: 'Velociraptor',
+ photoUrls: ['http://kongrules'],
+ status: 'available',
+ };
+
+ missingNamePayload = {
+ id: 1,
+ category: {
+ id: 1,
+ name: 'Reptiles',
+ },
+ photoUrls: ['http://localhost:8080/callback'],
+ status: 'available',
+ };
+
+ missingPhotoUrlsPayload = {
+ id: 1,
+ category: {
+ id: 1,
+ name: 'Reptiles',
+ },
+ name: 'Velociraptor',
+ status: 'available',
+ };
+
+ basePayload = {
+ name: 'oas-validation',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should not create oas-validation plugin without an api spec', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ validate_response_body: true,
+ verbose_response: true,
+ },
+ };
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.config.api_spec,
+ 'Should indicate the api spec field is missing'
+ ).to.equal('required field missing');
+ });
+
+ it('should not create oas-validation plugin when api spec format is incorrect', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ api_spec: openApiSchemas,
+ validate_response_body: true,
+ verbose_response: true,
+ },
+ };
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.name, 'Should be schema violation').to.equal(
+ 'schema violation'
+ );
+ expect(
+ resp.data.fields.config.api_spec,
+ 'Should indicate the api spec requires a string'
+ ).to.equal('expected a string');
+ });
+
+ it('should create oas-validation plugin with validation enabled', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ api_spec: apiSpec,
+ validate_response_body: true,
+ verbose_response: true,
+ },
+ };
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ pluginId = resp.data.id;
+
+ expect(resp.data.config.api_spec, 'Should have correct api spec').to.eq(
+ apiSpec
+ );
+ expect(
+ resp.data.config.validate_response_body,
+ 'Should have correct response body validation'
+ ).to.be.true;
+ expect(
+ resp.data.config.verbose_response,
+ 'Should have correct response body validation'
+ ).to.be.true;
+
+ await waitForConfigRebuild();
+ });
+
+ it('should not POST new item when required name field is missing when request validation is enforced', async function () {
+ await eventually(async () => {
+ const resp = await postNegative(
+ `${proxyUrl}${path}/${resourcePet}`,
+ missingNamePayload
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct validation message').to.eq(
+ "request body validation failed with error: 'property name is required'"
+ );
+ });
+ });
+
+ it('should not POST new item when required photoURLS field is missing when request validation is enforced', async function () {
+ const resp = await postNegative(
+ `${proxyUrl}${path}/${resourcePet}`,
+ missingPhotoUrlsPayload
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct validation message').to.eq(
+ "request body validation failed with error: 'property photoUrls is required'"
+ );
+ });
+
+ it('should POST new item with all required fields', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}/${resourcePet}`,
+ method: 'post',
+ data: petPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.category.name, 'Should have correct category name').to.eq(
+ 'Reptiles'
+ );
+ petId = resp.data.id;
+ });
+
+ it('should GET item with item ID', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${proxyUrl}${path}/${resourcePet}/${petId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.category.name, 'Should have correct category name').to.eq(
+ 'Reptiles'
+ );
+ expect(resp.data.status, 'Should have correct status').to.eq('available');
+ });
+
+ it('should update item using PUT with required fields', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}/${resourcePet}`,
+ method: 'put',
+ data: {
+ id: petId,
+ category: {
+ id: 1,
+ name: 'Reptiles',
+ },
+ name: 'Velociraptor',
+ photoUrls: ['url1'],
+ status: 'sold',
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.category.name, 'Should have correct category name').to.eq(
+ 'Reptiles'
+ );
+ expect(resp.data.status, 'Should have correct status').to.eq('sold');
+ petId = resp.data.id;
+ });
+
+ it('should retrieve updated item using GET with item ID', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${proxyUrl}${path}/${resourcePet}/${petId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.category.name, 'Should have correct category name').to.eq(
+ 'Reptiles'
+ );
+ expect(resp.data.status, 'Should have correct status').to.eq('sold');
+ });
+
+ it('should PATCH oas-validation plugin to skip request validation and enforce response validation', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ validate_response_body: true,
+ validate_request_body: false,
+ notify_only_request_validation_failure: false,
+ notify_only_response_body_validation_failure: false,
+ verbose_response: true,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.validate_request_body,
+ 'Should have correct response body validation'
+ ).to.be.false;
+ expect(
+ resp.data.config.validate_response_body,
+ 'Should have correct response body validation'
+ ).to.be.true;
+ expect(
+ resp.data.config.verbose_response,
+ 'Should have correct response body validation'
+ ).to.be.true;
+
+ await waitForConfigRebuild();
+ });
+
+ it('should enforce response validation when parameter enabled', async function () {
+ const resp = await postNegative(
+ `${proxyUrl}${path}/${resourcePet}`,
+ missingNamePayload
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 406').to.equal(406);
+ expect(
+ resp.data.message,
+ 'Should indicate response validation failed'
+ ).to.eq(
+ 'response body validation failed with error: property name is required'
+ );
+ });
+
+ it('should PATCH oas-validation plugin to skip validation', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ validate_response_body: true,
+ validate_request_body: false,
+ notify_only_request_validation_failure: false,
+ notify_only_response_body_validation_failure: true,
+ verbose_response: true,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.validate_request_body,
+ 'Should have correct response body validation'
+ ).to.be.false;
+ expect(
+ resp.data.config.validate_response_body,
+ 'Should have correct response body validation'
+ ).to.be.true;
+ expect(
+ resp.data.config.verbose_response,
+ 'Should have correct response body validation'
+ ).to.be.true;
+
+ await waitForConfigRebuild();
+ });
+
+ it('should POST new item without required name field when validation is skipped', async function () {
+ const resp = await postNegative(
+ `${proxyUrl}${path}/${resourcePet}`,
+ missingNamePayload
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.category.name, 'Should have correct category name').to.eq(
+ 'Reptiles'
+ );
+ });
+
+ it('should POST new item without required photoURLS field when validation is skipped', async function () {
+ const resp = await postNegative(
+ `${proxyUrl}${path}/${resourcePet}`,
+ missingPhotoUrlsPayload
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.category.name, 'Should have correct category name').to.eq(
+ 'Reptiles'
+ );
+ });
+
+ it('should delete the oas-validation plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/openid-connect.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/openid-connect.spec.ts
new file mode 100644
index 00000000..9e8e8118
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/openid-connect.spec.ts
@@ -0,0 +1,644 @@
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ createRouteForService,
+ deletePlugin,
+ waitForConfigRebuild,
+ logResponse,
+ isGateway,
+ resetGatewayContainerEnvVariable,
+ getKongContainerName,
+ getGatewayMode,
+ clearAllKongResources,
+ randomString,
+ generateDpopProof,
+ isGwNative,
+} from '@support'
+import {
+ authDetails,
+} from '@fixtures'
+import axios from 'axios'
+import https from 'https'
+import querystring from 'querystring'
+
+const urls = {
+ admin: `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`,
+ proxy: `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`,
+ proxySec: `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxySec,
+ })}`,
+ keycloak: `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.keycloakSec,
+ })}`,
+ okta: 'https://kong-sandbox.oktapreview.com/oauth2',
+}
+
+const isHybrid = getGatewayMode() === 'hybrid'
+const kongContainerName = getKongContainerName()
+const kongDpContainerName = 'kong-dp1';
+const serviceName = 'oidc-service'
+
+let serviceId: string
+let oidcPluginId: string
+
+describe('Gateway Plugins: OIDC with Okta', function () {
+ const oktaPath = '/oidcOktaAuthentication'
+ const oktaIssuerUrl = `${urls.okta}/default`
+ const oktaTokenUrl = `${urls.okta}/default/v1/token`
+
+ // DPoP vars
+ const dpopClientId = '0oae8abki4NEfVkLQ1d7'
+ const dpopClientSecret = 'eOXgBBLJHRcd9GYvkP3fbvfrf2tU6RmX3j_WYPYlRHGmQ1paRcNkfFM0DHolo1P6'
+ const jti = randomString()
+
+ let dpopProof: string
+ let dpopProofNonce: string
+ let updatedDpopProof: string
+ let dpopToken: string
+ let currentTime: number
+
+ before(async function () {
+ const service = await createGatewayService(serviceName)
+ serviceId = service.id
+ await createRouteForService(serviceId, [oktaPath])
+ })
+
+ //======= DPoP tests =======
+ it('should create OIDC plugin with dPoP enabled', async function () {
+ const resp = await axios({
+ method: 'POST',
+ url: `${urls.admin}/services/${serviceId}/plugins/`,
+ data: {
+ name: 'openid-connect',
+ config: {
+ client_id: [ dpopClientId ],
+ client_secret: [ dpopClientSecret ],
+ auth_methods: [ 'bearer' ],
+ issuer: oktaIssuerUrl,
+ proof_of_possession_dpop: 'strict',
+ scopes: ['scope1'],
+ expose_error_code: true,
+ },
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(201)
+ oidcPluginId = resp.data.id
+ await waitForConfigRebuild()
+ })
+
+ it('should be able to request token with dPoP', async function() {
+ // initial request to get nonce - should return 400
+ currentTime = Math.floor(Date.now() / 1000)
+ dpopProof = await generateDpopProof({time: currentTime, jti: jti, nonce: '', token: '', url: oktaTokenUrl})
+ const resp = await axios({
+ method: 'POST',
+ url: oktaTokenUrl,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Accept': 'application/json',
+ 'DPoP': dpopProof
+ },
+ data: querystring.stringify({
+ grant_type: 'client_credentials',
+ client_id: dpopClientId,
+ client_secret: dpopClientSecret,
+ scope: 'scope1'
+ }),
+ validateStatus: null,
+ })
+
+ expect(resp.status).to.equal(400)
+ expect(resp.data.error_description).to.contain('Authorization server requires nonce in DPoP proof')
+ dpopProofNonce = resp.headers['dpop-nonce']
+
+ // request token with dPoP proof
+ updatedDpopProof = await generateDpopProof({time: currentTime, jti: jti, nonce: dpopProofNonce, token: '', url: oktaTokenUrl})
+ const tokenResp = await axios({
+ method: 'POST',
+ url: oktaTokenUrl,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Accept': 'application/json',
+ 'DPoP': updatedDpopProof
+ },
+ data: querystring.stringify({
+ grant_type: 'client_credentials',
+ client_id: dpopClientId,
+ client_secret: dpopClientSecret,
+ scope: 'scope1'
+ }),
+ })
+ expect(tokenResp.status).to.equal(200)
+ expect(tokenResp.data).to.have.property('access_token')
+ expect(tokenResp.data.token_type).to.equal('DPoP')
+ dpopToken = tokenResp.data.access_token
+ await waitForConfigRebuild()
+ })
+
+ it('should return 401 when accessing route without token but with dpop proof', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxy}${oktaPath}`,
+ validateStatus: null,
+ headers: {
+ 'DPoP': dpopProof,
+ }
+ })
+
+ expect(resp.status).to.equal(401)
+ expect(resp.headers['www-authenticate']).to.contain('DPoP')
+ expect(resp.headers['www-authenticate']).to.contain('error="invalid_token"')
+ })
+
+ it('should return 401 when accessing route with token but without token proof', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxy}${oktaPath}`,
+ validateStatus: null,
+ headers: {
+ 'Authorization': `DPoP ${dpopToken}`,
+ }
+ })
+
+ expect(resp.status).to.equal(401)
+ expect(resp.headers['www-authenticate']).to.contain('DPoP')
+ expect(resp.headers['www-authenticate']).to.contain('error="invalid_dpop_proof"')
+ })
+
+ it('should return 401 when accessing route with downgraded dpop proof', async function() {
+ const proofWithRoute = await generateDpopProof({time: currentTime, jti: jti, nonce: '', token: dpopToken, url: `${urls.proxy}${oktaPath}`})
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxy}${oktaPath}`,
+ validateStatus: null,
+ headers: {
+ 'Authorization': `Bearer ${dpopToken}`,
+ 'DPoP': proofWithRoute,
+ }
+ })
+
+ logResponse(resp)
+ expect(resp.status).to.equal(401)
+ expect(resp.headers['www-authenticate']).to.contain('DPoP')
+ expect(resp.headers['www-authenticate']).to.contain('error="invalid_dpop_proof"')
+ })
+
+ it('should return 401 when accessing route with dpop proof with incorrect htu claim', async function() {
+ const proofWithIncorrectHtu = await generateDpopProof({time: currentTime, jti: jti, nonce: '', token: dpopToken, url: `http://localhost:8000/wrongpath`})
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxy}${oktaPath}`,
+ headers: {
+ 'Authorization': `DPoP ${dpopToken}`,
+ 'DPoP': proofWithIncorrectHtu,
+ },
+ validateStatus: null,
+ })
+
+ logResponse(resp)
+ expect(resp.status).to.equal(401)
+ expect(resp.headers['www-authenticate']).to.contain('DPoP')
+ expect(resp.headers['www-authenticate']).to.contain('error="invalid_dpop_proof"')
+ })
+
+ it('should return 200 when accessing route with token and dpop proof', async function() {
+ const proofWithRoute = await generateDpopProof({time: currentTime, jti: jti, nonce: '', token: dpopToken, url: `${urls.proxy}${oktaPath}`})
+ console.log('proofWithRoute: ', proofWithRoute)
+
+ const resp = await axios({
+ method: 'POST',
+ url: `${urls.proxy}${oktaPath}`,
+ headers: {
+ 'Authorization': `DPoP ${dpopToken}`,
+ 'DPoP': proofWithRoute,
+ },
+ validateStatus: null,
+ })
+
+ expect(resp.status).to.equal(200)
+ })
+
+ it('should delete OIDC plugin', async function () {
+ const resp = await axios({
+ method: 'DELETE',
+ url: `${urls.admin}/plugins/${oidcPluginId}`,
+ })
+ expect(resp.status).to.equal(204)
+ })
+
+ after(async function () {
+ await clearAllKongResources()
+ })
+
+})
+
+// Skip this test suite for packages to investigate the error when creating OIDC plugin
+describe('Gateway Plugins: OIDC with Keycloak', function () {
+ const keycloakPath = '/oidc'
+ const keycloakTokenRequestUrl = `${urls.keycloak}/realms/demo/protocol/openid-connect/token`
+ const keycloakIssuerUrl = `https://keycloak:8543/realms/demo/`
+ const certClientId = 'kong-certificate-bound'
+ const certClientSecret = '670f2328-85a0-11ee-b9d1-0242ac120002'
+ const mtlsClientId = 'kong-client-tls-auth'
+ const isKongNative = isGwNative();
+
+ const invalidCertificate = authDetails.keycloak.invalid_cert
+ const invalidKey = authDetails.keycloak.invalid_key
+ const clientCertificate = authDetails.keycloak.client_cert
+ const clientKey = authDetails.keycloak.client_key
+
+ const certHttpsAgent = new https.Agent({
+ cert: clientCertificate,
+ key: clientKey,
+ rejectUnauthorized: false,
+ })
+
+ let tlsPluginId: string
+ let certId: string
+ let invalidCertId: string
+ let expiredCertId: string
+ let token: string
+
+
+ before(async function () {
+ //set KONG_LUA_SSL_TRUSTED_CERTIFICATE value to root/intermediate CA certificates
+ //whenever the original LUA_SSL_TRUSTED_CERTIFICATE is being modified, the keyring needs to either be turned off or get its certificates updated as well
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_LUA_SSL_TRUSTED_CERTIFICATE: '/tmp/root_ca.crt,/tmp/intermediate_ca.crt', KONG_KEYRING_ENABLED: `${isKongNative ? 'off' : 'on'}`
+ },
+ kongContainerName
+ );
+
+ if (isHybrid) {
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_LUA_SSL_TRUSTED_CERTIFICATE: '/tmp/root_ca.crt,/tmp/intermediate_ca.crt', KONG_KEYRING_ENABLED: `${isKongNative ? 'off' : 'on'}`
+ },
+ kongDpContainerName
+ );
+ }
+
+ const service = await createGatewayService(serviceName)
+ serviceId = service.id
+ await createRouteForService(serviceId, [keycloakPath])
+
+ const validCert = await axios({
+ method: 'POST',
+ url: `${urls.admin}/certificates`,
+ data: {
+ cert: clientCertificate,
+ key: clientKey,
+ },
+ validateStatus: null,
+ })
+ certId = validCert.data.id
+
+ const invalidCert = await axios({
+ method: 'POST',
+ url: `${urls.admin}/certificates`,
+ data: {
+ cert: invalidCertificate,
+ key: invalidKey,
+ },
+ validateStatus: null,
+ })
+ invalidCertId = invalidCert.data.id
+
+ // Create TLS plugin for certificate-based tokens
+ const resp = await axios({
+ method: 'POST',
+ url: `${urls.admin}/services/${serviceId}/plugins`,
+ data: { name: 'tls-handshake-modifier' },
+ })
+ tlsPluginId = resp.data.id
+ })
+
+ //======= cert-based auth tests =======
+ it('should create OIDC plugin that uses certificate-based tokens', async function () {
+ const resp = await axios({
+ method: 'POST',
+ url: `${urls.admin}/services/${serviceId}/plugins`,
+ data: {
+ name: 'openid-connect',
+ config: {
+ client_id: [ certClientId ],
+ client_secret: [ certClientSecret ],
+ issuer: keycloakIssuerUrl,
+ token_endpoint: keycloakTokenRequestUrl,
+ proof_of_possession_mtls: 'strict',
+ ignore_signature: [ 'client_credentials' ],
+ auth_methods: ['bearer'],
+ cache_user_info: false,
+ cache_tokens: false,
+ cache_introspection: false,
+ },
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(201)
+ oidcPluginId = resp.data.id
+ await waitForConfigRebuild()
+ })
+
+ it('should not be able to request a token without a valid certificate', async function() {
+ const resp = await axios({
+ method: 'POST',
+ url: keycloakTokenRequestUrl,
+ data: querystring.stringify({
+ grant_type: 'client_credentials',
+ client_id: certClientId,
+ client_secret: certClientSecret,
+ }),
+ validateStatus: null,
+ })
+ logResponse(resp)
+
+ expect(resp.status).to.equal(400)
+ expect(resp.data.error_description).to.equal('Client Certification missing for MTLS HoK Token Binding')
+ })
+
+ it('should return 401 when accessing route without certificate in request', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ validateStatus: null,
+ })
+ expect(resp.status).to.equal(401)
+ expect(resp.data.message).to.equal('Unauthorized')
+ })
+
+ it('should be able to request token with valid certificate provided', async function() {
+ const resp = await axios({
+ method: 'POST',
+ url: keycloakTokenRequestUrl,
+ data: querystring.stringify({
+ grant_type: 'client_credentials',
+ client_id: certClientId,
+ client_secret: certClientSecret,
+ }),
+ httpsAgent: certHttpsAgent,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(200)
+ token = resp.data.access_token
+ })
+
+ it('should return 401 when accessing route with certificate but no token', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ validateStatus: null,
+ httpsAgent: certHttpsAgent,
+ })
+ expect(resp.status).to.equal(401)
+ })
+
+ it('should return 401 when accessing route with incorrect certificate', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ },
+ httpsAgent: new https.Agent({
+ cert: invalidCertificate,
+ key: invalidKey,
+ rejectUnauthorized: false,
+ }),
+ validateStatus: null,
+ })
+ expect(resp.status).to.equal(401)
+ })
+
+ it('should successfully authenticate when accessing route with certificate and token', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ httpsAgent: certHttpsAgent,
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(200)
+ // delete tls plugin to test mTLS auth
+ await deletePlugin(tlsPluginId)
+ await waitForConfigRebuild()
+ })
+
+ //======= mTLS auth tests =======
+ it('should not update plugin to use mTLS authentication without certificate', async function () {
+ const resp = await axios({
+ method: 'POST',
+ url: `${urls.admin}/plugins`,
+ data: {
+ name: 'openid-connect',
+ config: {
+ client_id: [ mtlsClientId ],
+ auth_methods: [ 'password' ],
+ issuer: keycloakIssuerUrl,
+ client_auth: [ 'tls_client_auth' ],
+ login_methods: ['authorization_code'],
+ tls_client_auth_ssl_verify: true,
+ },
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(400)
+ expect(resp.data.message).to.contain('tls_client_auth_cert_id is required when tls client auth is enabled')
+ })
+
+ it('should update plugin to use mTLS authentication', async function () {
+ const resp = await axios({
+ method: 'PATCH',
+ url: `${urls.admin}/plugins/${oidcPluginId}`,
+ data: {
+ config: {
+ client_id: [ mtlsClientId ],
+ auth_methods: [ 'password' ],
+ client_auth: [ 'tls_client_auth' ],
+ tls_client_auth_cert_id: certId,
+ issuer: keycloakIssuerUrl,
+ login_methods: ['authorization_code'],
+ tls_client_auth_ssl_verify: true,
+ ignore_signature: [],
+ proof_of_possession_mtls: 'off',
+ },
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(200)
+ await waitForConfigRebuild()
+ })
+
+ it('should return 401 when accessing route without credentials', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ validateStatus: null,
+ })
+ expect(resp.status).to.equal(401)
+ })
+
+ it('should return 401 when accessing route with incorrect credentials', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ auth: {
+ username:'john',
+ password:'no',
+ },
+ validateStatus: null,
+ })
+ expect(resp.status).to.equal(401)
+ })
+
+ it('should successfully authenticate when accessing route with mTLS authentication', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ auth: {
+ username:'john',
+ password:'doe',
+ },
+ validateStatus: null,
+ })
+
+ logResponse(resp)
+ expect(resp.status).to.equal(200)
+ expect(resp.data.headers).to.have.property('Authorization')
+ })
+
+ it('should update plugin to use an invalid certificate', async function () {
+ const resp = await axios({
+ method: 'PATCH',
+ url: `${urls.admin}/plugins/${oidcPluginId}`,
+ data: {
+ config: {
+ tls_client_auth_cert_id: invalidCertId,
+ },
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(200)
+ await waitForConfigRebuild()
+ })
+
+ it('should return 401 when accessing route with mismatched certificate', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ auth: {
+ username:'john',
+ password:'doe',
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(401)
+ })
+
+ it('should update plugin to use expired certificate', async function() {
+ const resp = await axios({
+ method: 'PATCH',
+ url: `${urls.admin}/plugins/${oidcPluginId}`,
+ data: {
+ config: {
+ tls_client_auth_cert_id: expiredCertId,
+ },
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(200)
+ })
+
+ it('should return 401 when accessing route with expired certificate', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxySec}${keycloakPath}`,
+ auth: {
+ username:'john',
+ password:'doe',
+ },
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(401)
+ })
+
+ //====== Unauthorized message tests =======
+ it('should update plugin to use different message for unauthorized requests', async function () {
+ const resp = await axios({
+ method: 'PATCH',
+ url: `${urls.admin}/plugins/${oidcPluginId}`,
+ data: {
+ config: {
+ unauthorized_error_message: 'You shall not pass!',
+ },
+ },
+ validateStatus: null,
+ })
+ expect(resp.status).to.equal(200)
+ await waitForConfigRebuild()
+ })
+
+ it('should return custom message when accessing route without authorization', async function() {
+ const resp = await axios({
+ method: 'GET',
+ url: `${urls.proxy}${keycloakPath}`,
+ validateStatus: null,
+ })
+ logResponse(resp)
+ expect(resp.status).to.equal(401)
+ expect(resp.data.message).to.equal('You shall not pass!')
+ })
+
+ it('should delete OIDC plugin', async function () {
+ const resp = await axios({
+ method: 'DELETE',
+ url: `${urls.admin}/plugins/${oidcPluginId}`,
+ })
+ expect(resp.status).to.equal(204)
+ })
+
+ after(async function () {
+ await clearAllKongResources()
+
+ // reset vars back to original setting 'system'
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_LUA_SSL_TRUSTED_CERTIFICATE: 'system', KONG_KEYRING_ENABLED: `${isKongNative ? 'on' : 'off'}`
+ },
+ kongContainerName
+ );
+ if (isHybrid) {
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_LUA_SSL_TRUSTED_CERTIFICATE: 'system', KONG_KEYRING_ENABLED: `${isKongNative ? 'on' : 'off'}`
+ },
+ kongDpContainerName
+ );
+ }
+ })
+})
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/opentelemetry.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/opentelemetry.spec.ts
new file mode 100644
index 00000000..08b9f01f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/opentelemetry.spec.ts
@@ -0,0 +1,713 @@
+import axios from 'axios';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ postNegative,
+ createGatewayService,
+ deleteGatewayService,
+ deleteGatewayRoute,
+ randomString,
+ isGwHybrid,
+ isLocalDatabase,
+ wait,
+ logResponse,
+ deletePlugin,
+ createRouteForService,
+ resetGatewayContainerEnvVariable,
+ getKongContainerName,
+ logDebug,
+ isGateway,
+ retryRequest,
+} from '@support';
+
+describe('Gateway Plugins: OpenTelemetry', function () {
+ this.timeout(50000);
+
+ const isHybrid = isGwHybrid();
+ const isLocalDb = isLocalDatabase();
+ const waitTime = 5000;
+ const hybridWaitTime = 8000;
+ const jaegerWait = 20000;
+ const configEndpoint = 'http://jaeger:4318/v1/traces';
+ const paths = ['/jaegertest1', '/jaegertest2', '/jaegertest3'];
+ const b3Header = '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90'
+ const traceparentHeader = '00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01'
+ const amazonHeader = 'Root=1-63441c4a-abcdef012345678912345678'
+ const otHeader = 'W31SFeJcgC00L0DFXtsjSmwVwgJB0soF'
+
+ const host = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.hostName,
+ })}`;
+ const jaegerTracesEndpoint = `http://${host}:16686/api/traces?service=kong&lookback=2m`;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ const gwContainerName = getKongContainerName();
+
+ let serviceId: string;
+ let routeId: string;
+ let pluginId: string;
+ let totalTraces: number;
+ let expectedTraces: number;
+
+ before(async function () {
+ // enable kong opel tracing for requests for this test
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_TRACING_INSTRUMENTATIONS: 'request',
+ KONG_TRACING_SAMPLING_RATE: 1,
+ },
+ gwContainerName
+ );
+ if (isHybrid) {
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_TRACING_INSTRUMENTATIONS: 'request',
+ KONG_TRACING_SAMPLING_RATE: 1,
+ },
+ 'kong-dp1'
+ );
+ }
+
+ // wait longer if running kong natively
+ await wait(gwContainerName === 'kong-cp' ? 2000 : 5000); // eslint-disable-line no-restricted-syntax
+ const service = await createGatewayService(randomString());
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, ['/']);
+ routeId = route.id;
+
+ await wait(jaegerWait); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should not create opel plugin with invalid config.endpoint', async function () {
+ const pluginPayload = {
+ name: 'opentelemetry',
+ config: {
+ endpoint: 'test',
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `schema violation (config.endpoint: missing host in url)`
+ );
+ });
+
+ it('should create otel plugin with valid jaeger config.endpoint', async function () {
+ const pluginPayload = {
+ name: 'opentelemetry',
+ config: {
+ endpoint: configEndpoint,
+ },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ pluginId = resp.data.id;
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ await wait(hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should send proxy request traces to jaeger', async function () {
+ let targetDataset: any;
+ let urlObj;
+ let statusObj;
+ let resp = await axios(`${proxyUrl}${paths[0]}`);
+ logResponse(resp);
+ await wait(jaegerWait + (isLocalDb ? 0 : 10000)); // eslint-disable-line no-restricted-syntax
+
+ resp = await axios(jaegerTracesEndpoint);
+ logResponse(resp);
+ totalTraces = resp.data.data.length;
+
+ expect(
+ resp.data.data.length,
+ 'Should send one request trace to jaeger'
+ ).to.gte(1);
+
+ let isFound = false;
+ for (const data of resp.data.data) {
+ // find the http.url object which value is 'http://localhost/jaegertest1''
+ urlObj = data.spans[0].tags.find((obj) => obj.key === 'http.url');
+ logDebug('urlObj.value: ' + urlObj.value);
+ statusObj = data.spans[0].tags.find((obj) => obj.key === 'http.status_code');
+ logDebug('urlStatus.value: ' + statusObj.value);
+
+ if (urlObj.value.includes(paths[0]) && statusObj.value === 200) {
+ isFound = true;
+ targetDataset = data;
+ break;
+ }
+ }
+
+ expect(isFound, 'Should find the target trace in jaeger').to.be.true;
+
+ expect(
+ targetDataset.spans[0].operationName,
+ 'Should have operationName kong'
+ ).to.equal(`kong`);
+
+ expect(
+ targetDataset.processes.p1.serviceName,
+ 'Should have correct serviceName'
+ ).to.equal(`kong`);
+
+ const instance_id = targetDataset.processes.p1.tags.find((obj) => {
+ return obj.key === 'service.instance.id';
+ });
+ const version = targetDataset.processes.p1.tags.find((obj) => {
+ return obj.key === 'service.version';
+ });
+ expect(
+ instance_id.value,
+ 'Should have service.instance.id'
+ ).to.be.string;
+ expect(
+ version.value,
+ 'Should have service.version'
+ ).to.be.string;
+
+ const opelTagUrl = `${proxyUrl.split(':8000')[0]}${paths[0]}`;
+
+ expect(urlObj.value, `Should see correct http.url`).to.equal(opelTagUrl);
+
+ expect(
+ targetDataset.spans[0].tags.some((tag) => tag.value === 200),
+ `Should see correct status_code in jaeger trace span tags`
+ ).to.be.true;
+ });
+
+ it('should patch opel plugin resource_attributes', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ resource_attributes: {
+ 'service.instance.id': '8888',
+ 'service.version': 'kongtest',
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.resource_attributes['service.instance.id'],
+ 'Should see updated instance id'
+ ).to.equal('8888');
+ expect(
+ resp.data.config.resource_attributes['service.version'],
+ 'Should see updated instance id'
+ ).to.equal('kongtest');
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+ });
+
+ it('should send updated service instance.id and version metadata to jaeger', async function () {
+ const resp = await axios(`${proxyUrl}${paths[1]}`);
+ logResponse(resp);
+ await wait(jaegerWait + (isLocalDb ? 0 : hybridWaitTime)); // eslint-disable-line no-restricted-syntax
+
+ expectedTraces = totalTraces + 1;
+
+ const req = () =>
+ axios({
+ url: jaegerTracesEndpoint,
+ });
+
+ const assertions = (resp) => {
+ expect(
+ resp.data.data.length,
+ 'Should see correct number of request traces in jaeger'
+ ).to.be.greaterThanOrEqual(expectedTraces);
+
+ // setting new totalTraces number
+ totalTraces = resp.data.data.length;
+
+ let isFound = false;
+ for (const data of resp.data.data) {
+ const urlObj = data.spans[0].tags.find((obj) => obj.key === 'http.url');
+
+ if (urlObj.value.includes(paths[1])) {
+ isFound = true;
+
+ expect(urlObj.value, `Should see correct http.url`).to.contain(
+ paths[1]
+ );
+
+ expect(
+ data.spans[0].operationName,
+ 'Should have kong operationName'
+ ).to.equal(`kong`);
+
+ const instance_id = data.processes.p1.tags.find((obj) => {
+ return obj.key === 'service.instance.id';
+ });
+ const version = data.processes.p1.tags.find((obj) => {
+ return obj.key === 'service.version';
+ });
+ expect(
+ instance_id.value,
+ 'Should have correct service.instance.id'
+ ).to.equal(`8888`);
+ expect(
+ version.value,
+ 'Should have correct service.version'
+ ).to.equal(`kongtest`);
+ }
+ }
+
+ expect(isFound, 'Should find the target trace in jaeger').to.be.true;
+ };
+
+ await retryRequest(req, assertions, 10000);
+ });
+
+ it('should not get 500 when traceparent -00 header is present in the request', async function () {
+ // note that traces will not be sent as the header has suffix -00
+ const resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ traceparent: '00-fff379b78684fd43a9e2bba4676ddc90-eb1c7f5c7a2f374f-00',
+ },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['Traceparent'], 'Should see Traceparent header being sent').to.contain('00-fff379b78684fd43a9e2bba4676ddc90');
+ });
+
+ it('should see only b3 header instead of traceparent when b3 exists in request', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ b3: b3Header,
+ },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['Traceparent'], 'Should not see Traceparent header when b3 header exists').to.not.exist
+ expect(resp.data.headers['B3'], 'Should see B3 header being sent').to.contain(b3Header.split('-')[0]);
+ });
+
+ it('should patch opel plugin header_type to ignore', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ resource_attributes: null,
+ header_type: 'ignore'
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.header_type,'Should see header_type updated').to.equal('ignore')
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+ });
+
+ it('should see a different traceparent header id when header_type is ignore', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ traceparent: traceparentHeader,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['Traceparent'], 'Should see Traceparent header with different id').to.not.contain(traceparentHeader.split('-')[1]);
+ });
+
+ it('should see the same value for both b3 and traceparent headers with header_type w3c', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ header_type: 'w3c'
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.header_type,'Should see header_type updated').to.equal('w3c')
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+
+ resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ b3: b3Header,
+ traceparent: traceparentHeader,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['Traceparent'], 'Should see Traceparent header with same value as B3').to.contain(b3Header.split('-')[0]);
+ expect(resp.data.headers['B3'], 'Should see B3 header with given value').to.contain(b3Header.split('-')[0]);
+ expect(resp.data.headers['B3'], 'Should see B3 header id').to.contain(b3Header.split('-')[1]);
+ });
+
+ it('should patch opel plugin propagation configurations', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ propagation: {
+ clear: null,
+ default_format: 'w3c',
+ extract: ['w3c'],
+ inject: ['w3c']
+ }
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.propagation.default_format,'Should see correct propagation.default_format').to.equal('w3c')
+ expect(resp.data.config.propagation.extract,'Should see updated propagation.extract').to.eql(['w3c'])
+ expect(resp.data.config.propagation.inject,'Should see updated propagation.inject').to.eql(['w3c'])
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+ });
+
+ it('should both b3 and traceparent headers preserve their values', async function () {
+ const req = () =>
+ axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ b3: b3Header,
+ traceparent: traceparentHeader,
+ },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['B3'], 'Should see B3 header with given value').to.contain(b3Header.split('-')[0]);
+ expect(resp.data.headers['B3'], 'Should see B3 header id').to.contain(b3Header.split('-')[1]);
+ expect(resp.data.headers['B3'], 'Should see B3 header parentSpanId').to.contain(b3Header.split('-')[3]);
+ expect(resp.data.headers['Traceparent'], 'Should see Traceparent header with its value').to.contain(traceparentHeader.split('-')[1]);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should propagation.clear the given b3 header', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ propagation: {
+ clear: ['b3'],
+ default_format: 'w3c',
+ extract: ['w3c'],
+ inject: ['w3c']
+ }
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.propagation.default_format,'Should see correct propagation.default_format').to.equal('w3c')
+ expect(resp.data.config.propagation.extract,'Should see updated propagation.extract').to.eql(['w3c'])
+ expect(resp.data.config.propagation.clear,'Should see updated propagation.clear').to.eql(['b3'])
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+
+ resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ b3: b3Header,
+ traceparent: traceparentHeader,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['B3'], 'Should not see B3 header').to.not.exist
+ expect(resp.data.headers['Traceparent'], 'Should see Traceparent header with its value').to.contain(traceparentHeader.split('-')[1]);
+ });
+
+ it('should not replace traceparent value with b3 when propagation.clear contains b3 and both headers exist in the request', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ propagation: {
+ clear: ['b3'],
+ default_format: 'w3c',
+ extract: ['w3c', 'b3'],
+ inject: ['w3c']
+ }
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.propagation.extract,'Should see updated propagation.extract').to.eql(['w3c', 'b3'])
+ expect(resp.data.config.propagation.clear,'Should see updated propagation.clear').to.eql(['b3'])
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+
+ resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ b3: b3Header,
+ traceparent: traceparentHeader,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['B3'], 'Should not see B3 header').to.not.exist
+ expect(resp.data.headers['Traceparent'], 'Should see Traceparent header with its value').to.contain(traceparentHeader.split('-')[1]);
+ });
+
+ it('should replace traceparent value with b3 when propagation.clear contains b3 and only b3 header exists in the request', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ b3: b3Header
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['B3'], 'Should not see B3 header').to.not.exist
+ expect(resp.data.headers['Traceparent'], 'Should see Traceparent header with B3 value').to.contain(b3Header.split('-')[0]);
+ });
+
+ it('should extract from given header and inject to the target one', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ propagation: {
+ clear: ['b3'],
+ default_format: 'w3c',
+ extract: ['aws'],
+ inject: ['ot']
+ }
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.propagation.extract,'Should see updated propagation.extract').to.eql(['aws'])
+ expect(resp.data.config.propagation.inject,'Should see updated propagation.inject').to.eql(['ot'])
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+
+ resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ "X-Amzn-Trace-Id": amazonHeader,
+ "Ot-Tracer-Traceid": otHeader,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Amzn-Trace-Id'], 'Should unchanged X-Amzn-Trace-Id header').to.equal(amazonHeader)
+ expect(resp.data.headers['Ot-Tracer-Traceid'], 'Should see Ot-Tracer-Traceid header with aws value').to.contain(amazonHeader.split('-')[1]);
+ });
+
+ it('should not inject to the target header when extract is not present', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ "Ot-Tracer-Traceid": otHeader,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Amzn-Trace-Id'], 'Should not see X-Amzn-Trace-Id header').to.not.exist
+ expect(resp.data.headers['Ot-Tracer-Traceid'], 'Should see Ot-Tracer-Traceid header with non aws value').to.not.contain(amazonHeader.split('-')[1]);
+ expect(resp.data.headers['Ot-Tracer-Sampled'], 'Should see ot sampled header').to.exist
+ expect(resp.data.headers['Ot-Tracer-Spanid'], 'Should see ot Spanid header').to.exist
+ });
+
+ it('should use the default header when extract is not present and inject is preserve', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ propagation: {
+ clear: null,
+ default_format: 'ot',
+ extract: ['datadog'],
+ inject: ['preserve']
+ }
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.propagation.extract,'Should see updated propagation.extract').to.eql(['datadog'])
+ expect(resp.data.config.propagation.inject,'Should see updated propagation.inject').to.eql(['preserve'])
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+
+ resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['Ot-Tracer-Sampled'], 'Should see ot sampled header').to.exist
+ expect(resp.data.headers['Ot-Tracer-Spanid'], 'Should see ot Spanid header').to.exist
+ expect(resp.data.headers['Ot-Tracer-Traceid'], 'Should see ot Traceid header').to.exist
+ });
+
+ it('should see all specified inject headers added by kong', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ propagation: {
+ extract: ['w3c', 'b3'],
+ inject: ['w3c', 'b3', 'datadog', 'aws', 'gcp', 'jaeger', 'ot'],
+ clear: ['b3'],
+ default_format: 'w3c',
+ }
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.propagation.inject,'Should see updated propagation.inject values').to.eql(['w3c', 'b3', 'datadog', 'aws', 'gcp', 'jaeger', 'ot'])
+
+ await wait( // eslint-disable-line no-restricted-syntax
+ isHybrid
+ ? hybridWaitTime + (isLocalDb ? 0 : hybridWaitTime)
+ : waitTime + (isLocalDb ? 0 : waitTime)
+ );
+
+ resp = await axios({
+ url: `${proxyUrl}${paths[1]}`,
+ headers: {
+ traceparent: traceparentHeader,
+ },
+ });
+
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['Ot-Tracer-Sampled'], 'Should see ot sampled header').to.exist
+ expect(resp.data.headers['Ot-Tracer-Spanid'], 'Should see ot Spanid header').to.exist
+ expect(resp.data.headers['Ot-Tracer-Traceid'], 'Should see ot Traceid header').to.exist
+ expect(resp.data.headers['Uber-Trace-Id'], 'Should see Uber-Trace-Id header').to.exist
+ expect(resp.data.headers['X-Amzn-Trace-Id'], 'Should see X-Amzn-Trace-Id header').to.contain('Root=')
+ expect(resp.data.headers['X-B3-Parentspanid'], 'Should see X-B3-Parentspanid header').to.exist
+ expect(resp.data.headers['X-B3-Sampled'], 'Should see X-B3-Sampled header').to.exist
+ expect(resp.data.headers['X-B3-Spanid'], 'Should see X-B3-Spanid header').to.exist
+ expect(resp.data.headers['X-B3-Traceid'], 'Should see X-B3-Traceid header').to.exist
+ expect(resp.data.headers['X-Cloud-Trace-Context'], 'Should see X-Cloud-Trace-Context header').to.exist
+ expect(resp.data.headers['X-Datadog-Parent-Id'], 'Should see X-Datadog-Parent-Id header').to.exist
+ expect(resp.data.headers['X-Datadog-Sampling-Priority'], 'Should see X-Datadog-Sampling-Priority header').to.exist
+ expect(resp.data.headers['X-Datadog-Trace-Id'], 'Should see X-Datadog-Trace-Id header').to.exist
+ expect(resp.data.headers['X-Datadog-Trace-Id'], 'Should see X-Datadog-Trace-Id header').to.exist
+ });
+
+ after(async function () {
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_TRACING_INSTRUMENTATIONS: 'off',
+ KONG_TRACING_SAMPLING_RATE: 0.01,
+ },
+ gwContainerName
+ );
+ if (isHybrid) {
+ await resetGatewayContainerEnvVariable(
+ {
+ KONG_TRACING_INSTRUMENTATIONS: 'off',
+ KONG_TRACING_SAMPLING_RATE: 0.01,
+ },
+ 'kong-dp1'
+ );
+ }
+ await deletePlugin(pluginId);
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/plugin-ordering.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/plugin-ordering.spec.ts
new file mode 100644
index 00000000..c387b520
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/plugin-ordering.spec.ts
@@ -0,0 +1,404 @@
+import axios from 'axios';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ getNegative,
+ randomString,
+ isGwHybrid,
+ wait,
+ deleteConsumer,
+ createConsumer,
+ createBasicAuthCredentialForConsumer,
+ postNegative,
+ logResponse,
+ retryRequest,
+ waitForConfigRebuild,
+ getMetric,
+ waitForConfigHashUpdate,
+ isGateway,
+} from '@support';
+
+describe('Plugin Ordering: featuring RTA,basic-auth,RV plugins', function () {
+ this.timeout(45000);
+ const path = `/${randomString()}`;
+ const basicAuthPassword = randomString();
+ const isHybrid = isGwHybrid();
+ const waitTime = 5000;
+ const hybridWaitTime = 8000;
+
+ let serviceId: string;
+ let routeId: string;
+ let consumerDetails: any;
+ let plugins: any;
+ let base64credentials: string;
+ let basePayload: any;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ before(async function () {
+ const service = await createGatewayService('pluginOrderingService');
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ const consumer = await createConsumer();
+
+ consumerDetails = {
+ id: consumer.id,
+ username: consumer.username,
+ };
+
+ basePayload = {
+ name: 'rate-limiting-advanced',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should create RT, basic-auth and RV plugins with given ordering', async function () {
+ // create basic-auth plugin with ordering before request-validator
+ const basicAuthPayload = {
+ ...basePayload,
+ name: 'basic-auth',
+ ordering: {
+ before: {
+ access: ['request-validator'],
+ },
+ },
+ };
+
+ let resp = await axios({
+ method: 'post',
+ url,
+ data: basicAuthPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ plugins = {
+ 'basic-auth': resp.data.id,
+ };
+
+ // create basic-auth credentials for a consumer
+ resp = await createBasicAuthCredentialForConsumer(
+ consumerDetails.id,
+ consumerDetails.username,
+ basicAuthPassword
+ );
+ logResponse(resp);
+
+ // base64 encode credentials
+ base64credentials = Buffer.from(
+ `${consumerDetails.username}:${basicAuthPassword}`
+ ).toString('base64');
+
+ // create request transformer advanced plugin with ordering before basic-auth
+ const rtaPayload = {
+ ...basePayload,
+ name: 'request-transformer-advanced',
+ config: {
+ add: {
+ headers: [
+ `Authorization: Basic ${base64credentials}`,
+ 'headerToBeAdded: 5',
+ ],
+ },
+ },
+ ordering: {
+ before: {
+ access: ['basic-auth'],
+ },
+ },
+ };
+
+ resp = await axios({
+ method: 'post',
+ url,
+ data: rtaPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.config.add.headers[0],
+ 'Should have correct add header'
+ ).to.equal(`Authorization: Basic ${base64credentials}`);
+ plugins['rta'] = resp.data.id;
+
+ // create request validator plugin which expects 'headerToBeAdded: ' header with no ordering
+ const rvPayload = {
+ ...basePayload,
+ name: 'request-validator',
+ config: {
+ body_schema: null,
+ verbose_response: true,
+ parameter_schema: [
+ {
+ name: 'headerToBeAdded',
+ in: 'header',
+ required: true,
+ schema: '{"type": "number"}',
+ style: 'simple',
+ explode: true,
+ },
+ ],
+ },
+ };
+
+ resp = await axios({
+ method: 'post',
+ url,
+ data: rvPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ plugins['rv'] = resp.data.id;
+
+ if (isHybrid) {
+ await waitForConfigHashUpdate(
+ await getMetric('kong_data_plane_config_hash')
+ );
+ }
+ });
+
+ it('should apply plugin ordering RTA > basic-auth > RV', async function () {
+ await waitForConfigRebuild();
+ // Current plugin ordering is: RTA > basic-auth > RV
+ const req = () => getNegative(`${proxyUrl}${path}`);
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should patch RTA plugin ordering to become after basic-auth', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${plugins.rta}`,
+ data: {
+ ordering: {
+ after: {
+ access: ['basic-auth'],
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should get 401 error as RTA now runs after basic-auth', async function () {
+ // Current plugin ordering is: basic-auth > RTA > RV
+
+ await waitForConfigRebuild();
+
+ const req = () => getNegative(`${proxyUrl}${path}`);
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should put basic-auth plugin ordering to become after RV', async function () {
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/${plugins['basic-auth']}`,
+ data: {
+ ...basePayload,
+ name: 'basic-auth',
+ ordering: {
+ after: {
+ access: ['request-validator'],
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ // updating RTA plugin to add only 'Authorization' header
+ resp = await axios({
+ method: 'patch',
+ url: `${url}/${plugins.rta}`,
+ data: {
+ config: {
+ add: {
+ headers: [`Authorization: Basic ${base64credentials}`],
+ },
+ },
+ ordering: {
+ before: {
+ access: ['request-validator'],
+ },
+ },
+ },
+ });
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should get 400 error as RTA now runs after basic-auth', async function () {
+ // Current plugin ordering is: RTA > RV > basic-auth
+ await waitForConfigRebuild();
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(
+ resp.data.message,
+ 'Should fail due to non-existing header'
+ ).to.include("header 'headerToBeAdded' validation failed");
+ });
+
+ it('should pass after adding correct header for RV', async function () {
+ // Current plugin ordering is: RTA > RV > basic-auth
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ const resp = await getNegative(`${proxyUrl}${path}`, {
+ headerToBeAdded: '5',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should patch plugins so that RV is the first in order', async function () {
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/${plugins['basic-auth']}`,
+ data: {
+ ...basePayload,
+ name: 'basic-auth',
+ ordering: {
+ after: {
+ access: ['request-transformer-advanced'],
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ resp = await axios({
+ method: 'patch',
+ url: `${url}/${plugins.rta}`,
+ data: {
+ ordering: {
+ after: {
+ access: ['request-validator'],
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should fail as RV now runs the first and header is not in the request', async function () {
+ // Current plugin ordering is: RV > RTA > basic-auth
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ const resp = await getNegative(`${proxyUrl}${path}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(
+ resp.data.message,
+ 'Should fail due to non-existing header'
+ ).to.include("header 'headerToBeAdded' validation failed");
+ });
+
+ it('should fully pass the ordering chain after adding correct header', async function () {
+ // Current plugin ordering is: RV > RTA > basic-auth
+ await wait(waitTime); // eslint-disable-line no-restricted-syntax
+ const resp = await getNegative(`${proxyUrl}${path}`, {
+ headerToBeAdded: '5',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should patch basic-auth plugin ordering to become before all in ordering', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${plugins['basic-auth']}`,
+ data: {
+ ordering: {
+ before: {
+ access: ['request-transformer-advanced'],
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ resp = await axios({
+ method: 'patch',
+ url: `${url}/${plugins.rv}`,
+ data: {
+ ordering: {
+ after: {
+ access: ['basic-auth'],
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ // Current plugin ordering is: basic-auth > RV > RTA
+ await wait(isHybrid ? hybridWaitTime : waitTime); // eslint-disable-line no-restricted-syntax
+ resp = await getNegative(`${proxyUrl}${path}`);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ });
+
+ it('should not be able to create consumer-scoped RLA plugin with ordering', async function () {
+ const rlaPayload = {
+ consumer: {
+ id: consumerDetails.id,
+ },
+ name: 'rate-limiting-advanced',
+ ordering: {
+ before: {
+ access: ['request-validator'],
+ },
+ },
+ };
+
+ const resp = await postNegative(url, rlaPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ "schema violation (ordering: can't apply dynamic reordering to consumer scoped plugins)"
+ );
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteConsumer(consumerDetails.id);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/precedence-model.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/precedence-model.spec.ts
new file mode 100644
index 00000000..5adfd69e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/precedence-model.spec.ts
@@ -0,0 +1,220 @@
+import axios from 'axios';
+import {
+ Environment,
+ expect,
+ getBasePath,
+ createGatewayService,
+ createRouteForService,
+ addConsumerToConsumerGroup,
+ createConsumer,
+ createConsumerGroup,
+ deleteGatewayService,
+ deleteGatewayRoute,
+ deleteConsumer,
+ deleteConsumerGroup,
+ wait,
+ postNegative,
+ logResponse,
+ createPlugin,
+ deletePlugin,
+ getNegative,
+ isGateway,
+} from '@support';
+
+describe.skip('Plugin Scope Precedence Model', () => {
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ const proxyUrl = getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ });
+ let serviceId;
+ let routeId;
+ let consumerId;
+ let consumerGroupId;
+ let basicAuthPluginId;
+ let pluginIdList;
+
+ // array containing each scope array in order of precedence
+ const scopes = [
+ ['consumer', 'route', 'service'],
+ ['consumer_group', 'service', 'route'],
+ ['consumer', 'route'],
+ ['consumer', 'service'],
+ ['consumer_group', 'route'],
+ ['consumer_group', 'service'],
+ ['route', 'service'],
+ ['consumer'],
+ ['consumer_group'],
+ ['route'],
+ ['service'],
+ ['global'],
+ ];
+
+ const scopesConsumerGroups = scopes.filter((scope) => {
+ return scope.includes('consumer_group');
+ });
+
+ const scopesOther = scopes.filter((scope) => {
+ return !scope.includes('consumer_group');
+ });
+
+ before(async function () {
+ const service = await createGatewayService('PrecedenceService');
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, ['/precedence_test']);
+ routeId = route.id;
+ const consumer = await createConsumer();
+ consumerId = consumer.id;
+ const consumerGroup = await createConsumerGroup('PrecedenceGroup');
+ consumerGroupId = consumerGroup.id;
+ await addConsumerToConsumerGroup(consumerId, consumerGroupId);
+
+ // set up basic-auth plugin and add credentials for consumer
+ const basicAuthPlugin = await createPlugin({ name: 'basic-auth' });
+ basicAuthPluginId = basicAuthPlugin.id;
+
+ await axios({
+ url: `${url}/consumers/${consumerId}/basic-auth`,
+ method: 'post',
+ data: {
+ username: 'test',
+ password: 'test',
+ },
+ });
+
+ await wait(3000); // eslint-disable-line no-restricted-syntax
+ });
+
+ function buildPayload(scope) {
+ const payload = {
+ name: 'request-transformer-advanced',
+ config: {
+ add: {
+ headers: [`x-test-header:${scope}`],
+ },
+ },
+ };
+ if (scope.includes('global')) {
+ return payload;
+ } else {
+ if (scope.includes('consumer')) {
+ payload['consumer'] = {
+ id: consumerId,
+ };
+ }
+ if (scope.includes('consumer_group')) {
+ payload['consumer_group'] = {
+ id: consumerGroupId,
+ };
+ }
+ if (scope.includes('route')) {
+ payload['route'] = {
+ id: routeId,
+ };
+ }
+ if (scope.includes('service')) {
+ payload['service'] = {
+ id: serviceId,
+ };
+ }
+ return payload;
+ }
+ }
+
+ async function deletePlugins(idArray) {
+ if (!idArray) return;
+ idArray.map(async (id) => {
+ if (id) await deletePlugin(id);
+ });
+ // wait a couple seconds to ensure deletion
+ await wait(3000); // eslint-disable-line no-restricted-syntax
+ }
+
+ it('should check that consumer group exists', async function () {
+ const resp = await getNegative(`${url}/consumer_groups/${consumerGroupId}`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.consumer_group.name).equal('PrecedenceGroup');
+ });
+
+ //test each combination of consumer group and non-consumer group scopes
+ scopesConsumerGroups.forEach(async (scopeConsumer) => {
+ scopesOther.forEach(async (scopeOther) => {
+ const firstScope =
+ scopes.indexOf(scopeConsumer) < scopes.indexOf(scopeOther)
+ ? scopeConsumer
+ : scopeOther;
+ const secondScope =
+ firstScope === scopeConsumer ? scopeOther : scopeConsumer;
+
+ it(`should check that ${firstScope} takes precedence over ${secondScope}`, async function () {
+ const firstPayload = buildPayload(firstScope);
+ const secondPayload = buildPayload(secondScope);
+
+ const respFirst = await postNegative(`${url}/plugins`, firstPayload);
+ logResponse(respFirst);
+ expect(respFirst.status, 'Status should be 201').equal(201);
+ const firstPluginId = respFirst.data.id;
+
+ const respSecond = await postNegative(`${url}/plugins`, secondPayload);
+ logResponse(respSecond);
+ expect(respSecond.status, 'Status should be 201').equal(201);
+ const secondPluginId = respSecond.data.id;
+
+ pluginIdList = [firstPluginId, secondPluginId];
+
+ await wait(8000); // eslint-disable-line no-restricted-syntax
+
+ const resp = await getNegative(`${proxyUrl}/precedence_test`, {
+ authorization: `Basic ${Buffer.from('test:test').toString('base64')}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+
+ expect(resp.data.headers['X-Test-Header']).equal(firstScope.join(','));
+ });
+ });
+ });
+
+ it('should create one plugin with each scope and ensure [consumer, route, service] takes precedence', async function () {
+ // create list comprehension of payloads for each scope
+ const payloads = scopes.map((scope) => {
+ return buildPayload(scope);
+ });
+
+ // create plugins for each scope
+ payloads.map(async (payload) => {
+ pluginIdList.push((await createPlugin(payload)).id);
+ console.log(pluginIdList);
+ const resp = await postNegative(`${url}/plugins`, payload);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').equal(201);
+ });
+
+ await wait(8000); // eslint-disable-line no-restricted-syntax
+
+ const resp = await getNegative(`${proxyUrl}/precedence_test`, {
+ authorization: `Basic ${Buffer.from('test:test').toString('base64')}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.headers['X-Test-Header']).equal('consumer,route,service');
+ });
+
+ afterEach(async function () {
+ await deletePlugins(pluginIdList);
+ pluginIdList = [];
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteConsumer(consumerId);
+ await deleteConsumerGroup(consumerGroupId);
+ await deletePlugin(basicAuthPluginId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/prometheus.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/prometheus.spec.ts
new file mode 100644
index 00000000..383dab14
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/prometheus.spec.ts
@@ -0,0 +1,431 @@
+import axios from 'axios';
+import {
+ createGatewayService,
+ createRouteForService,
+ Environment,
+ expect,
+ getBasePath,
+ logResponse,
+ waitForConfigRebuild,
+ isGateway,
+ queryPrometheusMetrics,
+ getAllMetrics,
+ eventually,
+ isGwHybrid,
+ createConsumer,
+ createBasicAuthCredentialForConsumer,
+ clearAllKongResources,
+ createPlugin,
+ createUpstream,
+ updateGatewayService,
+ addTargetToUpstream,
+ deletePlugin,
+ deleteConsumer,
+ wait,
+ createPolly
+} from '@support';
+
+describe('Gateway Plugins: Prometheus', function () {
+ const serviceName = 'prometheus-service';
+ const routeName = 'prometheus-route';
+ const routePath = '/api-prom'
+ const upstreamName = 'httpbinUpstream'
+ const target = 'httpbin:80'
+ const targetStates = ['healthchecks_off', 'dns_error', 'healthy', 'unhealthy']
+
+ let serviceId: string;
+ let routeId: string;
+ let pluginId: string;
+ let url: string;
+ let proxyUrl: string;
+ let consumer: any;
+ let pluginPayload: object;
+ let base64credentials: string;
+ let upstreamId: string;
+ let basicAuthPluginId: string;
+
+ before(async function () {
+ url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+ proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [routePath], { name: routeName});
+ routeId = route.id;
+
+ const consumerReq = await createConsumer();
+ consumer = {
+ id: consumerReq.id,
+ username: consumerReq.username,
+ username_lower: consumerReq.username.toLowerCase(),
+ };
+
+ pluginPayload = {
+ name: 'prometheus',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ }
+ }
+ });
+
+ it('should see kong metrics in 8100/metrics', async function () {
+ const kongData = await getAllMetrics()
+ expect(kongData.includes('kong_node_info{node_id='), 'should see kong_node_info in control plane metrics').to.be.true
+
+ if(isGwHybrid()) {
+ const kongDpData = await getAllMetrics("dp")
+ expect(kongDpData.includes('kong_node_info{node_id='), 'should see kong_node_info in data plane metrics').to.be.true
+ }
+ });
+
+ it('should see kong_node_info in prometheus without plugin created', async function () {
+ const nodeData = await queryPrometheusMetrics('kong_node_info')
+
+ for(const result of nodeData.result) {
+ // response differs for classic and hybrid modes as well as during package tests
+ if(isGwHybrid() && result.metric.instance.includes('dp')) {
+ expect(result.metric.job, "should see kong data plane job in prometheus metrics").to.equal('kong-dp1')
+ expect(result.metric.version, "should see kong version in data plane prometheus metrics").to.be.string
+ } else {
+ expect(result.metric.job, "should see kong control plane job in prometheus metrics").to.equal('kong-cp')
+ expect(result.metric.version, "should see kong version in control plane prometheus metrics").to.be.string
+ }
+ }
+ });
+
+ it('should create the prometheus plugin', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ pluginId = resp.data.id;
+
+ await waitForConfigRebuild();
+ });
+
+ it('should see kong_db_entities_total metric after enabling prometheus plugin', async function () {
+ await eventually(async () => {
+ let resp: any
+ // the initial metrics differ in case of traditional and hybrid gateway modes
+ // here we assert that a metric exists which is unique to each gateway mode
+ if(isGwHybrid()) {
+ resp = await queryPrometheusMetrics('kong_data_plane_version_compatible')
+ } else {
+ resp = await queryPrometheusMetrics('kong_db_entities_total')
+ }
+
+ expect(JSON.parse(resp.result[0].value[1]), 'should kong_db_entities_total number').to.be.gte(1)
+ });
+ });
+
+ it('should see kong_enterprise_license_features metric after enabling prometheus plugin', async function () {
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics('kong_enterprise_license_features')
+ expect(resp.result.length).to.equal(2)
+ expect(resp.result[0].metric.feature).to.equal("ee_entity_read")
+ expect(resp.result[1].metric.feature).to.equal("ee_entity_write")
+ });
+ });
+
+ it('should enable the prometheus plugin latency_metrics', async function () {
+ pluginPayload = { ...pluginPayload, config: { latency_metrics: true } }
+
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/plugins/${pluginId}`,
+ data: pluginPayload
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.latency_metrics, 'should see latency_metrics enabled').to.be.true
+ await waitForConfigRebuild();
+
+ // send request to upstream to log request latency metrics
+ resp = await axios(`${proxyUrl}/${routePath}`)
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ // count statistics first
+ ['kong_kong_latency_ms_count', 'kong_request_latency_ms_count', 'kong_upstream_latency_ms_count'].forEach((metric) => {
+ it(`should see the new ${metric}_bucket metric after request to upstream`, async function () {
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics(metric)
+ expect(resp.result[0].metric.service, 'should see correct service name in metrics').to.equal(serviceName)
+ expect(JSON.parse(resp.result[0].value[1]), 'should see the value of count').to.be.gte(1)
+
+ if(metric.includes("upstream")) {
+ // if last one send request to upstream to log request latency metrics
+ const resp = await axios(`${proxyUrl}/${routePath}`)
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ }
+ });
+ });
+ });
+
+ // bucket statistcs next
+ ['kong_kong_latency_ms_bucket', 'kong_request_latency_ms_bucket', 'kong_upstream_latency_ms_bucket'].forEach((metric) => {
+ it(`should see the new ${metric} metric after request to upstream`, async function () {
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics(metric)
+ expect(resp.result[0].metric.service, 'should see correct service name in metrics').to.equal(serviceName)
+ expect(resp.result.length, 'should see multiple latency metadata for buckets').to.be.gte(12)
+ });
+ });
+ })
+
+ it('should enable the prometheus plugin bandwidth_metrics', async function () {
+ pluginPayload = { ...pluginPayload, config: { bandwidth_metrics: true } }
+
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/plugins/${pluginId}`,
+ data: pluginPayload
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.bandwidth_metrics, 'should see bandwidth_metrics enabled').to.be.true
+ await waitForConfigRebuild();
+ });
+
+ it(`should see the new kong_bandwidth_bytes metric after request to upstream`, async function () {
+ this.retries(1)
+ // send request to upstream to log request bandwidth_metrics
+ const resp = await axios(`${proxyUrl}/${routePath}`)
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics('kong_bandwidth_bytes')
+ expect(resp.result.length, 'should see 2 bandwidth results').to.be.gte(2)
+ expect(JSON.parse(resp.result[0].value[1]), 'should see bandwidth value').to.be.gte(1)
+
+ resp.result.some((result) => {
+ expect(result.metric.route, 'should see correct route name').to.equal(routeName)
+ expect(result.metric.service, 'should see correct service name').to.equal(serviceName)
+ })
+ });
+ });
+
+ // skipped due to non-reliable way of testing stream responses
+ it.skip(`should see the new kong_stream_session_total bandwidth metric after request to upstream`, async function () {
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics('kong_stream_session_total')
+ expect(resp.result.length, 'should see stream_session_total results').to.be.gte(2)
+ });
+ });
+
+ it('should enable the prometheus plugin status_code_metrics', async function () {
+ pluginPayload = { ...pluginPayload, config: { status_code_metrics: true } }
+
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/plugins/${pluginId}`,
+ data: pluginPayload
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.status_code_metrics, 'should see status_code_metrics enabled').to.be.true
+ await waitForConfigRebuild();
+ });
+
+ it(`should see the new kong_http_requests_total metric after requests to upstream`, async function () {
+ // send requests to upstream to log request status_code_metrics
+ for(let i = 1; i <= 5; i++) {
+ const resp = await axios(`${proxyUrl}/${routePath}`)
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+
+ const totalValues = new Set()
+
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics('kong_http_requests_total')
+ resp.result.forEach((result) => {
+ totalValues.add(result.value[1])
+ })
+ });
+
+ // expecting at least 5 total requests as we have made 5 requests after enabling the status_code_metrics
+ // the below assertion is made dynamic, meaning if you rerun the test it will find a total greater than 5 from all values
+ const totalExists = Array.from(totalValues).some((value) => Number(value) >= 5)
+ expect(totalExists, 'should see correct kong_http_requests_total value').to.be.true
+ });
+
+ it('should enable the prometheus plugin per_consumer metric', async function () {
+ // Note that for per_consumer metric to work we need status_code_metrics and bandwidth_metrics enabled as well
+ // This is required for kong_http_requests_total and kong_bandwidth_bytes metrics to be exported so that per_consumer can fill consumer label in these
+ pluginPayload = { ...pluginPayload, consumer: { id: consumer.id }, config: { status_code_metrics: true, per_consumer: true, bandwidth_metrics: true } }
+
+ let resp = await axios({
+ method: 'put',
+ url: `${url}/plugins/${pluginId}`,
+ data: pluginPayload
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.status_code_metrics, 'should see status_code_metrics enabled').to.be.true
+ expect(resp.data.config.per_consumer, 'should see per_consumer enabled').to.be.true
+ expect(resp.data.config.bandwidth_metrics, 'should see bandwidth_metrics enabled').to.be.true
+
+ // create basic-auth plugin for consumer
+ const basicAuthResp = await createPlugin({ name: 'basic-auth' });
+ basicAuthPluginId = basicAuthResp.id;
+
+ await createBasicAuthCredentialForConsumer(
+ consumer.id,
+ consumer.username,
+ consumer.username
+ );
+
+ // base64 encode credentials
+ base64credentials = Buffer.from(
+ `${consumer.username}:${consumer.username}`
+ ).toString('base64');
+
+ await waitForConfigRebuild();
+
+ // send requests to upstream to log request upstream_target_health
+ resp = await axios({
+ url: `${proxyUrl}/${routePath}`,
+ headers: {
+ Authorization: `Basic ${base64credentials}`,
+ },
+ })
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+
+ ['kong_http_requests_total', 'kong_bandwidth_bytes'].forEach((metric) => {
+ it(`should see consumer label added in ${metric} metric`, async function () {
+ let foundMatch = false
+
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics(metric)
+ resp.result.some((result) => {
+ if(result.metric.consumer === consumer.username) {
+ foundMatch = true
+ }
+ })
+ expect(foundMatch, `should see correct consumer label in ${metric} metric`).to.be.true
+ });
+ });
+ })
+
+ it('should enable the prometheus plugin upstream_health_metrics', async function () {
+ pluginPayload = { ...pluginPayload, consumer: null, config: { upstream_health_metrics: true } }
+
+ // change service host to point to upstream
+ const serviceResp = await updateGatewayService(serviceId, { host: upstreamName, path: null })
+ expect(serviceResp.host, 'Should have upstream as service host').to.equal(upstreamName);
+
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/plugins/${pluginId}`,
+ data: pluginPayload
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.upstream_health_metrics, 'should see upstream_health_metrics enabled').to.be.true
+
+ // delete basic-auth plugin and consumer
+ await deletePlugin(basicAuthPluginId)
+ await deleteConsumer(consumer.id)
+
+ // create upstream and add target for the subsequent tests
+ const upstream = await createUpstream(upstreamName)
+ upstreamId = upstream.id
+ await addTargetToUpstream(upstreamId, target)
+
+ await waitForConfigRebuild();
+ });
+
+ it(`should see the new kong_upstream_target_health metric exported from kong`, async function () {
+ // send request to upstream to log the request's upstream_target_health metric
+ for(let i = 1; i <= 4; i++) {
+ // eslint-disable-next-line no-restricted-syntax
+ await wait(3000)
+ const resp = await axios({
+ url: `${proxyUrl}/${routePath}`
+ })
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+
+ await eventually(async () => {
+ const kongDpData = await getAllMetrics(isGwHybrid() ? "dp" : "cp")
+ expect(kongDpData.includes('kong_upstream_target_health'), `should see kong_upstream_target_health in kong ${isGwHybrid() ? "dp" : "cp"} metrics`).to.be.true
+ });
+ });
+
+ it(`should see the new kong_upstream_target_health metric in prometheus`, async function () {
+ // send request to upstream to log the request's upstream_target_health metric
+ for(let i = 1; i <= 2; i++) {
+ // eslint-disable-next-line no-restricted-syntax
+ await wait(3000)
+ const resp = await axios({
+ url: `${proxyUrl}/${routePath}`
+ })
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+
+ const promTargetResults = new Set()
+
+ // initiate a new pollyjs mock instance to mock and/or play existing recordings
+ const polly = createPolly('prometheus')
+
+ await eventually(async () => {
+ const resp = await queryPrometheusMetrics('kong_upstream_target_health')
+ expect(resp.result.length, 'should see 4 results for target health').to.be.gte(4)
+ expect(resp.result[0].metric.upstream, 'should see correct upstream name in metrics').to.equal(upstreamName)
+ expect(resp.result[0].metric.target, 'should see correct target address').to.equal(target)
+
+ resp.result.forEach((result) => {
+ promTargetResults.add(result.metric.state)
+ })
+ });
+
+ targetStates.every((state) => {
+ expect(promTargetResults.has(state), `Should see ${state} state in the target health array`).to.be.true
+ })
+
+ // stop the polly mock instance
+ await polly.stop()
+ });
+
+ it('should delete the prometheus plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/plugins/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await clearAllKongResources()
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/rate-limiting-advanced.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/rate-limiting-advanced.spec.ts
new file mode 100644
index 00000000..a4a40e85
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/rate-limiting-advanced.spec.ts
@@ -0,0 +1,268 @@
+import {
+ client,
+ createGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ Environment,
+ expect,
+ getAllKeys,
+ getBasePath,
+ getDbSize,
+ getNegative,
+ getTargetKeyData,
+ isGwHybrid,
+ logResponse,
+ postNegative,
+ resetRedisDB,
+ wait,
+ isGateway,
+ expectRedisFieldsInPlugins
+} from '@support';
+import axios from 'axios';
+
+describe('@gke: Gateway RLA Plugin Tests', function () {
+ this.timeout(30000);
+ const isHybrid = isGwHybrid();
+ const redisNamespace = 'apiRedisTest';
+ const redisUsername = 'redisuser';
+ const redisPassword = 'redispassword';
+ let serviceId: string;
+ let routeId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ const service = await createGatewayService('RlaService');
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId);
+ routeId = route.id;
+
+ basePayload = {
+ name: 'rate-limiting-advanced',
+ service: {
+ id: serviceId,
+ },
+ };
+
+ // connect to redis
+ await client.connect();
+ });
+
+ it('should not create RLA plugin with missing limit and window size', async function () {
+ const resp = await postNegative(url, basePayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ '2 schema violations'
+ );
+ });
+
+ if (isHybrid) {
+ it('should not create RLA plugin with strategy cluster in hybrid mode', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ strategy: 'cluster',
+ limit: [52],
+ window_size: [52],
+ sync_rate: 0,
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ "strategy 'cluster' is not supported with Hybrid deployments"
+ );
+ });
+ }
+
+ it('should not create RLA plugin without sync_rate', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ limit: [52],
+ window_size: [52],
+ strategy: 'redis',
+ redis: {
+ host: 'redis',
+ port: 6379,
+ username: redisUsername,
+ password: redisPassword,
+ },
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'sync_rate is required'
+ );
+ });
+
+ it('should not create RLA plugin with unequal limit and window size arrays', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ limit: [52, 40],
+ window_size: [52],
+ sync_rate: 0,
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ 'same number of windows and limits'
+ );
+ });
+
+ it('should create RLA plugin with correct config', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ limit: [1],
+ window_size: [3600],
+ window_type: 'fixed',
+ sync_rate: 0,
+ strategy: 'redis',
+ namespace: redisNamespace,
+ redis: {
+ host: 'redis',
+ port: 6379,
+ username: redisUsername,
+ password: redisPassword,
+ },
+ },
+ };
+
+ const resp: any = await axios({
+ method: 'post',
+ url,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.name, 'Should have correct plugin name').to.equal(
+ basePayload.name
+ );
+ pluginId = resp.data.id;
+ expect(pluginId, 'Plugin Id should be a string').to.be.string;
+ expect(resp.data.created_at, 'created_at should be a number').to.be.a(
+ 'number'
+ );
+ expect(resp.data.enabled, 'Should have enabled=true').to.be.true;
+ expect(resp.data.config.sync_rate, 'sync_rate should be 0').to.eq(0);
+ expect(resp.data.config.strategy, 'Should have strategy cluster').to.eq(
+ 'redis'
+ );
+ expect(
+ resp.data.config.window_size,
+ 'window_size should be 3600'
+ ).to.be.equalTo([3600]);
+ expect(resp.data.config.limit, 'Should have correct limit').to.be.equalTo([
+ 1,
+ ]);
+ if (resp.data.config.enforce_consumer_groups) {
+ console.log('Checking also consumer groups');
+ expect(
+ resp.data.config.enforce_consumer_groups,
+ 'Should have consumer groups disabled'
+ ).to.be.false;
+ }
+ });
+
+ it('should see correct redis configuration fields in the RLA plugin response', async function () {
+ const resp: any = await axios(url);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expectRedisFieldsInPlugins(resp.data.data[0])
+ });
+
+ it('should rate limit on 2nd request', async function () {
+ await resetRedisDB();
+ await wait(isHybrid ? 8000 : 7000); // eslint-disable-line no-restricted-syntax
+
+ for (let i = 0; i < 2; i++) {
+ const resp: any = await getNegative(`${proxyUrl}/apitest`);
+ logResponse(resp);
+
+ if (i === 1) {
+ expect(resp.status, 'Status should be 429').to.equal(429);
+ } else {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ }
+ }
+ });
+
+ it('should have correct redis dbsize and key data', async function () {
+ const dbSize = await getDbSize({ expectedSize: 1 });
+ expect(dbSize, 'Redis DB size should be 1').equal(1);
+
+ const allKeys: any = await getAllKeys();
+ expect(
+ allKeys[0],
+ 'Should store keys in redis with given namespace'
+ ).to.contain(redisNamespace);
+
+ const { entryCount, host } = await getTargetKeyData(allKeys[0]);
+ expect(
+ entryCount,
+ 'Should see 2 entries in redis key for 2 requests'
+ ).equal('2');
+ expect(host, 'should have host in key metadata').not.to.be.string;
+ });
+
+ it('should have correct redis key data after data update', async function () {
+ await wait(2000); // eslint-disable-line no-restricted-syntax
+ await resetRedisDB();
+
+ for (let i = 0; i < 6; i++) {
+ await getNegative(`${proxyUrl}/apitest`);
+ }
+
+ const dbSize = await getDbSize({ expectedSize: 1 });
+ expect(dbSize, 'Redis DB size should be 1').equal(1);
+
+ const allKeys: any = await getAllKeys();
+ const { entryCount, host } = await getTargetKeyData(allKeys[0]);
+
+ expect(
+ entryCount,
+ 'Should see 6 entries in redis key for 6 requests'
+ ).equal('6');
+ expect(host, 'should have host in key metadata').not.to.be.string;
+ });
+
+ it('should delete the RLA plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ client.quit();
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/request-validator-regression.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/request-validator-regression.spec.ts
new file mode 100644
index 00000000..4ee3a80e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/request-validator-regression.spec.ts
@@ -0,0 +1,364 @@
+import axios from 'axios';
+import { jsonSchemas } from '@fixtures';
+import { execSync } from 'child_process';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ postNegative,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ getNegative,
+ deletePlugin,
+ randomString,
+ isGwHybrid,
+ isLocalDatabase,
+ wait,
+ logResponse,
+ waitForConfigRebuild,
+ isGateway
+} from '@support';
+
+describe('@gke: Gateway Plugins: Request Validator Regression Tests', function () {
+ const path = `/${randomString()}`;
+ const isHybrid = isGwHybrid();
+ const isLocalDb = isLocalDatabase();
+ const waitTime = 5000;
+ const hybridWaitTime = 7000;
+ let serviceId: string;
+ let routeId: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ const service = await createGatewayService(randomString());
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ basePayload = {
+ name: 'request-validator',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+ it('should not create RV Plugin without Body and Parameter Schema', async function () {
+ const resp = await postNegative(url, basePayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `at least one of these fields must be non-empty: 'body_schema', 'parameter_schema'`
+ );
+ });
+
+ it('should not create RV Plugin with non-string body schema', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ body_schema: [{ name: { type: 'string', required: true } }],
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `schema violation (config.body_schema: expected a string)`
+ );
+ });
+
+ it('should create RV plugin with valid draft4 body_schema', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ body_schema: jsonSchemas.maxItems,
+ version: 'draft4',
+ },
+ };
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ pluginId = resp.data.id;
+ });
+
+ it('should pass request body with maxItems 2600 and more', async function () {
+ const arrayOf2600 = Array.from(new Array(2600), () => 'a');
+ await wait(isHybrid ? 12000 : 6000); // eslint-disable-line no-restricted-syntax
+
+ const reqBody = {
+ where: {
+ kpiId: arrayOf2600,
+ },
+ };
+
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ headers: { 'Content-Type': 'application/json' },
+ data: reqBody,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.eq(200);
+ });
+
+ it('should pass request body with duplicate keys', async function () {
+ const reqBody = '{"where":{"kpiId":["a"],"kpiId":["a"]}}';
+ let response: any = execSync(
+ `curl -v -H "Content-Type: application/json" ${proxyUrl}${path} --data '${reqBody}'`,
+ { encoding: 'utf8', stdio: 'pipe' }
+ );
+
+ response = JSON.parse(response.toString());
+
+ // eslint-disable-next-line prettier/prettier
+ expect(response.data, 'Request should pass').to.eq(
+ `{"where":{"kpiId":["a"],"kpiId":["a"]}}`
+ );
+ });
+
+ // existing bug https://konghq.atlassian.net/browse/FTI-2100
+ it.skip('should patch the plugin with extra long body_schema', async function () {
+ const resp = await postNegative(
+ `${url}/${pluginId}`,
+ { config: { body_schema: jsonSchemas.longSchema } },
+ 'patch'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(400);
+ });
+
+ it('should patch the plugin with parameter_schema', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ body_schema: null,
+ verbose_response: true,
+ parameter_schema: [
+ {
+ name: 'kpiId',
+ in: 'query',
+ required: false,
+ schema: jsonSchemas.paramSchema,
+ style: 'form',
+ explode: true,
+ },
+ ],
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.body_schema, 'Should have body_schema null').to.be
+ .null;
+ expect(
+ resp.data.config.parameter_schema[0],
+ 'Should have correct parameter_schema'
+ ).to.haveOwnProperty('explode', true);
+
+ // give some time for changes to take effect and avoid flakiness
+ await wait(1000); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should validate with explode true in parameter_schema and query params', async function () {
+ const queryUrl = `${proxyUrl}${path}?kpiId=ServiceId_marquee-desktop-resource&kpiId=ServiceId_marquee-desktop-resourc2`;
+ const resp = await getNegative(queryUrl, {
+ 'Content-Type': 'application/json',
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should update the request validator plugin with put request', async function () {
+ await wait(isHybrid ? 6000 : 3000); // eslint-disable-line no-restricted-syntax
+
+ const resp = await axios({
+ method: 'put',
+ url: `${url}/${pluginId}`,
+ data: {
+ name: 'request-validator',
+ config: {
+ parameter_schema: null,
+ body_schema: jsonSchemas.dateTime,
+ version: 'draft4',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.body_schema, 'Should have body_schema null').to.eq(
+ jsonSchemas.dateTime
+ );
+ expect(
+ resp.data.config.parameter_schema,
+ 'Should have correct parameter_schema'
+ ).to.be.null;
+ });
+
+ it('should validate invalid date format', async function () {
+ await wait(waitTime + (isLocalDb ? 0 : hybridWaitTime)); // eslint-disable-line no-restricted-syntax
+
+ const resp = await getNegative(
+ `${proxyUrl}${path}`,
+ { 'Content-Type': 'application/json' },
+ { startTime: '20200-06-05T14:48:00.000Z' }
+ );
+ logResponse(resp);
+
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ `request body doesn't conform to schema`
+ );
+ });
+
+ it('should validate correct date format', async function () {
+ await wait(waitTime + 1000); // eslint-disable-line no-restricted-syntax
+
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ headers: { 'Content-Type': 'application/json' },
+ data: { startTime: '2020-06-05T14:48:00.000Z' },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should validate schema with unicode characters in regex and do not throw error', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ body_schema: jsonSchemas.unicode,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ resp = await getNegative(
+ `${proxyUrl}${path}`,
+ { 'Content-Type': 'application/json' },
+ {
+ context: {
+ ip: 'test㍿ㇰㇱ𒐹',
+ },
+ }
+ );
+ logResponse(resp);
+
+ expect(resp.data.message, 'Should have correct error message').to.eq(
+ `request body doesn't conform to schema`
+ );
+ });
+
+ it('should delete the Request Validator plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ it('should create plugin with given body_schema', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ body_schema: jsonSchemas.bodySchema,
+ verbose_response: true,
+ parameter_schema: null,
+ allowed_content_types: null,
+ version: 'draft4',
+ },
+ };
+
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.config.body_schema,
+ 'Should have correct body_schema'
+ ).to.eq(jsonSchemas.bodySchema);
+ pluginId = resp.data.id;
+ });
+
+ it('should support multiple headers with same name', async function () {
+ const pluginPayload = {
+ config: {
+ body_schema: null,
+ parameter_schema: [
+ {
+ name: 'testHeader',
+ in: 'header',
+ required: false,
+ schema:
+ '{"type":"array","items":{"maxLength":512,"minLength":1,"pattern":"^[\\\\w.,-]*$","type":"string"},"maxItems":5,"minItems":0}',
+ style: 'simple',
+ explode: false,
+ },
+ ],
+ version: 'kong',
+ },
+ };
+
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ await waitForConfigRebuild();
+
+ let response: any = execSync(
+ `curl -v ${proxyUrl}${path} -H 'testHeader:www.example.com' -H 'testHeader:www.sample.com' -H 'myHeader:www.test.com'`,
+ { encoding: 'utf8', stdio: 'pipe' }
+ );
+
+ response = JSON.parse(response.toString());
+ expect(
+ response.headers.Testheader,
+ 'Should see multiple headers with same name'
+ ).to.equal('www.example.com,www.sample.com');
+ expect(response.headers.Myheader, 'Should see myheader').to.equal(
+ 'www.test.com'
+ );
+ });
+
+ after(async function () {
+ await deletePlugin(pluginId);
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/request-validator.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/request-validator.spec.ts
new file mode 100644
index 00000000..213b2c6b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/request-validator.spec.ts
@@ -0,0 +1,290 @@
+import axios from 'axios';
+import {
+ expect,
+ Environment,
+ getBasePath,
+ postNegative,
+ createGatewayService,
+ deleteGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ getNegative,
+ randomString,
+ wait,
+ logResponse,
+ waitForConfigHashUpdate,
+ getMetric,
+ isGwHybrid,
+ isGateway,
+} from '@support';
+
+describe('Gateway Plugins: Request Validator', function () {
+ this.timeout(20000);
+ const path = `/${randomString()}`;
+ const isHybrid = isGwHybrid();
+ const paramPath = '~/status/(?[a-z0-9]+)';
+ const classicWait = 5000;
+ let serviceId: string;
+ let routeId: string;
+ let configHash: string;
+
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+ const proxyUrl = `${getBasePath({
+ app: 'gateway',
+ environment: Environment.gateway.proxy,
+ })}`;
+
+ let basePayload: any;
+ let pluginId: string;
+
+ before(async function () {
+ const service = await createGatewayService(randomString(), {
+ url: `http://httpbin`,
+ });
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+
+ basePayload = {
+ name: 'request-validator',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+
+ // configuration hash change can be spotted only in hybrid mode
+ if (isHybrid) {
+ configHash = await getMetric('kong_data_plane_config_hash');
+ }
+ });
+
+ it('should not create RV Plugin with non-array parameter_schema', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ parameter_schema: { name: { type: 'string', required: true } },
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `schema violation (config.parameter_schema: expected an array)`
+ );
+ });
+
+ it('should not create RV Plugin with version other than draft4 or kong', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ body_schema: "[{ name: { type: 'string', required: true } }]",
+ version: 'draft55',
+ },
+ };
+
+ const resp = await postNegative(url, pluginPayload);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `(config.version: expected one of: kong, draft4)`
+ );
+ });
+
+ it('should create plugin with version kong', async function () {
+ const pluginPayload = {
+ ...basePayload,
+ config: {
+ body_schema: '[{"name":{"type": "string", "required": true}}]',
+ verbose_response: true,
+ version: 'kong',
+ },
+ };
+ const resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(
+ resp.data.config.body_schema,
+ 'Should have correct body_schema'
+ ).to.eq('[{"name":{"type": "string", "required": true}}]');
+ expect(resp.data.config.version, 'Should have correct version').to.eq(
+ 'kong'
+ );
+ pluginId = resp.data.id;
+
+ if (isHybrid) {
+ configHash = await waitForConfigHashUpdate(configHash, {
+ targetNumberOfConfigHashChanges: 2,
+ });
+ } else {
+ await wait(classicWait); // eslint-disable-line no-restricted-syntax
+ }
+ });
+
+ it('should validate request body with wrong key', async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${path}`,
+ {
+ 'Content-Type': 'application/json',
+ },
+ { notype: 'test' }
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(
+ resp.data.message.name,
+ 'Should have correct error message'
+ ).to.contain(`required field missing`);
+ });
+
+ it('should validate request body with wrong key type', async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${path}`,
+ {
+ 'Content-Type': 'application/json',
+ },
+ { name: true }
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(
+ resp.data.message.name,
+ 'Should have correct error message'
+ ).to.contain(`expected a string`);
+ });
+
+ it('should not pass request with wrong content-type', async function () {
+ const resp = await getNegative(
+ `${proxyUrl}${path}`,
+ {
+ 'Content-Type': 'text/plain',
+ },
+ { name: true }
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ `specified Content-Type is not allowed`
+ );
+ });
+
+ it('should validate and pass correct request body', async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ data: { name: 'test' },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should update and see request validator plugin config with GET /pluginId', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/${pluginId}`,
+ data: {
+ config: {
+ body_schema: null,
+ version: 'draft4',
+ parameter_schema: [
+ {
+ required: true,
+ style: 'simple',
+ explode: false,
+ schema: '{"type": "number"}',
+ name: 'status_code',
+ in: 'path',
+ },
+ ],
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ if (isHybrid) {
+ configHash = await waitForConfigHashUpdate(configHash);
+ } else {
+ await wait(classicWait); // eslint-disable-line no-restricted-syntax
+ }
+
+ resp = await axios(`${url}/${pluginId}`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.body_schema, 'Should have body_schema null').to.be
+ .null;
+ expect(resp.data.config.version, 'Should have correct version').to.eq(
+ 'draft4'
+ );
+ expect(
+ resp.data.config.parameter_schema[0].name,
+ 'Should have correct parameter_schema name'
+ ).to.eq('status_code');
+ });
+
+ it('should validate wrong path parameter', async function () {
+ let resp = await postNegative(
+ `${url}/${pluginId}/route`,
+ {
+ strip_path: false,
+ paths: [paramPath],
+ },
+ 'patch',
+ {}
+ );
+ logResponse(resp);
+
+ if (isHybrid) {
+ configHash = await waitForConfigHashUpdate(configHash);
+ } else {
+ await wait(classicWait); // eslint-disable-line no-restricted-syntax
+ }
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+
+ resp = await getNegative(`${proxyUrl}/status/abc`, {}, {});
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.contain(
+ "path 'status_code' validation failed, [error] wrong type: expected number, got string"
+ );
+ });
+
+ it('should validate and pass correct path parameter', async function () {
+ const resp = await axios(`${proxyUrl}/status/200`);
+ logResponse(resp);
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should delete the Request Validator plugin', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${pluginId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+ });
+
+ after(async function () {
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/tls-handshake-modifier.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/tls-handshake-modifier.spec.ts
new file mode 100644
index 00000000..8f9cd4cf
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/tls-handshake-modifier.spec.ts
@@ -0,0 +1,208 @@
+import { authDetails } from '@fixtures';
+import * as https from 'https';
+import axios from 'axios';
+import {
+ createGatewayService,
+ createRouteForService,
+ Environment,
+ expect,
+ getBasePath,
+ logResponse,
+ clearAllKongResources,
+ isGateway,
+ waitForConfigRebuild,
+ patchRoute
+} from '@support';
+
+describe('@smoke: Gateway Plugins: TLS Handshake Modifier', function () {
+ const path = '/tls';
+ const serviceName = 'tls-service';
+ const certDn = 'CN=foo@example.com,OU=Gateway,O=Kong,L=Toronto,ST=ON,C=CA'
+ const resolvedIpAddress = '127.0.0.1'
+ const proxyUrls = ['https://f.example.com:8443', 'https://test.example.com:8443']
+ const wrongSniUrl = 'https://f.example.org:8443'
+ const proxyUrlsRightmost = ['https://foo.example.test:8443', 'https://foo.example.org:8443']
+ const wrongSniUrlRightmost = 'https://test.example.org:8443'
+ const certChunk = "MIIDfTCCAmWgAwIBAgIUJCdVEgDy5Wmy6M7GG68L65WrF9UwDQYJKoZIhvcNAQEL"
+
+ let url: string
+ let serviceId: string;
+ let routeId: string;
+ let basePayload: any;
+
+ before(async function () {
+ url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}/plugins`;
+
+ const service = await createGatewayService(serviceName);
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path], {snis: ["*.example.com"]});
+ routeId = route.id;
+
+
+ basePayload = {
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ };
+ });
+
+
+ it('should create tls-handshake-modifier and tls-metadata-headers plugins', async function () {
+ // tls-handshake-modifier is only responsible for requesting the client cert and verifying the client has the corresponding private key
+ // tls-handshake-modifier must be used in conjunction with the tls-metadata-headers plugin
+ // The TLS Metadata Header plugin detects client certificates in requests, retrieves the TLS metadata, such as the URL-encoded client certificate, and proxies this metadata via HTTP headers
+
+ let pluginPayload = {
+ ...basePayload,
+ name: 'tls-handshake-modifier'
+ };
+
+ let resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+
+ pluginPayload = {
+ ...basePayload,
+ name: 'tls-metadata-headers',
+ config: {
+ inject_client_cert_details: true
+ }
+ };
+
+ resp = await axios({ method: 'post', url, data: pluginPayload });
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+
+ await waitForConfigRebuild();
+ });
+
+ proxyUrls.forEach(async (proxyUrl) => {
+ it(`should proxy request with ${proxyUrl} when SNI has leftmost wildcard`, async function () {
+ // equivalent request in curl: curl -k --cert c.crt --key c.key --resolve fo.example.com:8443:127.0.0.1 https://fo.example.com:8443/tls
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.tls.cert,
+ key: authDetails.tls.key,
+ }),
+ proxy: {
+ host: resolvedIpAddress,
+ port: 8443
+ }
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Client-Cert'], 'Should see X-Client-Cert header').to.contain(certChunk)
+ expect(resp.data.headers['X-Client-Cert-Subject-Dn'], 'Should see X-Client-Cert-Subject-Dn header').to.equal(certDn)
+ expect(resp.data.headers['X-Client-Cert-Issuer-Dn'], 'Should see X-Client-Cert-Issuer-Dn header').to.equal(certDn)
+ });
+ })
+
+ it(`should not match route with wrong SNI`, async function () {
+ const resp = await axios({
+ url: `${wrongSniUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.tls.cert,
+ key: authDetails.tls.key,
+ }),
+ validateStatus: null,
+ proxy: {
+ host: resolvedIpAddress,
+ port: 8443
+ }
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should see correct error message').to.equal('no Route matched with those values')
+ });
+
+ it(`should match route but not put the cert in the header when certs do not exist in the request`, async function () {
+ const resp = await axios({
+ url: `${proxyUrls[0]}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ }),
+ proxy: {
+ host: resolvedIpAddress,
+ port: 8443
+ }
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Client-Cert'], 'Should not see X-Client-Cert header').to.not.exist
+ expect(resp.data.headers['X-Client-Cert-Subject-Dn'], 'Should not see X-Client-Cert-Subject-Dn header').to.not.exist
+ expect(resp.data.headers['X-Client-Cert-Issuer-Dn'], 'Should not see X-Client-Cert-Issuer-Dn header').to.not.exist
+ });
+
+ it('should not patch the route SNI to middle wildcard', async function () {
+ const resp = await patchRoute(routeId, { snis: ["foo.*.com"] });
+
+ expect(resp.data.message, 'Should see correct SNI error message').to.contain('wildcard must be leftmost or rightmost character')
+ });
+
+ it('should patch the route SNI to leftmost wildcard', async function () {
+ const resp = await patchRoute(routeId, { snis: ["foo.example.*"] });
+
+ expect(resp.status, 'Status should be 200').equal(200);
+ expect(resp.data.snis[0], 'Should see updated SNI').to.equal('foo.example.*')
+ await waitForConfigRebuild();
+ });
+
+ proxyUrlsRightmost.forEach(async (proxyUrl) => {
+ it(`should proxy request with ${proxyUrl} when SNI has rightmost wildcard`, async function () {
+ const resp = await axios({
+ url: `${proxyUrl}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.tls.cert,
+ key: authDetails.tls.key,
+ }),
+ proxy: {
+ host: resolvedIpAddress,
+ port: 8443
+ }
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.headers['X-Client-Cert'], 'Should see X-Client-Cert header').to.contain(certChunk)
+ expect(resp.data.headers['X-Client-Cert-Subject-Dn'], 'Should see X-Client-Cert-Subject-Dn header').to.equal(certDn)
+ expect(resp.data.headers['X-Client-Cert-Issuer-Dn'], 'Should see X-Client-Cert-Issuer-Dn header').to.equal(certDn)
+ });
+ })
+
+ it(`should not match route with rightmost wrong SNI`, async function () {
+ const resp = await axios({
+ url: `${wrongSniUrlRightmost}${path}`,
+ httpsAgent: new https.Agent({
+ rejectUnauthorized: false,
+ cert: authDetails.tls.cert,
+ key: authDetails.tls.key,
+ }),
+ validateStatus: null,
+ proxy: {
+ host: resolvedIpAddress,
+ port: 8443
+ }
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ expect(resp.data.message, 'Should see correct error message').to.equal('no Route matched with those values')
+ });
+
+ after(async function () {
+ await clearAllKongResources()
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/vault-auth.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/vault-auth.spec.ts
new file mode 100644
index 00000000..ac16ab30
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/plugins/vault-auth.spec.ts
@@ -0,0 +1,490 @@
+import axios from 'axios';
+import {
+ expect,
+ getBasePath,
+ Environment,
+ createGatewayService,
+ createRouteForService,
+ deleteGatewayRoute,
+ deleteGatewayService,
+ getNegative,
+ randomString,
+ wait,
+ createHcvMountWithTargetKvEngine,
+ createVaultAuthVaultEntity,
+ deleteVaultAuthPluginVaultEntity,
+ deleteHcvSecretMount,
+ createConsumer,
+ deleteConsumer,
+ createHcvVaultSecrets,
+ isLocalDatabase,
+ logResponse,
+ retryRequest,
+ waitForCacheInvalidation,
+ isGateway
+} from '@support';
+
+const kvEngineVersions = ['v1', 'v2'];
+const islocalDB = isLocalDatabase();
+
+kvEngineVersions.forEach((kvVersion) => {
+ describe(`Vault-Auth Plugin Tests with HCV kv engine '${kvVersion}'`, function () {
+ let serviceId = '';
+ let routeId = '';
+ let vaPluginId = '';
+ let vaultEntityId = '';
+ let consumerId = '';
+
+ const credentials: any = {};
+ const customCredentials = {
+ access_token: '7pC8fPiL022kX5MZviUXVF0JaHOeb5JG',
+ created_at: 1660654457,
+ secret_token: 'egapdBDCPGUPQJzEXc2Kzy0ktcirIskx',
+ ttl: 1,
+ };
+
+ const vaultName = 'kong-auth';
+ const waitTime = 5000;
+ const longWaitTime = 8000;
+ const path = `/${randomString()}`;
+ const url = `${getBasePath({
+ environment: isGateway() ? Environment.gateway.admin : undefined,
+ })}`;
+
+ const proxyUrl = getBasePath({ environment: isGateway() ? Environment.gateway.proxy : undefined });
+
+ before(async function () {
+ const service = await createGatewayService('VaultAuthService');
+ serviceId = service.id;
+ const route = await createRouteForService(serviceId, [path]);
+ routeId = route.id;
+ const consumer = await createConsumer();
+ consumerId = consumer.id;
+
+ // creating kong-auth kv engine 1 mount in hcv vault
+ await createHcvMountWithTargetKvEngine(
+ vaultName,
+ Number(kvVersion.slice(1))
+ );
+ });
+
+ it(`should create hcv vault entity for kong-auth with kv '${kvVersion}'`, async function () {
+ const resp = await createVaultAuthVaultEntity(
+ vaultName,
+ vaultName,
+ kvVersion
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ vaultEntityId = resp.data.id;
+ expect(vaultEntityId, 'Vault id should be a string').to.be.string;
+ });
+
+ it('should not create hcv vault entity with wrong kv value', async function () {
+ const resp = await createVaultAuthVaultEntity(
+ vaultName,
+ vaultName,
+ 'v111'
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 400').to.equal(400);
+ expect(resp.data.message, 'Should have correct error message').to.include(
+ 'schema violation (kv: expected one of: v1, v2)'
+ );
+ });
+
+ it('should create vault-auth plugin with reference to vault entity', async function () {
+ const pluginPayload = {
+ name: 'vault-auth',
+ service: {
+ id: serviceId,
+ },
+ route: {
+ id: routeId,
+ },
+ config: {
+ vault: {
+ id: vaultEntityId,
+ },
+ },
+ };
+
+ const resp: any = await axios({
+ method: 'post',
+ url: `${url}/plugins`,
+ data: pluginPayload,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ vaPluginId = resp.data.id;
+
+ expect(vaPluginId, 'Plugin Id should be a string').to.be.string;
+ expect(
+ resp.data.config.vault.id,
+ 'Should have correct vault id'
+ ).to.equal(vaultEntityId);
+
+ await wait(islocalDB ? 0 : longWaitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should not proxy a request without access/secret token pair', async function () {
+ const req = () => getNegative(`${proxyUrl}${path}`);
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'should have correct error message').to.eq(
+ 'No access token found'
+ );
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should create access/secret token pair for a consumer', async function () {
+ const resp = await axios({
+ method: 'post',
+ url: `${url}/vault-auth/${vaultName}/credentials/${consumerId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ expect(resp.data.data.access_token, 'should see access_token in response')
+ .to.be.string;
+ expect(resp.data.data.secret_token, 'should see secret_token in response')
+ .to.be.string;
+
+ credentials['access_token'] = resp.data.data.access_token;
+ credentials['secret_token'] = resp.data.data.secret_token;
+
+ await wait(islocalDB ? 0 : longWaitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should proxy a request with correct secrets as querystring parameters', async function () {
+ const req = () =>
+ axios({
+ method: 'get',
+ url: `${proxyUrl}${path}?access_token=${credentials.access_token}&secret_token=${credentials.secret_token}`,
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should proxy a request with correct secrets in request header', async function () {
+ const req = () =>
+ axios({
+ method: 'get',
+ url: `${proxyUrl}${path}`,
+ headers: {
+ access_token: credentials.access_token,
+ secret_token: credentials.secret_token,
+ },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should not proxy a request with incorrect secrets in request header', async function () {
+ const resp = await getNegative(`${proxyUrl}${path}`, {
+ // vault-auth plugin cache authentication results by access_token
+ // make sure to use a different access_token in this request
+ access_token: '11XYyybbu3Ty0Qt4ImIshPGQ0WsvjLzx',
+ secret_token: credentials.secret_token,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'should have correct error message').to.eq(
+ 'Unauthorized'
+ );
+ });
+
+ it('should GET access/secret credentials when they exist', async function () {
+ const resp = await axios(`${url}/vault-auth/kong-auth/credentials`);
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.data.access_token, 'should see access_token in response')
+ .to.be.string;
+ expect(resp.data.data.secret_token, 'should see secret_token in response')
+ .to.be.string;
+ });
+
+ it('should delete access/secret token pair', async function () {
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/vault-auth/${vaultName}/credentials/token/${credentials.access_token}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+
+ await wait(islocalDB ? waitTime : longWaitTime); // eslint-disable-line no-restricted-syntax
+ });
+
+ it('should not see access/secret credentials when they have been deleted', async function () {
+ const resp = await getNegative(`${url}/vault-auth/kong-auth/credentials`);
+ logResponse(resp);
+
+ if (kvVersion === 'v1') {
+ expect(resp.status, 'Status should be 404').to.equal(404);
+ } else {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.data,
+ 'Should not see access/secret token after deletion'
+ ).to.be.empty;
+ }
+ });
+
+ it('should not proxy a request after secrets have been deleted', async function () {
+ await waitForCacheInvalidation(`vault-auth:${credentials.access_token}:${vaultEntityId}`, 8000)
+
+ const resp = await getNegative(
+ `${proxyUrl}${path}?access_token=${credentials.access_token}&secret_token=${credentials.secret_token}`
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'should have correct error message').to.eq(
+ 'Unauthorized'
+ );
+ });
+
+ it('should proxy a request with secrets created directly in HCV', async function () {
+ await createHcvVaultSecrets(
+ {
+ ...customCredentials,
+ consumer: {
+ id: consumerId,
+ },
+ },
+ 'kong-auth',
+ customCredentials.access_token
+ );
+
+ const resp = await axios({
+ method: 'get',
+ url: `${proxyUrl}${path}`,
+ headers: {
+ access_token: customCredentials.access_token,
+ secret_token: customCredentials.secret_token,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.headers['Secret-Token'],
+ 'should see secret token sent to upstream'
+ ).to.equal(customCredentials.secret_token);
+ });
+
+ it('should patch the plugin and hide credentials from upstream', async function () {
+ const resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${vaPluginId}`,
+ data: {
+ config: {
+ hide_credentials: true,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.hide_credentials,
+ 'should see hide_credentials enabled'
+ ).to.be.true;
+
+ const req = () =>
+ axios({
+ method: 'get',
+ url: `${proxyUrl}${path}`,
+ headers: {
+ access_token: customCredentials.access_token,
+ secret_token: customCredentials.secret_token,
+ },
+ });
+
+ const assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.headers,
+ 'should hide secret token from upstream'
+ ).to.not.haveOwnProperty('Secret-Token');
+ expect(
+ resp.data.headers,
+ 'should hide access token from upstream'
+ ).to.not.haveOwnProperty('Access-Token');
+ };
+
+ await retryRequest(req, assertions);
+ });
+
+ it('should read tokens from request body', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${vaPluginId}`,
+ data: {
+ config: {
+ tokens_in_body: true,
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.tokens_in_body,
+ 'should see tokens_in_body enabled'
+ ).to.be.true;
+
+ await wait(islocalDB ? waitTime : longWaitTime); // eslint-disable-line no-restricted-syntax
+
+ resp = await axios({
+ method: 'get',
+ url: `${proxyUrl}${path}`,
+ data: {
+ access_token: customCredentials.access_token,
+ secret_token: customCredentials.secret_token,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should proxy the request with token pair in request body and hide_credentials true', async function () {
+ const resp = await axios({
+ method: 'get',
+ url: `${proxyUrl}${path}`,
+ data: {
+ access_token: customCredentials.access_token,
+ secret_token: customCredentials.secret_token,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should read new token name from request body', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${vaPluginId}`,
+ data: {
+ config: {
+ hide_credentials: false,
+ access_token_name: 'test',
+ secret_token_name: 'foo',
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(
+ resp.data.config.access_token_name,
+ 'should see updated access_token_name'
+ ).to.eq('test');
+
+ await wait(islocalDB ? waitTime : longWaitTime); // eslint-disable-line no-restricted-syntax
+
+ resp = await axios({
+ method: 'get',
+ url: `${proxyUrl}${path}`,
+ data: {
+ test: customCredentials.access_token,
+ foo: customCredentials.secret_token,
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ });
+
+ it('should fail to send request as ttl is reached and secret was updated in hcv', async function () {
+ // updating secret_token in hcv vault while still using the old one in request to make sure ttl works
+ await createHcvVaultSecrets(
+ {
+ ...customCredentials,
+ secret_token: 'updated',
+ consumer: {
+ id: consumerId,
+ },
+ },
+ 'kong-auth',
+ customCredentials.access_token
+ );
+
+ // wait for ttl to expire which is 1 second
+ await wait(2000); // eslint-disable-line no-restricted-syntax
+
+ const resp = await getNegative(
+ `${proxyUrl}${path}`,
+ {},
+ {
+ test: customCredentials.access_token,
+ foo: customCredentials.secret_token,
+ }
+ );
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 401').to.equal(401);
+ expect(resp.data.message, 'should have correct error text').to.equal(
+ 'Invalid secret token'
+ );
+ });
+
+ it('should not proxy the request with wrong vault id in plugin', async function () {
+ let resp = await axios({
+ method: 'patch',
+ url: `${url}/plugins/${vaPluginId}`,
+ data: {
+ config: {
+ access_token_name: 'access_token',
+ secret_token_name: 'secret_token',
+ tokens_in_body: false,
+ vault: {
+ id: '3f840fe4-583b-4747-8a90-adec2e2bbb22',
+ },
+ },
+ },
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.config.vault.id, 'should see updated vault id').to.eq(
+ '3f840fe4-583b-4747-8a90-adec2e2bbb22'
+ );
+
+ await wait(islocalDB ? waitTime : longWaitTime); // eslint-disable-line no-restricted-syntax
+
+ resp = await getNegative(
+ `${proxyUrl}${path}?access_token=${customCredentials.access_token}&secret_token=${customCredentials.secret_token}`
+ );
+ logResponse(resp);
+ expect(resp.status, 'Status should be 500').to.equal(500);
+ });
+
+ after(async function () {
+ await deleteVaultAuthPluginVaultEntity();
+ await deleteHcvSecretMount(vaultName);
+ await deleteGatewayRoute(routeId);
+ await deleteGatewayService(serviceId);
+ await deleteConsumer(consumerId);
+ });
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/status-endpoint.spec.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/status-endpoint.spec.ts
new file mode 100644
index 00000000..a5fbc135
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/gateway/status-endpoint.spec.ts
@@ -0,0 +1,62 @@
+// This test requires `make generate STATUS_LISTEN=true HTTP2=true GW_MODE=hybrid` to pass
+
+import {
+ expect,
+ isGwHybrid,
+ isLocalDatabase,
+ expectStatusReadyEndpointOk,
+ expectStatusReadyEndpoint503,
+ waitForTargetStatus,
+ runDockerContainerCommand,
+ eventually,
+ isGwNative
+} from '@support';
+
+const isHybrid = isGwHybrid();
+const isLocalDb = isLocalDatabase();
+const databaseContainerName = 'kong-ee-database';
+const dpPortNumber = 8101;
+
+const isPackageTest = isGwNative();
+
+// skip tests for package mode due to failures in the last test
+// needs to be investigated why the kong-ee-database throws cert access denied error and doesn't start
+(isPackageTest ? describe.skip : describe)('/status Endpoint tests', function () {
+ before(async function () {
+ if (!isLocalDb) {
+ this.skip();
+ }
+ });
+
+ it('should return 200 OK for CP status when Kong is loaded and ready', async function () {
+ await expectStatusReadyEndpointOk();
+ });
+
+ it('should return 503 for CP status when connection to database is lost', async function () {
+ // Sever connection between Kong and database
+ await runDockerContainerCommand(databaseContainerName, 'stop');
+ await runDockerContainerCommand(databaseContainerName, 'container wait');
+
+ expect(await waitForTargetStatus(503, 10000)).to.equal(true);
+ await expectStatusReadyEndpoint503('failed to connect to database');
+ });
+
+ if (isHybrid) {
+ it('should return 200 in DP status when connection to database is severed', async function () {
+ expect(await waitForTargetStatus(200, 10000, dpPortNumber)).to.equal(true);
+ });
+ }
+
+ it('should return 200 OK for CP status when connection to database is restored', async function () {
+ // Restore connection between Kong and database
+ await runDockerContainerCommand(databaseContainerName, 'start');
+ await eventually(async () => {
+ const containerStatus = JSON.parse(await runDockerContainerCommand(databaseContainerName, "inspect"))
+ expect(typeof containerStatus).to.equal("object")
+ expect(typeof containerStatus[0]).to.equal("object")
+ expect(containerStatus[0]?.State?.Health?.Status).to.equal("healthy")
+ }, 60000, 1000)
+
+ expect(await waitForTargetStatus(200, 10000)).to.equal(true);
+ });
+});
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/koko/_hooks.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/koko/_hooks.ts
new file mode 100644
index 00000000..1828700b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/koko/_hooks.ts
@@ -0,0 +1,34 @@
+import { registerOrgAndAuthenticateAdmin } from '@shared/kauth_workflows';
+import { getDefaultRuntimeGroup, setupKonnectDataPlane } from '@shared/konnect_workflows';
+import { getAuthOptions, removeCertficatesAndKeys, stopAndRemoveTargetContainer, KokoAuthHeaders } from '@support';
+import axios from 'axios';
+
+export const mochaHooks: Mocha.RootHookObject = {
+ beforeAll: async function (this: Mocha.Context) {
+ try {
+ // stop and remove the data plane container if exists
+ stopAndRemoveTargetContainer('konnect-dp1')
+
+ await registerOrgAndAuthenticateAdmin();
+ // get the deafult runtime group id which is being created automatically with the Organization
+ await getDefaultRuntimeGroup();
+
+ const kokoAuthHeaders: KokoAuthHeaders = getAuthOptions()?.headers;
+ Object.entries(kokoAuthHeaders).forEach(([key, value]) => {
+ axios.defaults.headers[key] = value;
+ });
+
+ // setup the local Data Plane for Konnect
+ await setupKonnectDataPlane()
+
+ } catch (error) {
+ console.error(error);
+ process.exit(1);
+ }
+ },
+
+ afterAll: async function (this: Mocha.Context) {
+ // remove the generated certificates and keys for konnect data plane
+ removeCertficatesAndKeys()
+ },
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/auth0_workflows.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/auth0_workflows.ts
new file mode 100644
index 00000000..6f2fc110
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/auth0_workflows.ts
@@ -0,0 +1,69 @@
+import { teams } from '@fixtures';
+import {
+ authenticateUser as authenticateUserUtil,
+ getUserOrganizations as getUserOrganizationsUtil,
+ registerOrganization as registerOrganizationUtil,
+} from '@kong/kauth-test-utils';
+import {
+ expect,
+ getAuth0UserCreds,
+ getEnvironment,
+ randomString,
+ setKAuthCookies,
+ isKAuthV3,
+ App,
+ setOrgName,
+ setOrgId,
+ getOrgId
+} from '@support';
+
+
+export const registerOrganization = async ({
+ username = getAuth0UserCreds().username,
+ password = getAuth0UserCreds().password,
+ orgName = `quality-${randomString()}`,
+} = {}) => {
+ const orgId = await registerOrganizationUtil(
+ username,
+ password,
+ orgName,
+ getEnvironment(),
+ getAppVersion()
+ );
+ setOrgId(orgId);
+ setOrgName(orgName);
+};
+
+export const getUserOrganizations = async ({
+ username = getAuth0UserCreds().username,
+ password = getAuth0UserCreds().password,
+} = {}) => {
+ const orgs = await getUserOrganizationsUtil(
+ username,
+ password,
+ getEnvironment(),
+ getAppVersion()
+ );
+ expect(orgs, 'orgs data array is not empty').to.not.be.empty;
+ return orgs;
+};
+
+export const authenticateUser = async ({
+ username = getAuth0UserCreds().username,
+ password = getAuth0UserCreds().password,
+ orgId = getOrgId(),
+ team = teams.DefaultTeamNames.ORGANIZATION_ADMIN,
+} = {}) => {
+ const { access, refresh } = await authenticateUserUtil(
+ username,
+ password,
+ orgId,
+ getEnvironment(),
+ getAppVersion()
+ );
+ setKAuthCookies(access, refresh, team);
+};
+
+const getAppVersion = () => {
+ return (isKAuthV3() ? App.kauth_v3 : App.kauth_v2) as string;
+};
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/gateway_workflows.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/gateway_workflows.ts
new file mode 100644
index 00000000..a86b27ea
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/gateway_workflows.ts
@@ -0,0 +1,38 @@
+import axios from 'axios';
+import { getUrl, expect, logResponse, waitForConfigRebuild } from '@support';
+import { authDetails } from '@fixtures';
+
+const url = getUrl('licenses');
+const validLicense = authDetails.license.valid;
+
+export const postGatewayEeLicense = async () => {
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: {
+ payload: validLicense,
+ },
+ });
+
+ logResponse(resp);
+ expect(resp.status, 'Status should be 201').to.equal(201);
+ console.log('Gateway EE License was successfully posted');
+
+ // wait until the license is applied
+ await waitForConfigRebuild();
+
+ return resp.data;
+};
+
+export const deleteGatewayEeLicense = async () => {
+ const licenses = await axios(url);
+ const licenseId = licenses.data.data[0].id;
+
+ const resp = await axios({
+ method: 'delete',
+ url: `${url}/${licenseId}`,
+ });
+ logResponse(resp);
+
+ expect(resp.status, 'Status should be 204').to.equal(204);
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/kauth_workflows.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/kauth_workflows.ts
new file mode 100644
index 00000000..c66ffc82
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/kauth_workflows.ts
@@ -0,0 +1,9 @@
+import {
+ authenticateUser as auth0AuthenticateUser,
+ registerOrganization as auth0RegisterOrganization,
+} from './auth0_workflows';
+
+export const registerOrgAndAuthenticateAdmin = async () => {
+ await auth0RegisterOrganization();
+ await auth0AuthenticateUser();
+};
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/konnect_workflows.ts b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/konnect_workflows.ts
new file mode 100644
index 00000000..ae604f13
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/test/shared/konnect_workflows.ts
@@ -0,0 +1,169 @@
+import { teams } from '@fixtures';
+import {
+ RuntimeGroupsApi,
+} from '@kong/khcp-api-client';
+import {
+ App,
+ expect,
+ getApiConfig,
+ getAuthOptions,
+ logResponse,
+ setRuntimeGroupId,
+ Environment,
+ getBasePath,
+ getOrgName,
+ generatePublicPrivateCertificates,
+ getTargetFileContent,
+ deployKonnectDataPlane,
+ getDataPlaneDockerImage,
+ getNegative,
+ retryRequest,
+ setKonnectControlPlaneId
+} from '@support';
+import { validate as uuidValidate } from 'uuid';
+import axios from 'axios';
+
+const getControlPlanesUrl = (endpoint = 'control-planes') => {
+ const basePath = getBasePath({
+ app: 'konnect_v2',
+ environment: Environment.konnect_v2.dev,
+ });
+
+ return `${basePath}/${endpoint}`;
+};
+
+export const getDefaultRuntimeGroup = async () => {
+ let runtimeGroupId;
+ const config = getApiConfig(App.konnect);
+ const api = new RuntimeGroupsApi(config);
+
+ const team = teams.DefaultTeamNames.ORGANIZATION_ADMIN;
+ const options = getAuthOptions(team);
+
+ const request: any = () => api.getManyBase(
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ options
+ );
+
+ const assertions = (resp) => {
+ expect(resp.status, 'response status should be 200').to.equal(200);
+
+ runtimeGroupId = resp.data.data[0].id;
+ expect(uuidValidate(runtimeGroupId), 'runtime group id is a UUID').to.be.true;
+ };
+
+ // the runtime group creation is now async process, need to retry to get the id
+ await retryRequest(request, assertions);
+
+ setRuntimeGroupId(runtimeGroupId);
+ console.info(`\nCurrent Runtime Group id is >>> ${runtimeGroupId} <<<`)
+ console.info(`Current Organization name is >>> ${getOrgName()} <<<`)
+ console.info(`You can access the organization with current user credentials at https://cloud.konghq.tech/us\n`)
+};
+
+/**
+ * Get control plane and telemetry endpoints to launh a Data Plane
+ * @param {object} headers - additional headers to provide with the request
+ * @returns {object}
+ */
+export const getCpAndTelemetryEndpoints = async (headers = {}) => {
+ const controlPlanesUrl = getControlPlanesUrl()
+
+ const resp = await axios({
+ method: 'get',
+ url: controlPlanesUrl,
+ headers,
+ });
+ logResponse(resp)
+
+ expect(resp.status, 'should return status 200').to.equal(200);
+
+ if (resp.data.data.length > 1) {
+ throw new Error('There should be only one control plane in the current Organization')
+ }
+
+ const controlPlaneEndpoint = resp.data.data[0].config.control_plane_endpoint.split('//')[1]
+ const telemetryEndpoint = resp.data.data[0].config.telemetry_endpoint.split('//')[1]
+ const controlPlaneId = resp.data.data[0].id
+
+ return { controlPlaneEndpoint, telemetryEndpoint, controlPlaneId }
+}
+
+/**
+ * Upload the new generated Certificate to Konnect
+ * @param {string} controlPlaneId
+ * @param {string} publicCertificate - public certificate file content
+ * @param {object} headers - optional
+ */
+export const pinNewDpClientCertificate = async (controlPlaneId: string, publicCertificate: string | undefined, headers?: any) => {
+ const url = `${getControlPlanesUrl()}/${controlPlaneId}/dp-client-certificates`
+
+ const resp = await axios({
+ method: 'post',
+ url,
+ data: { "cert": publicCertificate },
+ headers,
+ });
+ logResponse(resp)
+
+ expect(resp.status, 'should return status 201').to.equal(201);
+ return resp.data
+}
+
+/**
+ * Check that Konnect CP and DP have the same config hash
+ * @param {string} controlPlaneId
+ */
+export const checkKonnectCpAndDpConfigHashMatch = async (controlPlaneId) => {
+ let dpNodeId = ''
+
+ // Get all DP nodes associated with the Konnect CP
+ let req = () => getNegative(`${getControlPlanesUrl()}/${controlPlaneId}/nodes`);
+ let assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.items.length, 'Should see at least one DP node in Konnect').to.be.greaterThanOrEqual(1)
+ dpNodeId = resp.data.items[0].id
+ };
+ await retryRequest(req, assertions);
+
+ // Get the Konnect Control Plane config hash
+ const cpConfigHash = await axios(`${getControlPlanesUrl()}/${controlPlaneId}/expected-config-hash`)
+ expect(cpConfigHash.status, 'Status should be 200').to.equal(200);
+ const expectedConfigHash = cpConfigHash.data.expected_hash
+
+ // Get our target DP node config_hash and check if it is equal to the CP config hash
+ req = () => getNegative(`${getControlPlanesUrl()}/${controlPlaneId}/nodes/${dpNodeId}`);
+ assertions = (resp) => {
+ expect(resp.status, 'Status should be 200').to.equal(200);
+ expect(resp.data.item.config_hash, 'Should have matching Konnect and Data Plane config hash').to.eq(expectedConfigHash)
+ };
+ await retryRequest(req, assertions);
+}
+
+export const setupKonnectDataPlane = async () => {
+ // GET the required control_plane_endpoint, telemetry_endpoint and control_plane id
+ const { controlPlaneEndpoint, telemetryEndpoint, controlPlaneId } = await getCpAndTelemetryEndpoints()
+ setKonnectControlPlaneId(controlPlaneId)
+
+ // Generate the keys and the certificate for DP to upload to Konnect
+ await generatePublicPrivateCertificates()
+
+ // read the generated file contents to put that in the docker run code snippet
+ const certContent = getTargetFileContent('certificate.crt')
+ const privateKey = getTargetFileContent('private.pem')
+
+ // Upload the certificate to Konnect
+ await pinNewDpClientCertificate(controlPlaneId, certContent)
+
+ // Start the data plane locally
+ deployKonnectDataPlane(controlPlaneEndpoint, telemetryEndpoint, certContent, privateKey, getDataPlaneDockerImage())
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/tsconfig.json b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/tsconfig.json
new file mode 100644
index 00000000..a76a377a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/kong-api-tests/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "extends": "./node_modules/@tsconfig/recommended/tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@fixtures": [
+ "./fixtures"
+ ],
+ "@scripts/*": [
+ "./scripts/*"
+ ],
+ "@shared/*": [
+ "./test/shared/*"
+ ],
+ "@support": [
+ "./support"
+ ],
+ },
+ "noImplicitAny": false,
+ "resolveJsonModule": true
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/thirdparty-integration/aws/01-vault_spec.lua b/kong-versions/test9.9.9.3/kong/spec-ee/thirdparty-integration/aws/01-vault_spec.lua
new file mode 100644
index 00000000..43a03439
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/thirdparty-integration/aws/01-vault_spec.lua
@@ -0,0 +1,206 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local fmt = string.format
+local pl_file = require "pl.file"
+local helpers = require "spec.helpers"
+
+local aws_region = os.getenv("RUNNER_AWS_REGION")
+local aws_account_id = os.getenv("RUNNER_AWS_ACCOUNT_ID")
+
+for _, strategy in helpers.each_strategy() do
+
+describe("Kong startup with AWS Vault: ", function ()
+ lazy_setup(function()
+ local _ = helpers.get_db_utils()
+ helpers.setenv("AWS_REGION", aws_region)
+ helpers.setenv("KONG_LICENSE_DATA", pl_file.read("spec-ee/fixtures/mock_license.json"))
+ end)
+
+ lazy_teardown(function ()
+ helpers.unsetenv("AWS_REGION")
+ helpers.unsetenv("KONG_LICENSE_DATA")
+ end)
+
+ describe("credential fetching in multiple scenarios: ", function ()
+ it("should start Kong with correct EC2 profile IAM role", function ()
+ assert(helpers.start_kong({
+ database = strategy,
+ nginx_conf = "spec/fixtures/custom_nginx.template",
+ log_level = "{vault://aws/gw-test-vault-test-secret/log_level}",
+ plugins = "bundled",
+ vaults = "bundled",
+ vault_aws_region = aws_region,
+ }, nil, nil, nil))
+
+ finally(function ()
+ assert(helpers.stop_kong())
+ end)
+ end)
+
+ it("should start Kong with role assuming", function ()
+ assert(helpers.start_kong({
+ database = strategy,
+ nginx_conf = "spec/fixtures/custom_nginx.template",
+ log_level = "{vault://aws/gw-test-vault-test-secret/log_level}",
+ plugins = "bundled",
+ vaults = "bundled",
+ vault_aws_region = aws_region,
+ vault_aws_assume_role_arn = fmt("arn:aws:iam::%s:role/gw-test-test-sm-read-role", aws_account_id)
+ }, nil, nil, nil))
+
+ finally(function ()
+ assert(helpers.stop_kong())
+ end)
+ end)
+ end)
+end)
+
+for _, strategy in helpers.each_strategy() do
+ describe("[#".. strategy .. "] " .. "Create AWS Vault via Admin API: ", function ()
+ local proxy_client
+ local admin_client
+
+ helpers.setenv("AWS_REGION", aws_region)
+ helpers.setenv("KONG_LICENSE_DATA", pl_file.read("spec-ee/fixtures/mock_license.json"))
+
+ lazy_setup(function()
+ local bp = helpers.get_db_utils(strategy, {
+ "routes",
+ "services",
+ "plugins",
+ "vaults",
+ }, { "request-transformer-advanced" })
+
+ local service = assert(bp.services:insert {
+ name = "example-service",
+ host = "echo_server",
+ protocol = "http",
+ port = 10001,
+ })
+
+ assert(bp.routes:insert {
+ hosts = { "example.com" },
+ service = service,
+ })
+
+ local fixtures = {
+ dns_mock = helpers.dns_mock.new(),
+ http_mock = {
+ echo_server = [[
+ server {
+ server_name echo_server;
+ listen 10001;
+
+ location ~ "/" {
+ content_by_lua_block {
+ local cjson = require "cjson"
+ local headers = ngx.req.get_headers(0)
+ ngx.say(cjson.encode({headers=headers}))
+ }
+ }
+ }
+ ]]
+ },
+ }
+
+ fixtures.dns_mock:A {
+ name = "echo_server",
+ address = "127.0.0.1",
+ }
+
+ assert(helpers.start_kong({
+ database = strategy,
+ plugins = "bundled, request-transformer-advanced",
+ vaults = "bundled",
+ vault_aws_region = aws_region,
+ nginx_conf = "spec/fixtures/custom_nginx.template",
+ }, nil, nil, fixtures))
+
+ proxy_client = helpers.proxy_client()
+ admin_client = helpers.admin_client()
+ end)
+
+ lazy_teardown(function ()
+ helpers.unsetenv("AWS_REGION")
+ helpers.unsetenv("KONG_LICENSE_DATA")
+ proxy_client:close()
+ admin_client:close()
+ helpers.stop_kong()
+ end)
+
+ it("should create and use plugin with vault secret value correctly", function ()
+ local res = assert(admin_client:send({
+ method = "POST",
+ path = "/plugins",
+ body = {
+ name = "request-transformer-advanced",
+ config = {
+ add = {
+ headers = { "{vault://aws/gw-test-vault-test-secret/request_transformer_add_headers}" },
+ },
+ },
+ },
+ headers = {
+ ["Content-Type"] = "application/json",
+ },
+ }))
+ assert.response(res).has.status(201)
+ local _ = assert.response(res).has.jsonbody()
+
+ helpers.wait_for_all_config_update()
+
+ local res = assert(proxy_client:send({
+ method = "GET",
+ path = "/testpath",
+ headers = {
+ ["Host"] = "example.com",
+ }
+ }))
+
+ assert.response(res).has.status(200)
+ local headers = assert.response(res).has.jsonbody().headers
+ assert.same("secret-value", headers["x-secret-header"])
+ end)
+ end)
+end
+
+describe("Kong Vault Command with AWS Vault: ", function ()
+ lazy_setup(function()
+ helpers.setenv("AWS_REGION", aws_region)
+ helpers.setenv("KONG_LICENSE_DATA", pl_file.read("spec-ee/fixtures/mock_license.json"))
+ end)
+
+ lazy_teardown(function ()
+ helpers.unsetenv("AWS_REGION")
+ helpers.unsetenv("KONG_LICENSE_DATA")
+ helpers.clean_prefix()
+ end)
+
+ it("should fetch correct value", function ()
+ local ok, _, stdout = helpers.kong_exec("vault get aws/gw-test-vault-test-secret/secret_key", {
+ log_level = "error",
+ prefix = helpers.test_conf.prefix,
+ vault_aws_region = aws_region,
+ })
+ assert.matches("secret_value", stdout, nil, true)
+ assert.is_true(ok)
+ end)
+
+ it("should fetch the correct value with role assuming", function ()
+ local ok, _, stdout = helpers.kong_exec("vault get aws/gw-test-vault-test-secret/secret_key", {
+ log_level = "error",
+ prefix = helpers.test_conf.prefix,
+ vault_aws_region = aws_region,
+ vault_aws_assume_role_arn = fmt("arn:aws:iam::%s:role/gw-test-test-sm-read-role", aws_account_id)
+ })
+ assert.matches("secret_value", stdout, nil, true)
+ assert.is_true(ok)
+ end)
+end)
+
+end
diff --git a/kong-versions/test9.9.9.3/kong/spec-ee/thirdparty-integration/aws/README.md b/kong-versions/test9.9.9.3/kong/spec-ee/thirdparty-integration/aws/README.md
new file mode 100644
index 00000000..f19fc929
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec-ee/thirdparty-integration/aws/README.md
@@ -0,0 +1,11 @@
+## Third party integration test - AWS
+
+** Note: The third party integration test on AWS is a new workflow which is not yet in a good shape. The implementation and arrangement of this test is still under discussion and development, so the description in this README only represents the current situation and may change in the future.
+
+The directory holds the integration test suite on a real AWS environment.
+
+Github workflow is `.github/workflows/integration_test_on_aws.yml`.
+
+This integration test now runs on the self hosted runner, which is a group of EC2 instances.
+
+The integration test related AWS resources are located in the repo https://github.com/Kong/terraform-self-hosted-runners, inside the module `gateway-integration-test`.
diff --git a/kong-versions/test9.9.9.3/kong/spec/README.md b/kong-versions/test9.9.9.3/kong/spec/README.md
new file mode 100644
index 00000000..b2fd654e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/README.md
@@ -0,0 +1,31 @@
+Test helpers for Kong (integration) tests
+=========================================
+
+To generate the documentation run the following command in the Kong source tree:
+
+```
+# install ldoc using LuaRocks
+luarocks install ldoc
+
+# generate and open the docs
+cd spec && ldoc . && open docs/index.html && cd ..
+```
+
+## Environment variables
+
+When testing, Kong will ignore the `KONG_xxx` environment variables that are
+usually used to configure it. This is to make sure that the tests run deterministically.
+If this behaviour needs to be overridden, the `KONG_TEST_xxx`
+version of the variable can be used, which will be respected by the Kong test
+instance.
+
+To prevent the test helpers from cleaning the Kong working directory, the
+variable `KONG_TEST_DONT_CLEAN` can be set.
+This comes in handy when inspecting the logs after the tests complete.
+
+When testing with Redis, the environment variable `KONG_SPEC_TEST_REDIS_HOST` can be
+used to specify where the Redis server can be found. If not specified, it will default
+to `127.0.0.1`. This setting is available to tests via `helpers.redis_host`.
+
+The configuration file to be used can be set with `KONG_SPEC_TEST_CONF_PATH`. It can be
+accessed via helpers as `helpers.test_conf_path`.
diff --git a/kong-versions/test9.9.9.3/kong/spec/busted-ci-helper.lua b/kong-versions/test9.9.9.3/kong/spec/busted-ci-helper.lua
new file mode 100644
index 00000000..21ab2b02
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/busted-ci-helper.lua
@@ -0,0 +1,83 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- busted-log-failed.lua
+
+-- Log which test files run by busted had failures or errors in a
+-- file. The file to use for logging is specified in the
+-- FAILED_TEST_FILES_FILE environment variable. This is used to
+-- reduce test rerun times for flaky tests.
+
+
+-- needed before requiring 'socket.unix'
+require 'socket'
+
+local busted = require 'busted'
+local cjson = require 'cjson'
+local socket_unix = require 'socket.unix'
+
+local busted_event_path = os.getenv("BUSTED_EVENT_PATH")
+
+-- Function to recursively copy a table, skipping keys associated with functions
+local function copyTable(original, copied, cache, max_depth, current_depth)
+ copied = copied or {}
+ cache = cache or {}
+ max_depth = max_depth or 5
+ current_depth = current_depth or 1
+
+ if cache[original] then return cache[original] end
+ cache[original] = copied
+
+ for key, value in pairs(original) do
+ if type(value) == "table" then
+ if current_depth < max_depth then
+ copied[key] = copyTable(value, {}, cache, max_depth, current_depth + 1)
+ end
+ elseif type(value) == "userdata" then
+ copied[key] = tostring(value)
+ elseif type(value) ~= "function" then
+ copied[key] = value
+ end
+ end
+
+ return copied
+end
+
+if busted_event_path then
+ local sock = assert(socket_unix())
+ assert(sock:connect(busted_event_path))
+
+ local events = {{ 'suite', 'reset' },
+ { 'suite', 'start' },
+ { 'suite', 'end' },
+ { 'file', 'start' },
+ { 'file', 'end' },
+ { 'test', 'start' },
+ { 'test', 'end' },
+ { 'pending' },
+ { 'failure', 'it' },
+ { 'error', 'it' },
+ { 'failure' },
+ { 'error' }}
+ for _, event in ipairs(events) do
+ busted.subscribe(event, function (...)
+ local args = {}
+ for i, original in ipairs{...} do
+ if type(original) == "table" then
+ args[i] = copyTable(original)
+ elseif type(original) == "userdata" then
+ args[i] = tostring(original)
+ elseif type(original) ~= "function" then
+ args[i] = original
+ end
+ end
+
+ sock:send(cjson.encode({ event = event[1] .. (event[2] and ":" .. event[2] or ""), args = args }) .. "\n")
+ return nil, true --continue
+ end)
+ end
+end
diff --git a/kong-versions/test9.9.9.3/kong/spec/config.ld b/kong-versions/test9.9.9.3/kong/spec/config.ld
new file mode 100644
index 00000000..52af0703
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/config.ld
@@ -0,0 +1,11 @@
+project='Kong test helpers'
+title='Kong test framework'
+description='Test helper functions for Kong (integration) testing'
+format='markdown'
+file={'./helpers.lua', './helpers', '../spec-ee/helpers.lua'}
+dir='docs'
+readme='README.md'
+sort=true
+sort_modules=true
+style='./'
+no_space_before_args=true
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/1.2_custom_nginx.template b/kong-versions/test9.9.9.3/kong/spec/fixtures/1.2_custom_nginx.template
new file mode 100644
index 00000000..dbfa8c15
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/1.2_custom_nginx.template
@@ -0,0 +1,574 @@
+# This is a custom nginx configuration template for Kong specs
+# This is the Kong 1.2 default template
+
+> if nginx_user then
+user ${{NGINX_USER}};
+> end
+worker_processes ${{NGINX_WORKER_PROCESSES}};
+daemon ${{NGINX_DAEMON}};
+
+pid pids/nginx.pid; # mandatory even for custom config templates
+error_log logs/error.log ${{LOG_LEVEL}};
+
+events {}
+
+http {
+> if #proxy_listeners > 0 or #admin_listeners > 0 then
+ error_log logs/error.log ${{LOG_LEVEL}};
+
+> if nginx_optimizations then
+>-- send_timeout 60s; # default value
+>-- keepalive_timeout 75s; # default value
+>-- client_body_timeout 60s; # default value
+>-- client_header_timeout 60s; # default value
+>-- tcp_nopush on; # disabled until benchmarked
+>-- proxy_buffer_size 128k; # disabled until benchmarked
+>-- proxy_buffers 4 256k; # disabled until benchmarked
+>-- proxy_busy_buffers_size 256k; # disabled until benchmarked
+>-- reset_timedout_connection on; # disabled until benchmarked
+> end
+
+ proxy_ssl_server_name on;
+ underscores_in_headers on;
+
+ lua_package_path '${{LUA_PACKAGE_PATH}};;';
+ lua_package_cpath '${{LUA_PACKAGE_CPATH}};;';
+ lua_socket_pool_size ${{LUA_SOCKET_POOL_SIZE}};
+ lua_max_running_timers 4096;
+ lua_max_pending_timers 16384;
+ lua_shared_dict kong 5m;
+ lua_shared_dict kong_db_cache ${{MEM_CACHE_SIZE}};
+> if database == "off" then
+ lua_shared_dict kong_db_cache_2 ${{MEM_CACHE_SIZE}};
+> end
+ lua_shared_dict kong_db_cache_miss 12m;
+> if database == "off" then
+ lua_shared_dict kong_db_cache_miss_2 12m;
+> end
+ lua_shared_dict kong_secrets 5m;
+ lua_shared_dict kong_locks 8m;
+ lua_shared_dict kong_cluster_events 5m;
+ lua_shared_dict kong_healthchecks 5m;
+ lua_shared_dict kong_rate_limiting_counters 12m;
+ lua_shared_dict kong_vaults_hcv 1m;
+ lua_socket_log_errors off;
+> if lua_ssl_trusted_certificate_combined then
+ lua_ssl_trusted_certificate '${{LUA_SSL_TRUSTED_CERTIFICATE_COMBINED}}';
+> end
+ lua_ssl_verify_depth ${{LUA_SSL_VERIFY_DEPTH}};
+
+ lua_shared_dict kong_mock_upstream_loggers 10m;
+
+ # injected nginx_http_* directives
+> for _, el in ipairs(nginx_http_directives) do
+ $(el.name) $(el.value);
+> end
+
+ init_by_lua_block {
+ Kong = require 'kong'
+ Kong.init()
+ }
+
+ init_worker_by_lua_block {
+ Kong.init_worker()
+ }
+
+> if #proxy_listeners > 0 then
+ upstream kong_upstream {
+ server 0.0.0.1;
+ balancer_by_lua_block {
+ Kong.balancer()
+ }
+> if upstream_keepalive and upstream_keepalive > 0 then
+ keepalive ${{UPSTREAM_KEEPALIVE}};
+> end
+ }
+
+ server {
+ server_name kong;
+> for i = 1, #proxy_listeners do
+ listen $(proxy_listeners[i].listener);
+> end
+ error_page 400 404 408 411 412 413 414 417 494 /kong_error_handler;
+ error_page 500 502 503 504 /kong_error_handler;
+
+ access_log logs/access.log;
+
+> if proxy_ssl_enabled then
+> for i = 1, #ssl_cert do
+ ssl_certificate $(ssl_cert[i]);
+ ssl_certificate_key $(ssl_cert_key[i]);
+> end
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_certificate_by_lua_block {
+ Kong.ssl_certificate()
+ }
+> end
+
+ # injected nginx_proxy_* directives
+> for _, el in ipairs(nginx_proxy_directives) do
+ $(el.name) $(el.value);
+> end
+> for i = 1, #trusted_ips do
+ set_real_ip_from $(trusted_ips[i]);
+> end
+
+ location / {
+ default_type '';
+
+ set $ctx_ref '';
+ set $upstream_te '';
+ set $upstream_host '';
+ set $upstream_upgrade '';
+ set $upstream_connection '';
+ set $upstream_scheme '';
+ set $upstream_uri '';
+ set $upstream_x_forwarded_for '';
+ set $upstream_x_forwarded_proto '';
+ set $upstream_x_forwarded_host '';
+ set $upstream_x_forwarded_port '';
+
+ rewrite_by_lua_block {
+ Kong.rewrite()
+ }
+
+ access_by_lua_block {
+ Kong.access()
+ }
+
+ proxy_http_version 1.1;
+ proxy_set_header Host $upstream_host;
+ proxy_set_header Upgrade $upstream_upgrade;
+ proxy_set_header Connection $upstream_connection;
+ proxy_set_header X-Forwarded-For $upstream_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $upstream_x_forwarded_proto;
+ proxy_set_header X-Forwarded-Host $upstream_x_forwarded_host;
+ proxy_set_header X-Forwarded-Port $upstream_x_forwarded_port;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_pass_header Server;
+ proxy_pass_header Date;
+ proxy_ssl_name $upstream_host;
+ proxy_pass $upstream_scheme://kong_upstream$upstream_uri;
+
+ header_filter_by_lua_block {
+ Kong.header_filter()
+ }
+
+ body_filter_by_lua_block {
+ Kong.body_filter()
+ }
+
+ log_by_lua_block {
+ Kong.log()
+ }
+ }
+
+ location = /kong_error_handler {
+ internal;
+ uninitialized_variable_warn off;
+
+ content_by_lua_block {
+ Kong.handle_error()
+ }
+
+ header_filter_by_lua_block {
+ Kong.header_filter()
+ }
+
+ body_filter_by_lua_block {
+ Kong.body_filter()
+ }
+
+ log_by_lua_block {
+ Kong.log()
+ }
+ }
+ }
+> end
+
+> if #admin_listeners > 0 then
+ server {
+ charset UTF-8;
+ server_name kong_admin;
+> for i = 1, #admin_listeners do
+ listen $(admin_listeners[i].listener);
+> end
+
+ access_log logs/admin_access.log;
+
+> if admin_ssl_enabled then
+> for i = 1, #admin_ssl_cert do
+ ssl_certificate $(admin_ssl_cert[i]);
+ ssl_certificate_key $(admin_ssl_cert_key[i]);
+> end
+ ssl_protocols TLSv1.2 TLSv1.3;
+> end
+
+ # injected nginx_admin_* directives
+> for _, el in ipairs(nginx_admin_directives) do
+ $(el.name) $(el.value);
+> end
+
+ location / {
+ default_type application/json;
+ content_by_lua_block {
+ Kong.serve_admin_api()
+ }
+ }
+
+ location /nginx_status {
+ internal;
+ access_log off;
+ stub_status;
+ }
+
+ location /robots.txt {
+ return 200 'User-agent: *\nDisallow: /';
+ }
+ }
+> end
+
+ server {
+ server_name mock_upstream;
+
+ listen 15555;
+ listen 15556 ssl;
+
+> for i = 1, #ssl_cert do
+ ssl_certificate $(ssl_cert[i]);
+ ssl_certificate_key $(ssl_cert_key[i]);
+> end
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ set_real_ip_from 127.0.0.1;
+
+ location / {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ ngx.status = 404
+ return mu.send_default_json_response()
+ }
+ }
+
+ location = / {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({
+ valid_routes = {
+ ["/ws"] = "Websocket echo server",
+ ["/get"] = "Accepts a GET request and returns it in JSON format",
+ ["/xml"] = "Returns a simple XML document",
+ ["/post"] = "Accepts a POST request and returns it in JSON format",
+ ["/response-headers?:key=:val"] = "Returns given response headers",
+ ["/cache/:n"] = "Sets a Cache-Control header for n seconds",
+ ["/anything"] = "Accepts any request and returns it in JSON format",
+ ["/request"] = "Alias to /anything",
+ ["/delay/:duration"] = "Delay the response for seconds",
+ ["/basic-auth/:user/:pass"] = "Performs HTTP basic authentication with the given credentials",
+ ["/status/:code"] = "Returns a response with the specified ",
+ ["/stream/:num"] = "Stream chunks of JSON data via chunked Transfer Encoding",
+ },
+ })
+ }
+ }
+
+ location = /ws {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.serve_web_sockets()
+ }
+ }
+
+ location /get {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_method("GET")
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response()
+ }
+ }
+
+ location /xml {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local xml = [[
+
+
+ Kong, Monolith destroyer.
+
+ ]]
+ return mu.send_text_response(xml, "application/xml")
+ }
+ }
+
+ location /post {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_method("POST")
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response()
+ }
+ }
+
+ location = /response-headers {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_method("GET")
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({}, ngx.req.get_uri_args())
+ }
+ }
+
+ location = /hop-by-hop {
+ content_by_lua_block {
+ local header = ngx.header
+ header["Keep-Alive"] = "timeout=5, max=1000"
+ header["Proxy"] = "Remove-Me"
+ header["Proxy-Connection"] = "close"
+ header["Proxy-Authenticate"] = "Basic"
+ header["Proxy-Authorization"] = "Basic YWxhZGRpbjpvcGVuc2VzYW1l"
+ header["Transfer-Encoding"] = "chunked"
+ header["Content-Length"] = nil
+ header["TE"] = "trailers, deflate;q=0.5"
+ header["Trailer"] = "Expires"
+ header["Upgrade"] = "example/1, foo/2"
+
+ ngx.print("hello\r\n\r\nExpires: Wed, 21 Oct 2015 07:28:00 GMT\r\n\r\n")
+ ngx.exit(200)
+ }
+ }
+
+ location ~ "^/cache/(?\d+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({}, {
+ ["Cache-Control"] = "public, max-age=" .. ngx.var.n,
+ })
+ }
+ }
+
+ location ~ "^/basic-auth/(?[a-zA-Z0-9_]+)/(?.+)$" {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_basic_auth(ngx.var.username,
+ ngx.var.password)
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({
+ authenticated = true,
+ user = ngx.var.username,
+ })
+ }
+ }
+
+ location ~ "^/(request|anything)" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response()
+ }
+ }
+
+ location ~ "^/delay/(?\d{1,3})$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local delay_seconds = tonumber(ngx.var.delay_seconds)
+ if not delay_seconds then
+ return ngx.exit(ngx.HTTP_NOT_FOUND)
+ end
+
+ ngx.sleep(delay_seconds)
+
+ return mu.send_default_json_response({
+ delay = delay_seconds,
+ })
+ }
+ }
+
+ location ~ "^/status/(?\d{3})$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local code = tonumber(ngx.var.code)
+ if not code then
+ return ngx.exit(ngx.HTTP_NOT_FOUND)
+ end
+ ngx.status = code
+ return mu.send_default_json_response({
+ code = code,
+ })
+ }
+ }
+
+ location ~ "^/stream/(?\d+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local rep = tonumber(ngx.var.num)
+ local res = require("cjson").encode(mu.get_default_json_response())
+
+ ngx.header["X-Powered-By"] = "mock_upstream"
+ ngx.header["Content-Type"] = "application/json"
+
+ for i = 1, rep do
+ ngx.say(res)
+ end
+ }
+ }
+
+ location ~ "^/post_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.store_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/post_auth_log/(?[a-z0-9_]+)/(?[a-zA-Z0-9_]+)/(?.+)$" {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_basic_auth(ngx.var.username,
+ ngx.var.password)
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.store_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/read_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.retrieve_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/count_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.count_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/reset_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.reset_log(ngx.var.logname)
+ }
+ }
+ }
+
+ include '*.http_mock';
+
+> end
+}
+
+> if #stream_listeners > 0 then
+stream {
+ log_format basic '$remote_addr [$time_local] '
+ '$protocol $status $bytes_sent $bytes_received '
+ '$session_time';
+
+ lua_package_path '${{LUA_PACKAGE_PATH}};;';
+ lua_package_cpath '${{LUA_PACKAGE_CPATH}};;';
+ lua_shared_dict stream_kong 5m;
+ lua_shared_dict stream_kong_db_cache ${{MEM_CACHE_SIZE}};
+> if database == "off" then
+ lua_shared_dict stream_kong_db_cache_2 ${{MEM_CACHE_SIZE}};
+> end
+ lua_shared_dict stream_kong_db_cache_miss 12m;
+> if database == "off" then
+ lua_shared_dict stream_kong_db_cache_miss_2 12m;
+> end
+ lua_shared_dict stream_kong_locks 8m;
+ lua_shared_dict stream_kong_cluster_events 5m;
+ lua_shared_dict stream_kong_healthchecks 5m;
+ lua_shared_dict stream_kong_rate_limiting_counters 12m;
+ lua_shared_dict stream_prometheus_metrics 5m;
+ lua_shared_dict stream_kong_vaults_hcv 1m;
+
+ # injected nginx_stream_* directives
+> for _, el in ipairs(nginx_stream_directives) do
+ $(el.name) $(el.value);
+> end
+
+ init_by_lua_block {
+ -- shared dictionaries conflict between stream/http modules. use a prefix.
+ local shared = ngx.shared
+ ngx.shared = setmetatable({}, {
+ __index = function(t, k)
+ return shared["stream_"..k]
+ end,
+ })
+
+ Kong = require 'kong'
+ Kong.init()
+ }
+
+ init_worker_by_lua_block {
+ Kong.init_worker()
+ }
+
+ upstream kong_upstream {
+ server 0.0.0.1:1;
+ balancer_by_lua_block {
+ Kong.balancer()
+ }
+ }
+
+ server {
+> for i = 1, #stream_listeners do
+ listen $(stream_listeners[i].listener);
+> end
+
+ access_log logs/access.log basic;
+ error_log logs/error.log debug;
+
+> for i = 1, #trusted_ips do
+ set_real_ip_from $(trusted_ips[i]);
+> end
+
+ # injected nginx_sproxy_* directives
+> for _, el in ipairs(nginx_sproxy_directives) do
+ $(el.name) $(el.value);
+> end
+
+> if ssl_preread_enabled then
+ ssl_preread on;
+> end
+
+ preread_by_lua_block {
+ Kong.preread()
+ }
+
+ proxy_pass kong_upstream;
+
+ log_by_lua_block {
+ Kong.log()
+ }
+ }
+
+ server {
+ listen 15557;
+ listen 15558 ssl;
+
+> for i = 1, #ssl_cert do
+ ssl_certificate $(ssl_cert[i]);
+ ssl_certificate_key $(ssl_cert_key[i]);
+> end
+ ssl_protocols TLSv1.2;
+
+ content_by_lua_block {
+ local sock = assert(ngx.req.socket(true))
+ local data = sock:receive() -- read a line from downstream
+ ngx.say(data) -- echo whatever was sent
+ }
+ }
+
+ include '*.stream_mock';
+
+}
+> end
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/admin_api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/admin_api.lua
new file mode 100644
index 00000000..c0aac818
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/admin_api.lua
@@ -0,0 +1,113 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local blueprints = require "spec.fixtures.blueprints"
+local helpers = require "spec.helpers"
+local cjson = require "cjson"
+
+
+local prefix = ""
+
+
+local function api_send(method, path, body, forced_port)
+ local api_client = helpers.admin_client(nil, forced_port)
+ local res, err = api_client:send({
+ method = method,
+ path = prefix .. path,
+ headers = {
+ ["Content-Type"] = "application/json"
+ },
+ body = body,
+ })
+ if not res then
+ api_client:close()
+ return nil, err
+ end
+
+ if res.status == 204 then
+ api_client:close()
+ return nil
+ end
+
+ local resbody = res:read_body()
+ api_client:close()
+ if res.status < 300 then
+ return cjson.decode(resbody)
+ end
+
+ return nil, "Error " .. tostring(res.status) .. ": " .. resbody
+end
+
+
+local admin_api_as_db = {}
+
+
+for name, dao in pairs(helpers.db.daos) do
+ local admin_api_name = dao.schema.admin_api_name or name
+ admin_api_as_db[name] = {
+ insert = function(_, tbl)
+ return api_send("POST", "/" .. admin_api_name, tbl)
+ end,
+ remove = function(_, tbl)
+ return api_send("DELETE", "/" .. admin_api_name .. "/" .. tbl.id)
+ end,
+ update = function(_, id, tbl)
+ return api_send("PATCH", "/" .. admin_api_name .. "/" .. id, tbl)
+ end,
+ truncate = function()
+ repeat
+ local res = api_send("GET", "/" .. admin_api_name)
+ assert(type(res) == "table" and type(res.data) == "table")
+ for _, entity in ipairs(res.data) do
+ local _, err = admin_api_as_db[name]:remove(entity)
+ if err then
+ return nil, err
+ end
+ end
+ until #res.data == 0
+
+ return true
+ end,
+ }
+end
+
+
+admin_api_as_db["basicauth_credentials"] = {
+ insert = function(_, tbl)
+ return api_send("POST", "/consumers/" .. tbl.consumer.id .. "/basic-auth", tbl)
+ end,
+ remove = function(_, tbl)
+ return api_send("DELETE", "/consumers/" .. tbl.consumer.id .. "/basic-auth/" .. tbl.id)
+ end,
+ update = function(_, id, tbl)
+ return api_send("PATCH", "/consumers/" .. tbl.consumer.id .. "/basic-auth/" .. id, tbl)
+ end,
+}
+
+admin_api_as_db["targets"] = {
+ insert = function(_, tbl)
+ return api_send("POST", "/upstreams/" .. tbl.upstream.id .. "/targets", tbl)
+ end,
+ remove = function(_, tbl)
+ return api_send("DELETE", "/upstreams/" .. tbl.upstream.id .. "/targets/" .. tbl.id)
+ end,
+ update = function(_, id, tbl)
+ return api_send("PATCH", "/upstreams/" .. tbl.upstream.id .. "/targets/" .. id, tbl)
+ end,
+}
+
+
+local bp = blueprints.new(admin_api_as_db)
+
+
+function bp.set_prefix(p)
+ prefix = p
+end
+
+
+return bp
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/bad_request.json
new file mode 100644
index 00000000..99c40c7b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/bad_request.json
@@ -0,0 +1,12 @@
+{
+ "segassem":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good-stream.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good-stream.json
new file mode 100644
index 00000000..c05edd15
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good-stream.json
@@ -0,0 +1,13 @@
+{
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "stream": true
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good.json
new file mode 100644
index 00000000..4cc10281
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good.json
@@ -0,0 +1,12 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good_own_model.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good_own_model.json
new file mode 100644
index 00000000..9c27eaa1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/requests/good_own_model.json
@@ -0,0 +1,13 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "model": "try-to-override-the-model"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/bad_request.json
new file mode 100644
index 00000000..e4d33de0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/bad_request.json
@@ -0,0 +1,6 @@
+{
+ "error": {
+ "type": "invalid_request_error",
+ "message": "Invalid request"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/bad_upstream_response.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/bad_upstream_response.json
new file mode 100644
index 00000000..3bf212bd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/bad_upstream_response.json
@@ -0,0 +1,10 @@
+{
+ "nothing_object": {
+ "not_interesting_tag_names": "bad string",
+ "and_an_array": [
+ "because",
+ "why",
+ "not"
+ ]
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/good.json
new file mode 100644
index 00000000..174220a4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/good.json
@@ -0,0 +1,15 @@
+{
+ "content": [
+ {
+ "text": "The sum of 1 + 1 is 2.",
+ "type": "text"
+ }
+ ],
+ "model": "claude-2.1",
+ "stop_reason": "end_turn",
+ "stop_sequence": "string",
+ "usage": {
+ "input_tokens": 0,
+ "output_tokens": 0
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/internal_server_error.html b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/internal_server_error.html
new file mode 100644
index 00000000..4b37ec9f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/internal_server_error.html
@@ -0,0 +1,11 @@
+
+
+
+ Fake Internal Server Error
+
+
+
+
This is a fake Internal Server Error
+ It has come from your Mock AI server.
+
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/malformed_usage_response.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/malformed_usage_response.json
new file mode 100644
index 00000000..0a8ec4da
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/malformed_usage_response.json
@@ -0,0 +1,15 @@
+{
+ "content": [
+ {
+ "text": "The sum of 1 + 1 is 2.",
+ "type": "text"
+ }
+ ],
+ "model": "claude-2.1",
+ "stop_reason": "end_turn",
+ "stop_sequence": "string",
+ "usage": {
+ "foo": 0,
+ "bar": 0
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/no_usage_response.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/no_usage_response.json
new file mode 100644
index 00000000..6f10d884
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/no_usage_response.json
@@ -0,0 +1,11 @@
+{
+ "content": [
+ {
+ "text": "The sum of 1 + 1 is 2.",
+ "type": "text"
+ }
+ ],
+ "model": "claude-2.1",
+ "stop_reason": "end_turn",
+ "stop_sequence": "string"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/unauthorized.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/unauthorized.json
new file mode 100644
index 00000000..a3f286ac
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-chat/responses/unauthorized.json
@@ -0,0 +1,6 @@
+{
+ "error": {
+ "type": "authentication_error",
+ "message": "Invalid API Key"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/requests/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/requests/bad_request.json
new file mode 100644
index 00000000..795ead50
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/requests/bad_request.json
@@ -0,0 +1,3 @@
+{
+ "tpmorp": "bad prompt?"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/requests/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/requests/good.json
new file mode 100644
index 00000000..d66bba88
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/requests/good.json
@@ -0,0 +1,3 @@
+{
+ "prompt": "What are you?"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/bad_request.json
new file mode 100644
index 00000000..e4d33de0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/bad_request.json
@@ -0,0 +1,6 @@
+{
+ "error": {
+ "type": "invalid_request_error",
+ "message": "Invalid request"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/good.json
new file mode 100644
index 00000000..6214582e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/good.json
@@ -0,0 +1,5 @@
+{
+ "completion": " Hello! My name is Claude.",
+ "stop_reason": "stop_sequence",
+ "model": "claude-2.1"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/unauthorized.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/unauthorized.json
new file mode 100644
index 00000000..a3f286ac
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/llm-v1-completions/responses/unauthorized.json
@@ -0,0 +1,6 @@
+{
+ "error": {
+ "type": "authentication_error",
+ "message": "Invalid API Key"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/request-transformer/response-in-json.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/request-transformer/response-in-json.json
new file mode 100644
index 00000000..963e77d6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/anthropic/request-transformer/response-in-json.json
@@ -0,0 +1,18 @@
+{
+ "content": [
+ {
+ "text": "{\n \"persons\": [\n {\n \"name\": \"Kong A\",\n \"age\": 62\n },\n {\n \"name\": \"Kong B\",\n \"age\": 84\n }\n ]\n }\n",
+ "type": "text"
+ }
+ ],
+ "id": "msg_013Zva2CMHLNnXjNJJKqJ2EF",
+ "model": "claude-2.1",
+ "role": "assistant",
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "type": "message",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 25
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/azure/request-transformer/response-in-json.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/azure/request-transformer/response-in-json.json
new file mode 100644
index 00000000..cc8f792c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/azure/request-transformer/response-in-json.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": " {\n \"persons\": [\n {\n \"name\": \"Kong A\",\n \"age\": 62\n },\n {\n \"name\": \"Kong B\",\n \"age\": 84\n }\n ]\n }\n",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1701947430,
+ "id": "chatcmpl-8T6YwgvjQVVnGbJ2w8hpOA17SeNy2",
+ "model": "gpt-3.5-turbo-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 12,
+ "prompt_tokens": 25,
+ "total_tokens": 37
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/bad_request.json
new file mode 100644
index 00000000..99c40c7b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/bad_request.json
@@ -0,0 +1,12 @@
+{
+ "segassem":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good-stream.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good-stream.json
new file mode 100644
index 00000000..c05edd15
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good-stream.json
@@ -0,0 +1,13 @@
+{
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "stream": true
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good.json
new file mode 100644
index 00000000..4cc10281
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good.json
@@ -0,0 +1,12 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good_own_model.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good_own_model.json
new file mode 100644
index 00000000..9c27eaa1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/requests/good_own_model.json
@@ -0,0 +1,13 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "model": "try-to-override-the-model"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/bad_request.json
new file mode 100644
index 00000000..e0894287
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/bad_request.json
@@ -0,0 +1,3 @@
+{
+ "message": "bad request"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/bad_upstream_response.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/bad_upstream_response.json
new file mode 100644
index 00000000..3bf212bd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/bad_upstream_response.json
@@ -0,0 +1,10 @@
+{
+ "nothing_object": {
+ "not_interesting_tag_names": "bad string",
+ "and_an_array": [
+ "because",
+ "why",
+ "not"
+ ]
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/good.json
new file mode 100644
index 00000000..02dc99ab
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/good.json
@@ -0,0 +1,19 @@
+{
+ "text": "The sum of 1 + 1 is 2.",
+ "generation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "token_count": {
+ "billed_tokens": 339,
+ "prompt_tokens": 102,
+ "response_tokens": 258,
+ "total_tokens": 360
+ },
+ "meta": {
+ "api_version": {
+ "version": "1"
+ },
+ "billed_units": {
+ "input_tokens": 81,
+ "output_tokens": 258
+ }
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/internal_server_error.html b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/internal_server_error.html
new file mode 100644
index 00000000..4b37ec9f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/internal_server_error.html
@@ -0,0 +1,11 @@
+
+
+
+ Fake Internal Server Error
+
+
+
+
This is a fake Internal Server Error
+ It has come from your Mock AI server.
+
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/unauthorized.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/unauthorized.json
new file mode 100644
index 00000000..a27b4297
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-chat/responses/unauthorized.json
@@ -0,0 +1,3 @@
+{
+ "message": "invalid api token"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/requests/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/requests/bad_request.json
new file mode 100644
index 00000000..795ead50
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/requests/bad_request.json
@@ -0,0 +1,3 @@
+{
+ "tpmorp": "bad prompt?"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/requests/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/requests/good.json
new file mode 100644
index 00000000..d66bba88
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/requests/good.json
@@ -0,0 +1,3 @@
+{
+ "prompt": "What are you?"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/bad_request.json
new file mode 100644
index 00000000..e4d33de0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/bad_request.json
@@ -0,0 +1,6 @@
+{
+ "error": {
+ "type": "invalid_request_error",
+ "message": "Invalid request"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/good.json
new file mode 100644
index 00000000..f0dbde41
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/good.json
@@ -0,0 +1,34 @@
+{
+ "id": "string",
+ "prompt": "string",
+ "generations": [
+ {
+ "id": "123",
+ "text": "1 + 1 is 2.",
+ "index": 0,
+ "likelihood": 0,
+ "token_likelihoods": [
+ {
+ "token": "string",
+ "likelihood": 1.0
+ }
+ ]
+ }
+ ],
+ "meta": {
+ "api_version": {
+ "version": "string",
+ "is_deprecated": true,
+ "is_experimental": true
+ },
+ "billed_units": {
+ "input_tokens": 0,
+ "output_tokens": 0,
+ "search_units": 0,
+ "classifications": 0
+ },
+ "warnings": [
+ "string"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/unauthorized.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/unauthorized.json
new file mode 100644
index 00000000..a27b4297
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/llm-v1-completions/responses/unauthorized.json
@@ -0,0 +1,3 @@
+{
+ "message": "invalid api token"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/request-transformer/response-in-json.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/request-transformer/response-in-json.json
new file mode 100644
index 00000000..beda83d6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/cohere/request-transformer/response-in-json.json
@@ -0,0 +1,19 @@
+{
+ "text": "{\n \"persons\": [\n {\n \"name\": \"Kong A\",\n \"age\": 62\n },\n {\n \"name\": \"Kong B\",\n \"age\": 84\n }\n ]\n }\n",
+ "generation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "token_count": {
+ "billed_tokens": 339,
+ "prompt_tokens": 102,
+ "response_tokens": 258,
+ "total_tokens": 360
+ },
+ "meta": {
+ "api_version": {
+ "version": "1"
+ },
+ "billed_units": {
+ "input_tokens": 81,
+ "output_tokens": 258
+ }
+ }
+ }
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/json-schema.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/json-schema.json
new file mode 100644
index 00000000..ff255e86
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/json-schema.json
@@ -0,0 +1,65 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "oneOf": [
+ {
+ "$ref": "#/definitions/llm-v1-completions"
+ },
+ {
+ "$ref": "#/definitions/llm-v1-chat"
+ }
+ ],
+ "definitions": {
+ "llm-v1-completions": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "prompt": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "prompt"
+ ],
+ "title": "llm-v1-completions"
+ },
+ "llm-v1-chat": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "messages": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/message"
+ }
+ },
+ "id": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "messages"
+ ],
+ "title": "llm-v1-chat"
+ },
+ "message": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "role": {
+ "type": "string"
+ },
+ "content": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "content",
+ "role"
+ ],
+ "title": "message"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/ollama/chat-stream.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/ollama/chat-stream.json
new file mode 100644
index 00000000..790bb707
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/ollama/chat-stream.json
@@ -0,0 +1,13 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "stream": true
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/requests/good-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/requests/good-chat.json
new file mode 100644
index 00000000..9942f41c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/requests/good-chat.json
@@ -0,0 +1,20 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a video game knowledgebase."
+ },
+ {
+ "role": "user",
+ "content": "What is Missingno.?"
+ },
+ {
+ "role": "system",
+ "content": "Missingno. is a weird character from a popular game."
+ },
+ {
+ "role": "user",
+ "content": "Why is it popular?"
+ }
+ ]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/requests/good-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/requests/good-completions.json
new file mode 100644
index 00000000..286c7137
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/requests/good-completions.json
@@ -0,0 +1,3 @@
+{
+ "prompt": "What is Missingno.?"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/bad_request.json
new file mode 100644
index 00000000..6035fef9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/bad_request.json
@@ -0,0 +1,3 @@
+{
+ "error": "some error"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/good.json
new file mode 100644
index 00000000..a94180dc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/good.json
@@ -0,0 +1,7 @@
+{
+ "data": [
+ {
+ "generated_text": "[INST]\nWhat is Sans? ?\n[/INST]\n\nIs a well known font."
+ }
+ ]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/unauthorized.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/unauthorized.json
new file mode 100644
index 00000000..5471f480
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/raw/responses/unauthorized.json
@@ -0,0 +1,3 @@
+{
+ "error": "Model requires a Pro subscription."
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/request-transformer/response-in-json.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/request-transformer/response-in-json.json
new file mode 100644
index 00000000..7a433236
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/llama2/request-transformer/response-in-json.json
@@ -0,0 +1,7 @@
+{
+ "data": [
+ {
+ "generated_text": "[INST]\nWhat is Sans? ?\n[/INST]\n\n{\n \"persons\": [\n {\n \"name\": \"Kong A\",\n \"age\": 62\n },\n {\n \"name\": \"Kong B\",\n \"age\": 84\n }\n ]\n }\n"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/llm-v1-chat/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/llm-v1-chat/responses/good.json
new file mode 100644
index 00000000..755abcf0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/llm-v1-chat/responses/good.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "The sum of 1 + 1 is 2.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1701947430,
+ "id": "chatcmpl-8T6YwgvjQVVnGbJ2w8hpOA17SeNy2",
+ "model": "mistralai/Mistral-7B-Instruct-v0.1-instruct",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 12,
+ "prompt_tokens": 25,
+ "total_tokens": 37
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/llm-v1-completions/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/llm-v1-completions/responses/good.json
new file mode 100644
index 00000000..c2248445
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/llm-v1-completions/responses/good.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "length",
+ "index": 0,
+ "logprobs": null,
+ "text": "\n\nI am a language model AI created by OpenAI. I can answer questions"
+ }
+ ],
+ "created": 1701967000,
+ "id": "cmpl-8TBeaJVQIhE9kHEJbk1RnKzgFxIqN",
+ "model": "mistralai/Mistral-7B-Instruct-v0.1-instruct",
+ "object": "text_completion",
+ "usage": {
+ "completion_tokens": 16,
+ "prompt_tokens": 4,
+ "total_tokens": 20
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/request-transformer/response-in-json.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/request-transformer/response-in-json.json
new file mode 100644
index 00000000..754883eb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/mistral/request-transformer/response-in-json.json
@@ -0,0 +1,16 @@
+{
+ "model": "mistral-tiny",
+ "created_at": "2024-01-15T08:13:38.876196Z",
+ "message": {
+ "role": "assistant",
+ "content": " {\n \"persons\": [\n {\n \"name\": \"Kong A\",\n \"age\": 62\n },\n {\n \"name\": \"Kong B\",\n \"age\": 84\n }\n ]\n }\n"
+ },
+ "done": true,
+ "total_duration": 4062418334,
+ "load_duration": 1229365792,
+ "prompt_eval_count": 26,
+ "prompt_eval_duration": 167969000,
+ "eval_count": 100,
+ "eval_duration": 2658646000
+ }
+
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/oas.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/oas.yaml
new file mode 100644
index 00000000..a020b440
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/oas.yaml
@@ -0,0 +1,207 @@
+openapi: 3.0.1
+info:
+ title: AI-Proxy Plugin Schema
+ description: AI-Proxy Plugin objects (and samples) for Kong Gateway LLM integration.
+ version: 0.0.1
+servers:
+- url: 'https://localhost:9000'
+ description: Null Service for AI-Proxy
+tags:
+- name: llm
+ description: LLM Methods
+paths:
+ /{provider}/completions:
+ post:
+ tags:
+ - llm
+ summary: Provider Completions
+ operationId: provider-prompt-completions
+ description: Provider Prompt Completions
+ parameters:
+ - name: provider
+ in: path
+ required: true
+ schema:
+ type: string
+ requestBody:
+ description: Specific Kong-Conforming Post Body
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Prompt'
+ required: true
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PromptResponse'
+ /{provider}}/chat:
+ post:
+ tags:
+ - llm
+ summary: Provider Chat
+ operationId: provider-chat
+ description: Provider Chat
+ parameters:
+ - name: provider
+ in: path
+ required: true
+ schema:
+ type: string
+ requestBody:
+ description: Specific Kong-Conforming Post Body
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Chat'
+ required: true
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ChatResponse'
+
+components:
+ schemas:
+ Prompt:
+ required:
+ - prompt
+ type: object
+ description: 'Single-line prompt, sets up the entire question or completion prefix'
+ properties:
+ prompt:
+ type: string
+ Chat:
+ required:
+ - messages
+ type: object
+ description: 'Array of messages, or single-line template reference string'
+ properties:
+ messages:
+ anyOf:
+ - type: array
+ description: 'Array of role/content style chat messages'
+ minLength: 1
+ items:
+ $ref: '#/components/schemas/Message'
+ - type: string
+ description: 'Template reference, in the form {template://name}'
+ Message:
+ required:
+ - role
+ - content
+ type: object
+ description: 'Single chat message block'
+ properties:
+ role:
+ type: string
+ enum:
+ - "system"
+ - "user"
+ - "assistant"
+ content:
+ type: string
+ PromptResponse:
+ required:
+ - prompt
+ type: object
+ properties:
+ choices:
+ type: array
+ items:
+ type: object
+ properties:
+ finish_reason:
+ type: string
+ index:
+ type: integer
+ logprobs:
+ type: number
+ format: float
+ text:
+ type: string
+ required:
+ - finish_reason
+ - index
+ - logprobs
+ - text
+ created:
+ type: integer
+ id:
+ type: string
+ model:
+ type: string
+ object:
+ type: string
+ usage:
+ type: object
+ properties:
+ completion_tokens:
+ type: integer
+ prompt_tokens:
+ type: integer
+ total_tokens:
+ type: integer
+
+ ChatResponse:
+ required:
+ - messages
+ type: object
+ description: 'OpenAI-style chat response'
+
+ properties:
+ choices:
+ type: array
+ items:
+ type: object
+ properties:
+ finish_reason:
+ type: string
+ index:
+ type: integer
+ logprobs:
+ type: number
+ format: float
+ message:
+ type: object
+ properties:
+ content:
+ type: string
+ role:
+ type: string
+ required:
+ - content
+ - role
+ required:
+ - finish_reason
+ - index
+ - logprobs
+ - message
+ created:
+ type: integer
+ id:
+ type: string
+ model:
+ type: string
+ object:
+ type: string
+ system_fingerprint:
+ type: number
+ format: float
+ usage:
+ type: object
+ properties:
+ completion_tokens:
+ type: integer
+ prompt_tokens:
+ type: integer
+ total_tokens:
+ type: integer
+ required:
+ - completion_tokens
+ - prompt_tokens
+ - total_tokens
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/bad_request.json
new file mode 100644
index 00000000..99c40c7b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/bad_request.json
@@ -0,0 +1,12 @@
+{
+ "segassem":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good-stream.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good-stream.json
new file mode 100644
index 00000000..790bb707
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good-stream.json
@@ -0,0 +1,13 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "stream": true
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good.json
new file mode 100644
index 00000000..5d32fa0a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good.json
@@ -0,0 +1,13 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "stream": false
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good_own_model.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good_own_model.json
new file mode 100644
index 00000000..e72de3c7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/requests/good_own_model.json
@@ -0,0 +1,14 @@
+{
+ "messages":[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 1?"
+ }
+ ],
+ "model": "try-to-override-the-model",
+ "stream": false
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/bad_request.json
new file mode 100644
index 00000000..69b494a9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/bad_request.json
@@ -0,0 +1,8 @@
+{
+ "error": {
+ "code": null,
+ "message": "'messages' is a required property",
+ "param": null,
+ "type": "invalid_request_error"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/bad_upstream_response.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/bad_upstream_response.json
new file mode 100644
index 00000000..3bf212bd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/bad_upstream_response.json
@@ -0,0 +1,10 @@
+{
+ "nothing_object": {
+ "not_interesting_tag_names": "bad string",
+ "and_an_array": [
+ "because",
+ "why",
+ "not"
+ ]
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/good.json
new file mode 100644
index 00000000..8a3b0ab3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/good.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "The sum of 1 + 1 is 2.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1701947430,
+ "id": "chatcmpl-8T6YwgvjQVVnGbJ2w8hpOA17SeNy2",
+ "model": "gpt-3.5-turbo-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 12,
+ "prompt_tokens": 25,
+ "total_tokens": 37
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/internal_server_error.html b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/internal_server_error.html
new file mode 100644
index 00000000..4b37ec9f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/internal_server_error.html
@@ -0,0 +1,11 @@
+
+
+
+ Fake Internal Server Error
+
+
+
+
This is a fake Internal Server Error
+ It has come from your Mock AI server.
+
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/unauthorized.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/unauthorized.json
new file mode 100644
index 00000000..28908ba2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-chat/responses/unauthorized.json
@@ -0,0 +1,8 @@
+{
+ "error": {
+ "code": "invalid_api_key",
+ "message": "Incorrect API key provided: wro****ey. You can find your API key at https://platform.openai.com/account/api-keys.",
+ "param": null,
+ "type": "invalid_request_error"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/requests/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/requests/bad_request.json
new file mode 100644
index 00000000..795ead50
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/requests/bad_request.json
@@ -0,0 +1,3 @@
+{
+ "tpmorp": "bad prompt?"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/requests/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/requests/good.json
new file mode 100644
index 00000000..d66bba88
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/requests/good.json
@@ -0,0 +1,3 @@
+{
+ "prompt": "What are you?"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/bad_request.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/bad_request.json
new file mode 100644
index 00000000..def62036
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/bad_request.json
@@ -0,0 +1,8 @@
+{
+ "error": {
+ "code": null,
+ "message": "you must provide a 'prompt' parameter",
+ "param": null,
+ "type": "invalid_request_error"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/good.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/good.json
new file mode 100644
index 00000000..8c357cd0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/good.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "length",
+ "index": 0,
+ "logprobs": null,
+ "text": "\n\nI am a language model AI created by OpenAI. I can answer questions"
+ }
+ ],
+ "created": 1701967000,
+ "id": "cmpl-8TBeaJVQIhE9kHEJbk1RnKzgFxIqN",
+ "model": "gpt-3.5-turbo-instruct",
+ "object": "text_completion",
+ "usage": {
+ "completion_tokens": 16,
+ "prompt_tokens": 4,
+ "total_tokens": 20
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/unauthorized.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/unauthorized.json
new file mode 100644
index 00000000..28908ba2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/llm-v1-completions/responses/unauthorized.json
@@ -0,0 +1,8 @@
+{
+ "error": {
+ "code": "invalid_api_key",
+ "message": "Incorrect API key provided: wro****ey. You can find your API key at https://platform.openai.com/account/api-keys.",
+ "param": null,
+ "type": "invalid_request_error"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-in-json.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-in-json.json
new file mode 100644
index 00000000..cc8f792c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-in-json.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": " {\n \"persons\": [\n {\n \"name\": \"Kong A\",\n \"age\": 62\n },\n {\n \"name\": \"Kong B\",\n \"age\": 84\n }\n ]\n }\n",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1701947430,
+ "id": "chatcmpl-8T6YwgvjQVVnGbJ2w8hpOA17SeNy2",
+ "model": "gpt-3.5-turbo-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 12,
+ "prompt_tokens": 25,
+ "total_tokens": 37
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-not-json.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-not-json.json
new file mode 100644
index 00000000..35c96e72
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-not-json.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Sure! Here is your JSON: {\n \"persons\": [\n {\n \"name\": \"Kong A\",\n \"age\": 62\n },\n {\n \"name\": \"Kong B\",\n \"age\": 84\n }\n ]\n }.\n Can I do anything else for you?",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1701947430,
+ "id": "chatcmpl-8T6YwgvjQVVnGbJ2w8hpOA17SeNy2",
+ "model": "gpt-3.5-turbo-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 12,
+ "prompt_tokens": 25,
+ "total_tokens": 37
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-with-bad-instructions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-with-bad-instructions.json
new file mode 100644
index 00000000..b2f10834
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-with-bad-instructions.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Sure! Here's your response: {\n \"status\": 209,\n \"headers\": {\n \"content-type\": \"application/xml\"\n },\n \"body\": \"\n \n Kong A \n 62 \n \n \n Kong B \n 84 \n \n \"\n}.\nCan I help with anything else?",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1701947430,
+ "id": "chatcmpl-8T6YwgvjQVVnGbJ2w8hpOA17SeNy2",
+ "model": "gpt-3.5-turbo-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 12,
+ "prompt_tokens": 25,
+ "total_tokens": 37
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-with-instructions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-with-instructions.json
new file mode 100644
index 00000000..29445e6a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/openai/request-transformer/response-with-instructions.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "{\n \"status\": 209,\n \"headers\": {\n \"content-type\": \"application/xml\"\n },\n \"body\": \"\n \n Kong A \n 62 \n \n \n Kong B \n 84 \n \n \"\n}\n",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1701947430,
+ "id": "chatcmpl-8T6YwgvjQVVnGbJ2w8hpOA17SeNy2",
+ "model": "gpt-3.5-turbo-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 12,
+ "prompt_tokens": 25,
+ "total_tokens": 37
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/anthropic/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/anthropic/llm-v1-chat.json
new file mode 100644
index 00000000..00756b59
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/anthropic/llm-v1-chat.json
@@ -0,0 +1,29 @@
+{
+ "model": "claude-2.1",
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is 1 + 2?"
+ },
+ {
+ "role": "assistant",
+ "content": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Multiply that by 2"
+ },
+ {
+ "role": "assistant",
+ "content": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Why can't you divide by zero?"
+ }
+ ],
+ "system": "You are a mathematician.",
+ "max_tokens": 512,
+ "temperature": 0.5,
+ "stream": false
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/anthropic/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/anthropic/llm-v1-completions.json
new file mode 100644
index 00000000..7af2711f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/anthropic/llm-v1-completions.json
@@ -0,0 +1,7 @@
+{
+ "model": "claude-2.1",
+ "prompt": "Human: Explain why you can't divide by zero?\n\nAssistant:",
+ "max_tokens_to_sample": 512,
+ "temperature": 0.5,
+ "stream": false
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/azure/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/azure/llm-v1-chat.json
new file mode 100644
index 00000000..5a9c2d9e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/azure/llm-v1-chat.json
@@ -0,0 +1,33 @@
+{
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a mathematician."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 2?"
+ },
+ {
+ "role": "assistant",
+ "content": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Multiply that by 2"
+ },
+ {
+ "role": "assistant",
+ "content": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Why can't you divide by zero?"
+ }
+ ],
+ "model": "gpt-4",
+ "max_tokens": 512,
+ "temperature": 0.5,
+ "stream": false,
+ "top_p": 1.0
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/azure/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/azure/llm-v1-completions.json
new file mode 100644
index 00000000..31c90e2b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/azure/llm-v1-completions.json
@@ -0,0 +1,8 @@
+{
+ "prompt": "Explain why you can't divide by zero?",
+ "model": "gpt-3.5-turbo-instruct",
+ "max_tokens": 512,
+ "temperature": 0.5,
+ "stream": false,
+ "top_p": 1
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/cohere/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/cohere/llm-v1-chat.json
new file mode 100644
index 00000000..46ae6562
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/cohere/llm-v1-chat.json
@@ -0,0 +1,15 @@
+{
+ "chat_history": [
+ {"role": "USER", "message": "You are a mathematician."},
+ {"role": "USER", "message": "What is 1 + 2?"},
+ {"role": "CHATBOT", "message": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"},
+ {"role": "USER", "message": "Multiply that by 2"},
+ {"role": "CHATBOT", "message": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"}
+ ],
+ "message": "Why can't you divide by zero?",
+ "model": "command",
+ "max_tokens": 512,
+ "temperature": 0.5,
+ "p": 1.0,
+ "stream": false
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/cohere/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/cohere/llm-v1-completions.json
new file mode 100644
index 00000000..400114b7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/cohere/llm-v1-completions.json
@@ -0,0 +1,9 @@
+{
+ "prompt": "Explain why you can't divide by zero?",
+ "model": "command",
+ "max_tokens": 512,
+ "temperature": 0.5,
+ "p": 0.75,
+ "k": 5,
+ "stream": false
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/ollama/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/ollama/llm-v1-chat.json
new file mode 100644
index 00000000..358a31d2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/ollama/llm-v1-chat.json
@@ -0,0 +1,34 @@
+{
+ "model": "llama2",
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a mathematician."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 2?"
+ },
+ {
+ "role": "assistant",
+ "content": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Multiply that by 2"
+ },
+ {
+ "role": "assistant",
+ "content": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Why can't you divide by zero?"
+ }
+ ],
+ "stream": false,
+ "options": {
+ "num_predict": 512,
+ "temperature": 0.5
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/ollama/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/ollama/llm-v1-completions.json
new file mode 100644
index 00000000..d81dbead
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/ollama/llm-v1-completions.json
@@ -0,0 +1,9 @@
+{
+ "model": "llama2",
+ "prompt": "Explain why you can't divide by zero?",
+ "stream": false,
+ "options": {
+ "num_predict": 512,
+ "temperature": 0.5
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/raw/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/raw/llm-v1-chat.json
new file mode 100644
index 00000000..71c517de
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/raw/llm-v1-chat.json
@@ -0,0 +1,10 @@
+{
+ "inputs": "[INST] <> You are a mathematician. < > What is 1 + 2? [/INST] [INST] Multiply that by 2 [/INST] [INST] Why can't you divide by zero? [/INST]",
+ "parameters": {
+ "max_new_tokens": 512,
+ "temperature": 0.5,
+ "top_k": 40,
+ "top_p": 1,
+ "stream": false
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/raw/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/raw/llm-v1-completions.json
new file mode 100644
index 00000000..d4011887
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/llama2/raw/llm-v1-completions.json
@@ -0,0 +1,8 @@
+{
+ "inputs": " [INST] <> You are a helpful assistant. <> Explain why you can't divide by zero? [/INST]",
+ "parameters": {
+ "max_new_tokens": 512,
+ "temperature": 0.5,
+ "stream": false
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/mistral/ollama/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/mistral/ollama/llm-v1-chat.json
new file mode 100644
index 00000000..62150c54
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/mistral/ollama/llm-v1-chat.json
@@ -0,0 +1,34 @@
+{
+ "model": "mistral-tiny",
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a mathematician."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 2?"
+ },
+ {
+ "role": "assistant",
+ "content": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Multiply that by 2"
+ },
+ {
+ "role": "assistant",
+ "content": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Why can't you divide by zero?"
+ }
+ ],
+ "stream": false,
+ "options": {
+ "num_predict": 512,
+ "temperature": 0.5
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/mistral/openai/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/mistral/openai/llm-v1-chat.json
new file mode 100644
index 00000000..adb87db4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/mistral/openai/llm-v1-chat.json
@@ -0,0 +1,32 @@
+{
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a mathematician."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 2?"
+ },
+ {
+ "role": "assistant",
+ "content": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Multiply that by 2"
+ },
+ {
+ "role": "assistant",
+ "content": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Why can't you divide by zero?"
+ }
+ ],
+ "model": "mistral-tiny",
+ "max_tokens": 512,
+ "stream": false,
+ "temperature": 0.5
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/openai/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/openai/llm-v1-chat.json
new file mode 100644
index 00000000..d7aa2028
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/openai/llm-v1-chat.json
@@ -0,0 +1,32 @@
+{
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a mathematician."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 2?"
+ },
+ {
+ "role": "assistant",
+ "content": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Multiply that by 2"
+ },
+ {
+ "role": "assistant",
+ "content": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Why can't you divide by zero?"
+ }
+ ],
+ "model": "gpt-4",
+ "max_tokens": 512,
+ "stream": false,
+ "temperature": 0.5
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/openai/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/openai/llm-v1-completions.json
new file mode 100644
index 00000000..bc7368bb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-requests/openai/llm-v1-completions.json
@@ -0,0 +1,7 @@
+{
+ "prompt": "Explain why you can't divide by zero?",
+ "model": "gpt-3.5-turbo-instruct",
+ "max_tokens": 512,
+ "temperature": 0.5,
+ "stream": false
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/anthropic/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/anthropic/llm-v1-chat.json
new file mode 100644
index 00000000..969489f8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/anthropic/llm-v1-chat.json
@@ -0,0 +1,14 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop_sequence",
+ "index": 0,
+ "message": {
+ "content": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "model": "claude-2.1",
+ "object": "chat.completion"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/anthropic/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/anthropic/llm-v1-completions.json
new file mode 100644
index 00000000..0c3eccb7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/anthropic/llm-v1-completions.json
@@ -0,0 +1,11 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop_sequence",
+ "index": 0,
+ "text": "You cannot divide by zero because it is not a valid operation in mathematics."
+ }
+ ],
+ "model": "claude-2.1",
+ "object": "text_completion"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/azure/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/azure/llm-v1-chat.json
new file mode 100644
index 00000000..74b0c341
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/azure/llm-v1-chat.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Dividing by zero is undefined in mathematics because it leads to results that are contradictory or nonsensical. \n\nHere's a simple way to think about it: Division is the inverse of multiplication. If you divide 10 by 2, you're asking \"what number times 2 gives me 10?\" The answer is 5, because 5 times 2 equals 10. \n\nBut if you ask \"what number times 0 gives me 10?\" there is no number that can fulfill this, because zero times any number always equals zero. \n\nTherefore, division by zero is undefined because there is no number that you can multiply by 0 to get a non-zero number.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1702325640,
+ "id": "chatcmpl-8Ugx63a79wKACVkaBbKnR2C2HPcxT",
+ "model": "gpt-4-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/azure/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/azure/llm-v1-completions.json
new file mode 100644
index 00000000..68396057
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/azure/llm-v1-completions.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "logprobs": null,
+ "text": "\n\nDividing by zero is undefined because it violates the fundamental mathematical principle of division, which states that when dividing a number by another number, the result is the number of times the divisor can fit into the dividend. In other words, division is the inverse operation of multiplication.\n\nHowever, when dividing by zero, there is no number that can be multiplied by zero to give a specific result. This is because any number multiplied by zero will always equal zero, therefore there is no solution or value that can be assigned to the quotient.\n\nAdditionally, dividing by zero can lead to contradictory or nonsensical results. For example, if we divide a number by a smaller and smaller number approaching zero, the resulting quotient becomes larger and larger, approaching infinity. On the other hand, if we divide a number by a larger and larger number approaching zero, the resulting quotient becomes smaller and smaller, approaching negative infinity. This inconsistency shows that dividing by zero does not follow the rules of arithmetic and is therefore considered undefined.\n\nIn summary, division by zero is not allowed because it is mathematically undefined and can lead to nonsensical results."
+ }
+ ],
+ "created": 1702325696,
+ "id": "cmpl-8Ugy0y4E5S8s5GfqNal9TYhXMyitF",
+ "model": "gpt-3.5-turbo-instruct",
+ "object": "text_completion",
+ "usage": {
+ "completion_tokens": 225,
+ "prompt_tokens": 10,
+ "total_tokens": 235
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/cohere/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/cohere/llm-v1-chat.json
new file mode 100644
index 00000000..9e3f88ee
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/cohere/llm-v1-chat.json
@@ -0,0 +1,20 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "id": "f8aabbeb-f745-4e9b-85b1-71a3269620d9",
+ "model": "command",
+ "object": "chat.completion",
+ "usage": {
+ "completion_tokens": 258,
+ "prompt_tokens": 102,
+ "total_tokens": 360
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/cohere/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/cohere/llm-v1-completions.json
new file mode 100644
index 00000000..7240745e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/cohere/llm-v1-completions.json
@@ -0,0 +1,17 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "text": " You cannot divide by zero because it is not a valid operation in mathematics. Division is the process of finding how many times one number can fit into another number while subtraction is finding the difference between two numbers. For example, if you have a pizza that is divided into 5 pieces and you eat 2 pieces, you have 3 pieces left. This is expressed as $5 \\div 2 = 3$. However, if you eat all 5 pieces, there are no pieces left. We cannot define the result of"
+ }
+ ],
+ "id": "77d630a0-c350-4f4e-bbff-ae6eda8919f3",
+ "model": "command",
+ "object": "text_completion",
+ "usage": {
+ "completion_tokens": 100,
+ "prompt_tokens": 8,
+ "total_tokens": 108
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/ollama/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/ollama/llm-v1-chat.json
new file mode 100644
index 00000000..08bb7a7e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/ollama/llm-v1-chat.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "model": "llama2",
+ "object": "chat.completion",
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/ollama/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/ollama/llm-v1-completions.json
new file mode 100644
index 00000000..e8702be8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/ollama/llm-v1-completions.json
@@ -0,0 +1,15 @@
+{
+ "choices": [
+ {
+ "index": 0,
+ "text": "You cannot divide by zero because it is not a valid operation in mathematics."
+ }
+ ],
+ "object": "text_completion",
+ "model": "llama2",
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/raw/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/raw/llm-v1-chat.json
new file mode 100644
index 00000000..4214b115
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/raw/llm-v1-chat.json
@@ -0,0 +1,12 @@
+{
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "content": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "object": "chat.completion"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/raw/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/raw/llm-v1-completions.json
new file mode 100644
index 00000000..65c6b38f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/llama2/raw/llm-v1-completions.json
@@ -0,0 +1,9 @@
+{
+ "choices": [
+ {
+ "index": 0,
+ "text": "You cannot divide by zero because it is not a valid operation in mathematics."
+ }
+ ],
+ "object": "text_completion"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/mistral/ollama/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/mistral/ollama/llm-v1-chat.json
new file mode 100644
index 00000000..f5b53122
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/mistral/ollama/llm-v1-chat.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "model": "mistral-tiny",
+ "object": "chat.completion",
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/mistral/openai/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/mistral/openai/llm-v1-chat.json
new file mode 100644
index 00000000..d1c1c905
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/mistral/openai/llm-v1-chat.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Dividing by zero is undefined in mathematics because it leads to results that are contradictory or nonsensical. \n\nHere's a simple way to think about it: Division is the inverse of multiplication. If you divide 10 by 2, you're asking \"what number times 2 gives me 10?\" The answer is 5, because 5 times 2 equals 10. \n\nBut if you ask \"what number times 0 gives me 10?\" there is no number that can fulfill this, because zero times any number always equals zero. \n\nTherefore, division by zero is undefined because there is no number that you can multiply by 0 to get a non-zero number.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1702325640,
+ "id": "chatcmpl-8Ugx63a79wKACVkaBbKnR2C2HPcxT",
+ "model": "mistral-tiny",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/openai/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/openai/llm-v1-chat.json
new file mode 100644
index 00000000..74b0c341
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/openai/llm-v1-chat.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Dividing by zero is undefined in mathematics because it leads to results that are contradictory or nonsensical. \n\nHere's a simple way to think about it: Division is the inverse of multiplication. If you divide 10 by 2, you're asking \"what number times 2 gives me 10?\" The answer is 5, because 5 times 2 equals 10. \n\nBut if you ask \"what number times 0 gives me 10?\" there is no number that can fulfill this, because zero times any number always equals zero. \n\nTherefore, division by zero is undefined because there is no number that you can multiply by 0 to get a non-zero number.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1702325640,
+ "id": "chatcmpl-8Ugx63a79wKACVkaBbKnR2C2HPcxT",
+ "model": "gpt-4-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/openai/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/openai/llm-v1-completions.json
new file mode 100644
index 00000000..68396057
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/expected-responses/openai/llm-v1-completions.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "logprobs": null,
+ "text": "\n\nDividing by zero is undefined because it violates the fundamental mathematical principle of division, which states that when dividing a number by another number, the result is the number of times the divisor can fit into the dividend. In other words, division is the inverse operation of multiplication.\n\nHowever, when dividing by zero, there is no number that can be multiplied by zero to give a specific result. This is because any number multiplied by zero will always equal zero, therefore there is no solution or value that can be assigned to the quotient.\n\nAdditionally, dividing by zero can lead to contradictory or nonsensical results. For example, if we divide a number by a smaller and smaller number approaching zero, the resulting quotient becomes larger and larger, approaching infinity. On the other hand, if we divide a number by a larger and larger number approaching zero, the resulting quotient becomes smaller and smaller, approaching negative infinity. This inconsistency shows that dividing by zero does not follow the rules of arithmetic and is therefore considered undefined.\n\nIn summary, division by zero is not allowed because it is mathematically undefined and can lead to nonsensical results."
+ }
+ ],
+ "created": 1702325696,
+ "id": "cmpl-8Ugy0y4E5S8s5GfqNal9TYhXMyitF",
+ "model": "gpt-3.5-turbo-instruct",
+ "object": "text_completion",
+ "usage": {
+ "completion_tokens": 225,
+ "prompt_tokens": 10,
+ "total_tokens": 235
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/anthropic/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/anthropic/llm-v1-chat.json
new file mode 100644
index 00000000..624214cf
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/anthropic/llm-v1-chat.json
@@ -0,0 +1,12 @@
+{
+ "content": [{
+ "text": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "type": "text"
+ }],
+ "stop_reason": "stop_sequence",
+ "model": "claude-2.1",
+ "usage": {
+ "input_tokens": 0,
+ "output_tokens": 0
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/anthropic/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/anthropic/llm-v1-completions.json
new file mode 100644
index 00000000..eed219f5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/anthropic/llm-v1-completions.json
@@ -0,0 +1,5 @@
+{
+ "completion": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "stop_reason": "stop_sequence",
+ "model": "claude-2.1"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/azure/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/azure/llm-v1-chat.json
new file mode 100644
index 00000000..74b0c341
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/azure/llm-v1-chat.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Dividing by zero is undefined in mathematics because it leads to results that are contradictory or nonsensical. \n\nHere's a simple way to think about it: Division is the inverse of multiplication. If you divide 10 by 2, you're asking \"what number times 2 gives me 10?\" The answer is 5, because 5 times 2 equals 10. \n\nBut if you ask \"what number times 0 gives me 10?\" there is no number that can fulfill this, because zero times any number always equals zero. \n\nTherefore, division by zero is undefined because there is no number that you can multiply by 0 to get a non-zero number.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1702325640,
+ "id": "chatcmpl-8Ugx63a79wKACVkaBbKnR2C2HPcxT",
+ "model": "gpt-4-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/azure/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/azure/llm-v1-completions.json
new file mode 100644
index 00000000..68396057
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/azure/llm-v1-completions.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "logprobs": null,
+ "text": "\n\nDividing by zero is undefined because it violates the fundamental mathematical principle of division, which states that when dividing a number by another number, the result is the number of times the divisor can fit into the dividend. In other words, division is the inverse operation of multiplication.\n\nHowever, when dividing by zero, there is no number that can be multiplied by zero to give a specific result. This is because any number multiplied by zero will always equal zero, therefore there is no solution or value that can be assigned to the quotient.\n\nAdditionally, dividing by zero can lead to contradictory or nonsensical results. For example, if we divide a number by a smaller and smaller number approaching zero, the resulting quotient becomes larger and larger, approaching infinity. On the other hand, if we divide a number by a larger and larger number approaching zero, the resulting quotient becomes smaller and smaller, approaching negative infinity. This inconsistency shows that dividing by zero does not follow the rules of arithmetic and is therefore considered undefined.\n\nIn summary, division by zero is not allowed because it is mathematically undefined and can lead to nonsensical results."
+ }
+ ],
+ "created": 1702325696,
+ "id": "cmpl-8Ugy0y4E5S8s5GfqNal9TYhXMyitF",
+ "model": "gpt-3.5-turbo-instruct",
+ "object": "text_completion",
+ "usage": {
+ "completion_tokens": 225,
+ "prompt_tokens": 10,
+ "total_tokens": 235
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/cohere/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/cohere/llm-v1-chat.json
new file mode 100644
index 00000000..bbed8b91
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/cohere/llm-v1-chat.json
@@ -0,0 +1,20 @@
+{
+ "generation_id": "f8aabbeb-f745-4e9b-85b1-71a3269620d9",
+ "meta": {
+ "api_version": {
+ "version": "1"
+ },
+ "billed_units": {
+ "input_tokens": 81,
+ "output_tokens": 258
+ }
+ },
+ "response_id": "3ed9cd6c-afcc-4591-a4d3-5745ba88922e",
+ "text": "You cannot divide by zero because it is not a valid operation in mathematics.",
+ "token_count": {
+ "billed_tokens": 339,
+ "prompt_tokens": 102,
+ "response_tokens": 258,
+ "total_tokens": 360
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/cohere/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/cohere/llm-v1-completions.json
new file mode 100644
index 00000000..1a7c75aa
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/cohere/llm-v1-completions.json
@@ -0,0 +1,20 @@
+{
+ "generations": [
+ {
+ "finish_reason": "MAX_TOKENS",
+ "id": "d9b056b7-5506-4407-8b8f-b65b995f4203",
+ "text": " You cannot divide by zero because it is not a valid operation in mathematics. Division is the process of finding how many times one number can fit into another number while subtraction is finding the difference between two numbers. For example, if you have a pizza that is divided into 5 pieces and you eat 2 pieces, you have 3 pieces left. This is expressed as $5 \\div 2 = 3$. However, if you eat all 5 pieces, there are no pieces left. We cannot define the result of"
+ }
+ ],
+ "id": "77d630a0-c350-4f4e-bbff-ae6eda8919f3",
+ "meta": {
+ "api_version": {
+ "version": "1"
+ },
+ "billed_units": {
+ "input_tokens": 8,
+ "output_tokens": 100
+ }
+ },
+ "prompt": "Why can't you divide by zero?"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/ollama/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/ollama/llm-v1-chat.json
new file mode 100644
index 00000000..98a3bbfc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/ollama/llm-v1-chat.json
@@ -0,0 +1,15 @@
+{
+ "model": "llama2",
+ "created_at": "2024-01-15T08:13:38.876196Z",
+ "message": {
+ "role": "assistant",
+ "content": "You cannot divide by zero because it is not a valid operation in mathematics."
+ },
+ "done": true,
+ "total_duration": 4062418334,
+ "load_duration": 1229365792,
+ "prompt_eval_count": 26,
+ "prompt_eval_duration": 167969000,
+ "eval_count": 100,
+ "eval_duration": 2658646000
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/ollama/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/ollama/llm-v1-completions.json
new file mode 100644
index 00000000..644d4078
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/ollama/llm-v1-completions.json
@@ -0,0 +1,14 @@
+{
+ "model": "llama2",
+ "created_at": "2024-01-15T08:14:21.967358Z",
+ "response": "Because I said so.",
+ "done": true,
+ "context": [
+ ],
+ "total_duration": 613583209,
+ "load_duration": 2220959,
+ "prompt_eval_count": 13,
+ "prompt_eval_duration": 307784000,
+ "eval_count": 12,
+ "eval_duration": 299573000
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/raw/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/raw/llm-v1-chat.json
new file mode 100644
index 00000000..fdec6c60
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/raw/llm-v1-chat.json
@@ -0,0 +1,7 @@
+{
+ "data": [
+ {
+ "generated_text": "[INST] <> You are a mathematician. < > What is 1 + 2? [/INST] [INST] Multiply that by 2 [/INST] [INST] Why can't you divide by zero? [/INST]\n\nYou cannot divide by zero because it is not a valid operation in mathematics."
+ }
+ ]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/raw/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/raw/llm-v1-completions.json
new file mode 100644
index 00000000..cf080117
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/llama2/raw/llm-v1-completions.json
@@ -0,0 +1,7 @@
+{
+ "data": [
+ {
+ "generated_text": " [INST] <> You are a helpful assistant. <> Explain why you can't divide by zero? [/INST]\n\nYou cannot divide by zero because it is not a valid operation in mathematics."
+ }
+ ]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/mistral/ollama/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/mistral/ollama/llm-v1-chat.json
new file mode 100644
index 00000000..45520146
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/mistral/ollama/llm-v1-chat.json
@@ -0,0 +1,15 @@
+{
+ "model": "mistral-tiny",
+ "created_at": "2024-01-15T08:13:38.876196Z",
+ "message": {
+ "role": "assistant",
+ "content": "You cannot divide by zero because it is not a valid operation in mathematics."
+ },
+ "done": true,
+ "total_duration": 4062418334,
+ "load_duration": 1229365792,
+ "prompt_eval_count": 26,
+ "prompt_eval_duration": 167969000,
+ "eval_count": 100,
+ "eval_duration": 2658646000
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/mistral/openai/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/mistral/openai/llm-v1-chat.json
new file mode 100644
index 00000000..d1c1c905
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/mistral/openai/llm-v1-chat.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Dividing by zero is undefined in mathematics because it leads to results that are contradictory or nonsensical. \n\nHere's a simple way to think about it: Division is the inverse of multiplication. If you divide 10 by 2, you're asking \"what number times 2 gives me 10?\" The answer is 5, because 5 times 2 equals 10. \n\nBut if you ask \"what number times 0 gives me 10?\" there is no number that can fulfill this, because zero times any number always equals zero. \n\nTherefore, division by zero is undefined because there is no number that you can multiply by 0 to get a non-zero number.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1702325640,
+ "id": "chatcmpl-8Ugx63a79wKACVkaBbKnR2C2HPcxT",
+ "model": "mistral-tiny",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/openai/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/openai/llm-v1-chat.json
new file mode 100644
index 00000000..74b0c341
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/openai/llm-v1-chat.json
@@ -0,0 +1,22 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "message": {
+ "content": "Dividing by zero is undefined in mathematics because it leads to results that are contradictory or nonsensical. \n\nHere's a simple way to think about it: Division is the inverse of multiplication. If you divide 10 by 2, you're asking \"what number times 2 gives me 10?\" The answer is 5, because 5 times 2 equals 10. \n\nBut if you ask \"what number times 0 gives me 10?\" there is no number that can fulfill this, because zero times any number always equals zero. \n\nTherefore, division by zero is undefined because there is no number that you can multiply by 0 to get a non-zero number.",
+ "role": "assistant"
+ }
+ }
+ ],
+ "created": 1702325640,
+ "id": "chatcmpl-8Ugx63a79wKACVkaBbKnR2C2HPcxT",
+ "model": "gpt-4-0613",
+ "object": "chat.completion",
+ "system_fingerprint": null,
+ "usage": {
+ "completion_tokens": 139,
+ "prompt_tokens": 130,
+ "total_tokens": 269
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/openai/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/openai/llm-v1-completions.json
new file mode 100644
index 00000000..68396057
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-responses/openai/llm-v1-completions.json
@@ -0,0 +1,19 @@
+{
+ "choices": [
+ {
+ "finish_reason": "stop",
+ "index": 0,
+ "logprobs": null,
+ "text": "\n\nDividing by zero is undefined because it violates the fundamental mathematical principle of division, which states that when dividing a number by another number, the result is the number of times the divisor can fit into the dividend. In other words, division is the inverse operation of multiplication.\n\nHowever, when dividing by zero, there is no number that can be multiplied by zero to give a specific result. This is because any number multiplied by zero will always equal zero, therefore there is no solution or value that can be assigned to the quotient.\n\nAdditionally, dividing by zero can lead to contradictory or nonsensical results. For example, if we divide a number by a smaller and smaller number approaching zero, the resulting quotient becomes larger and larger, approaching infinity. On the other hand, if we divide a number by a larger and larger number approaching zero, the resulting quotient becomes smaller and smaller, approaching negative infinity. This inconsistency shows that dividing by zero does not follow the rules of arithmetic and is therefore considered undefined.\n\nIn summary, division by zero is not allowed because it is mathematically undefined and can lead to nonsensical results."
+ }
+ ],
+ "created": 1702325696,
+ "id": "cmpl-8Ugy0y4E5S8s5GfqNal9TYhXMyitF",
+ "model": "gpt-3.5-turbo-instruct",
+ "object": "text_completion",
+ "usage": {
+ "completion_tokens": 225,
+ "prompt_tokens": 10,
+ "total_tokens": 235
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/cohere/llm-v1-chat.txt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/cohere/llm-v1-chat.txt
new file mode 100644
index 00000000..f6158488
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/cohere/llm-v1-chat.txt
@@ -0,0 +1 @@
+{"is_finished":false,"event_type":"text-generation","text":"the answer"}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/cohere/llm-v1-completions.txt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/cohere/llm-v1-completions.txt
new file mode 100644
index 00000000..4add796f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/cohere/llm-v1-completions.txt
@@ -0,0 +1 @@
+{"text":"the answer","is_finished":false,"event_type":"text-generation"}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/openai/llm-v1-chat.txt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/openai/llm-v1-chat.txt
new file mode 100644
index 00000000..2f7c45fe
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/openai/llm-v1-chat.txt
@@ -0,0 +1 @@
+data: {"choices": [{"delta": {"content": "the answer"},"finish_reason": null,"index": 0,"logprobs": null}],"created": 1711938086,"id": "chatcmpl-991aYb1iD8OSD54gcxZxv8uazlTZy","model": "gpt-4-0613","object": "chat.completion.chunk","system_fingerprint": null}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/openai/llm-v1-completions.txt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/openai/llm-v1-completions.txt
new file mode 100644
index 00000000..e9e1b313
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/real-stream-frames/openai/llm-v1-completions.txt
@@ -0,0 +1 @@
+data: {"choices": [{"finish_reason": null,"index": 0,"logprobs": null,"text": "the answer"}],"created": 1711938803,"id": "cmpl-991m7YSJWEnzrBqk41In8Xer9RIEB","model": "gpt-3.5-turbo-instruct","object": "text_completion"}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-chat.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-chat.json
new file mode 100644
index 00000000..c3f059f1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-chat.json
@@ -0,0 +1,28 @@
+{
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a mathematician."
+ },
+ {
+ "role": "user",
+ "content": "What is 1 + 2?"
+ },
+ {
+ "role": "assistant",
+ "content": "The sum of 1 + 2 is 3. If you have any more math questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Multiply that by 2"
+ },
+ {
+ "role": "assistant",
+ "content": "Certainly! If you multiply 3 by 2, the result is 6. If you have any more questions or if there's anything else I can help you with, feel free to ask!"
+ },
+ {
+ "role": "user",
+ "content": "Why can't you divide by zero?"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-completion-template.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-completion-template.json
new file mode 100644
index 00000000..17486d19
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-completion-template.json
@@ -0,0 +1,8 @@
+{
+ "prompt": {
+ "name": "python-chat",
+ "properties": {
+ "program": "fibonacci sequence"
+ }
+ }
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-completions.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-completions.json
new file mode 100644
index 00000000..158e601b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ai-proxy/unit/requests/llm-v1-completions.json
@@ -0,0 +1,3 @@
+{
+ "prompt": "Explain why you can't divide by zero?"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/app_dynamics/kong/plugins/app-dynamics/appdynamics.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/app_dynamics/kong/plugins/app-dynamics/appdynamics.lua
new file mode 100644
index 00000000..d44a5ba1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/app_dynamics/kong/plugins/app-dynamics/appdynamics.lua
@@ -0,0 +1,104 @@
+-- This module implements a mocked version of the AppDynamics C SDK,
+-- as far as it is used in the AppDynamics plugin. The intent of it
+-- is not to completely remodel the behavior of the SDK as we don't
+-- completely know what it does. Instead, we want to ensure that the
+-- intended SDK functions are invoked in response to requests sent to
+-- Kong. This is achieved by creating a call trace in a file which
+-- can then be asserted against by the integration test for the
+-- plugin.
+
+local MOCK_TRACE_FILENAME = os.getenv("KONG_APPD_MOCK_TRACE_FILENAME")
+
+local ffi = require "ffi"
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+ffi.cdef [[
+ char* strdup(const char*);
+]]
+
+local function log_call(name, arguments)
+ local call_info = name .. "("
+ for i = 1,#arguments do
+ local argument = arguments[i]
+ local formatted_argument
+ if type(argument) == 'string' then
+ formatted_argument = "\"" .. argument .. "\""
+ elseif type(argument) == 'table' then
+ formatted_argument = "<" .. tostring(argument) .. ">"
+ else
+ formatted_argument = tostring(argument)
+ end
+ call_info = call_info .. formatted_argument
+ if i ~= #arguments then
+ call_info = call_info .. ", "
+ end
+ end
+ call_info = call_info .. ")\n"
+ io.write(call_info)
+ if MOCK_TRACE_FILENAME then
+ local out = assert(io.open(MOCK_TRACE_FILENAME, "a"))
+ out:write(call_info)
+ out:close()
+ end
+end
+
+
+local appd = {
+ APPD_LOG_LEVEL_TRACE = 0,
+ APPD_LOG_LEVEL_DEBUG = 1,
+ APPD_LOG_LEVEL_INFO = 2,
+ APPD_LOG_LEVEL_WARN = 3,
+ APPD_LOG_LEVEL_ERROR = 4,
+ APPD_LOG_LEVEL_FATAL = 5,
+
+ APPD_LEVEL_NOTICE = 0,
+ APPD_LEVEL_WARNING = 1,
+ APPD_LEVEL_ERROR = 2,
+
+ appd_config_init = function()
+ return {}
+ end,
+
+ appd_exitcall_begin = function(bt_handle, backend_name)
+ log_call('appd_exitcall_begin', { bt_handle, backend_name })
+ return {
+ bt_handle = bt_handle,
+ backend_name = backend_name,
+ correlation_header = bt_handle.correlation_header,
+ }
+ end,
+
+ appd_exitcall_get_correlation_header = function(exit_handle)
+ log_call('appd_exitcall_get_correlation_header', { exit_handle })
+ -- The memory allocated by strdup below is never freed. This
+ -- should be acceptable for this mock implementation.
+ return ffi.C.strdup(exit_handle.correlation_header)
+ end,
+
+ appd_bt_begin = function(name, correlation_header)
+ log_call('appd_bt_begin', { name, correlation_header })
+ return {
+ name = name,
+ correlation_header = correlation_header or "mocked-correlation-header-content",
+ }
+ end,
+}
+
+setmetatable(
+ appd,
+ {
+ __index = function(_, name)
+ return function(...)
+ log_call(name, {...})
+ return 0 -- 0 generally indicates success in the AppDynamics SDK
+ end
+ end
+ }
+)
+
+return appd
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/aws-lambda.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/aws-lambda.lua
new file mode 100644
index 00000000..32894e42
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/aws-lambda.lua
@@ -0,0 +1,148 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local helpers = require "spec.helpers"
+
+local fixtures = {
+ dns_mock = helpers.dns_mock.new(),
+ http_mock = {
+ lambda_plugin = [[
+
+ server {
+ server_name mock_aws_lambda;
+ listen 10001 ssl;
+> if ssl_cert[1] then
+> for i = 1, #ssl_cert do
+ ssl_certificate $(ssl_cert[i]);
+ ssl_certificate_key $(ssl_cert_key[i]);
+> end
+> else
+ ssl_certificate ${{SSL_CERT}};
+ ssl_certificate_key ${{SSL_CERT_KEY}};
+> end
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ location ~ "/2015-03-31/functions/(?:[^/])*/invocations" {
+ content_by_lua_block {
+ local function x()
+ local function say(res, status)
+ ngx.header["x-amzn-RequestId"] = "foo"
+
+ if string.match(ngx.var.uri, "functionWithUnhandledError") then
+ ngx.header["X-Amz-Function-Error"] = "Unhandled"
+ end
+
+ ngx.status = status
+
+ if string.match(ngx.var.uri, "functionWithBadJSON") then
+ local badRes = "{\"foo\":\"bar\""
+ ngx.header["Content-Length"] = #badRes + 1
+ ngx.say(badRes)
+
+ elseif string.match(ngx.var.uri, "functionWithNoResponse") then
+ ngx.header["Content-Length"] = 0
+
+ elseif string.match(ngx.var.uri, "functionWithBase64EncodedResponse") then
+ ngx.header["Content-Type"] = "application/json"
+ ngx.say("{\"statusCode\": 200, \"body\": \"dGVzdA==\", \"isBase64Encoded\": true}")
+
+ elseif string.match(ngx.var.uri, "functionWithNotBase64EncodedResponse") then
+ ngx.header["Content-Type"] = "application/json"
+ ngx.say("{\"statusCode\": 200, \"body\": \"dGVzdA=\", \"isBase64Encoded\": false}")
+
+ elseif string.match(ngx.var.uri, "functionWithIllegalBase64EncodedResponse") then
+ ngx.say("{\"statusCode\": 200, \"body\": \"dGVzdA=\", \"isBase64Encoded\": \"abc\"}")
+
+ elseif string.match(ngx.var.uri, "functionWithMultiValueHeadersResponse") then
+ ngx.header["Content-Type"] = "application/json"
+ ngx.say("{\"statusCode\": 200, \"headers\": { \"Age\": \"3600\"}, \"multiValueHeaders\": {\"Access-Control-Allow-Origin\": [\"site1.com\", \"site2.com\"]}}")
+
+ elseif string.match(ngx.var.uri, "functionEcho") then
+ require("spec.fixtures.mock_upstream").send_default_json_response()
+
+ elseif string.match(ngx.var.uri, "functionWithTransferEncodingHeader") then
+ ngx.say("{\"statusCode\": 200, \"headers\": { \"Transfer-Encoding\": \"chunked\", \"transfer-encoding\": \"chunked\"}}")
+
+ elseif string.match(ngx.var.uri, "functionWithLatency") then
+ -- additional latency
+ ngx.sleep(2)
+ ngx.say("{\"statusCodge\": 200, \"body\": \"dGVzdA=\", \"isBase64Encoded\": false}")
+
+ elseif string.match(ngx.var.uri, "functionWithEmptyArray") then
+ ngx.header["Content-Type"] = "application/json"
+ local str = "{\"statusCode\": 200, \"testbody\": [], \"isBase64Encoded\": false}"
+ ngx.say(str)
+
+ elseif type(res) == 'string' then
+ ngx.header["Content-Length"] = #res + 1
+ ngx.say(res)
+
+ else
+ ngx.req.discard_body()
+ ngx.header['Content-Length'] = 0
+ end
+
+ ngx.exit(0)
+ end
+
+ ngx.sleep(.2) -- mock some network latency
+
+ local invocation_type = ngx.var.http_x_amz_invocation_type
+ if invocation_type == 'Event' then
+ say(nil, 202)
+
+ elseif invocation_type == 'DryRun' then
+ say(nil, 204)
+ end
+
+ local qargs = ngx.req.get_uri_args()
+ ngx.req.read_body()
+ local request_body = ngx.req.get_body_data()
+ if request_body == nil then
+ local body_file = ngx.req.get_body_file()
+ if body_file then
+ ngx.log(ngx.DEBUG, "reading file cached to disk: ",body_file)
+ local file = io.open(body_file, "rb")
+ request_body = file:read("*all")
+ file:close()
+ end
+ end
+ print(request_body)
+ local args = require("cjson").decode(request_body)
+
+ say(request_body, 200)
+ end
+ local ok, err = pcall(x)
+ if not ok then
+ ngx.log(ngx.ERR, "Mock error: ", err)
+ end
+ }
+ }
+ }
+
+ ]]
+ },
+}
+
+fixtures.stream_mock = {
+ lambda_proxy = [[
+ server {
+ listen 13128;
+
+ content_by_lua_block {
+ require("spec.fixtures.forward-proxy-server").connect()
+ }
+ }
+ ]],
+}
+
+fixtures.dns_mock:A {
+ name = "lambda.us-east-1.amazonaws.com",
+ address = "127.0.0.1",
+}
+
+return fixtures
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/aws-sam.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/aws-sam.lua
new file mode 100644
index 00000000..ebf1bebc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/aws-sam.lua
@@ -0,0 +1,79 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--AWS SAM Local Test Helper
+local ngx_pipe = require "ngx.pipe"
+local helpers = require "spec.helpers"
+local utils = require "spec.helpers.perf.utils"
+local fmt = string.format
+
+local _M = {}
+
+
+--- Get system architecture by uname
+-- @function get_os_architecture
+-- @return architecture string if success, or nil and an error message
+function _M.get_os_architecture()
+ local ret, err = utils.execute("uname -m")
+
+ return ret, err
+end
+
+
+function _M.is_sam_installed()
+ local ret, err = utils.execute("sam --version")
+ if err then
+ return nil, fmt("SAM CLI version check failed(code: %s): %s", err, ret)
+ end
+
+ return true
+end
+
+local sam_proc
+
+
+function _M.start_local_lambda()
+ local port = helpers.get_available_port()
+ if not port then
+ return nil, "No available port found"
+ end
+
+ -- run in background
+ local err
+ sam_proc, err = ngx_pipe.spawn({"sam",
+ "local",
+ "start-lambda",
+ "--template-file", "spec/fixtures/sam-app/template.yaml",
+ "--port", port
+ })
+ if not sam_proc then
+ return nil, err
+ end
+
+ local ret, err = utils.execute("pgrep -f 'sam local'")
+ if err then
+ return nil, fmt("Start SAM CLI failed(code: %s): %s", err, ret)
+ end
+
+ return true, port
+end
+
+
+function _M.stop_local_lambda()
+ if sam_proc then
+ local ok, err = sam_proc:kill(15)
+ if not ok then
+ return nil, fmt("Stop SAM CLI failed: %s", err)
+ end
+ sam_proc = nil
+ end
+
+ return true
+end
+
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/balancer_utils.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/balancer_utils.lua
new file mode 100644
index 00000000..5ad68c5b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/balancer_utils.lua
@@ -0,0 +1,651 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cjson = require "cjson"
+local declarative = require "kong.db.declarative"
+local helpers = require "spec.helpers"
+local format_host = require("kong.tools.ip").format_host
+local kong_table = require "kong.tools.table"
+local https_server = require "spec.fixtures.https_server"
+local uuid = require("kong.tools.uuid").uuid
+
+
+local CONSISTENCY_FREQ = 1
+local HEALTHCHECK_INTERVAL = 1
+local SLOTS = 10
+local TEST_LOG = false -- extra verbose logging
+local healthchecks_defaults = {
+ active = {
+ timeout = 1,
+ concurrency = 10,
+ http_path = "/",
+ healthy = {
+ interval = 0, -- 0 = disabled by default
+ http_statuses = { 200, 302 },
+ successes = 2,
+ },
+ unhealthy = {
+ interval = 0, -- 0 = disabled by default
+ http_statuses = { 429, 404,
+ 500, 501, 502, 503, 504, 505 },
+ tcp_failures = 2,
+ timeouts = 3,
+ http_failures = 5,
+ },
+ },
+ passive = {
+ healthy = {
+ http_statuses = { 200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308 },
+ successes = 5,
+ },
+ unhealthy = {
+ http_statuses = { 429, 500, 503 },
+ tcp_failures = 2,
+ timeouts = 7,
+ http_failures = 5,
+ },
+ },
+}
+local get_available_port = helpers.get_available_port
+
+
+local prefix = ""
+
+
+local function healthchecks_config(config)
+ return kong_table.cycle_aware_deep_merge(healthchecks_defaults, config)
+end
+
+
+local function direct_request(host, port, path, protocol, host_header)
+ local pok, client = pcall(helpers.http_client, {
+ host = host,
+ port = port,
+ scheme = protocol,
+ })
+ if not pok then
+ return nil, "pcall: " .. client .. " : " .. host ..":"..port
+ end
+ if not client then
+ return nil, "client"
+ end
+
+ local res, err = client:send {
+ method = "GET",
+ path = path,
+ headers = { ["Host"] = host_header or host }
+ }
+ local body = res and res:read_body()
+ client:close()
+ if err then
+ return nil, err
+ end
+ return body
+end
+
+
+local function put_target_endpoint(upstream_id, host, port, endpoint)
+ if host == "[::1]" then
+ host = "[0000:0000:0000:0000:0000:0000:0000:0001]"
+ end
+ local path = "/upstreams/" .. upstream_id
+ .. "/targets/"
+ .. format_host(host, port)
+ .. "/" .. endpoint
+ local api_client = helpers.admin_client()
+ local res, err = assert(api_client:put(prefix .. path, {
+ headers = {
+ ["Content-Type"] = "application/json",
+ },
+ body = {},
+ }))
+ api_client:close()
+ return res, err
+end
+
+-- client_sync_request requires a route with
+-- hosts = { "200.test" } to sync requests
+local function client_sync_request(proxy_host , proxy_port)
+ -- kong have two port 9100(TCP) and 80(HTTP)
+ -- we just need to request http
+ if proxy_port == 9100 then
+ proxy_port = 80
+ end
+ local proxy_client = helpers.proxy_client({
+ host = proxy_host,
+ port = proxy_port,
+ })
+
+ local res = assert(proxy_client:send {
+ method = "GET",
+ headers = {
+ ["Host"] = "200.test",
+ },
+ path = "/",
+ })
+ local status = res.status
+ proxy_client:close()
+ return status == 200
+end
+
+local function client_requests(n, host_or_headers, proxy_host, proxy_port, protocol, uri)
+ local oks, fails = 0, 0
+ local last_status
+ for _ = 1, n do
+ -- hack sync avoid concurrency request
+ -- There is an issue here, if a request is completed and a response is received,
+ -- it does not necessarily mean that the log phase has been executed
+ -- (many operations require execution in the log phase, such as passive health checks),
+ -- so we need to ensure that the log phase has been completely executed here.
+ -- We choose to wait here for the log phase of the last connection to finish.
+ client_sync_request(proxy_host, proxy_port)
+ local client
+ if proxy_host and proxy_port then
+ client = helpers.http_client({
+ host = proxy_host,
+ port = proxy_port,
+ scheme = protocol,
+ })
+
+ else
+ if protocol == "https" then
+ client = helpers.proxy_ssl_client()
+ else
+ client = helpers.proxy_client()
+ end
+ end
+
+ local res = client:send {
+ method = "GET",
+ path = uri or "/",
+ headers = type(host_or_headers) == "string"
+ and { ["Host"] = host_or_headers }
+ or host_or_headers
+ or {}
+ }
+ if not res then
+ fails = fails + 1
+ if TEST_LOG then
+ print("FAIL (no body)")
+ end
+ elseif res.status == 200 then
+ oks = oks + 1
+ if TEST_LOG then
+ print("OK ", res.status, res:read_body())
+ end
+ elseif res.status > 399 then
+ fails = fails + 1
+ if TEST_LOG then
+ print("FAIL ", res.status, res:read_body())
+ end
+ end
+ last_status = res and res.status
+ client:close()
+ end
+ return oks, fails, last_status
+end
+
+
+local add_certificate
+local add_upstream
+local remove_upstream
+local patch_upstream
+local get_upstream
+local get_upstream_health
+local get_balancer_health
+local put_target_address_health
+local get_router_version
+local add_target
+local update_target
+local add_api
+local patch_api
+local gen_multi_host
+local invalidate_router
+do
+ local gen_sym
+ do
+ local sym = 0
+ gen_sym = function(name)
+ sym = sym + 1
+ return name .. "_" .. sym
+ end
+ end
+
+ local function api_send(method, path, body, forced_port)
+ local api_client = helpers.admin_client(nil, forced_port)
+ local res, err = api_client:send({
+ method = method,
+ path = prefix .. path,
+ headers = {
+ ["Content-Type"] = "application/json"
+ },
+ body = body,
+ })
+ if not res then
+ api_client:close()
+ return nil, err
+ end
+ local res_body = res.status ~= 204 and cjson.decode((res:read_body()))
+ api_client:close()
+ return res.status, res_body
+ end
+
+ add_certificate = function(bp, data)
+ local certificate_id = uuid()
+ local req = kong_table.cycle_aware_deep_copy(data) or {}
+ req.id = certificate_id
+ bp.certificates:insert(req)
+ return certificate_id
+ end
+
+ add_upstream = function(bp, data)
+ local upstream_id = uuid()
+ local req = kong_table.cycle_aware_deep_copy(data) or {}
+ local upstream_name = req.name or gen_sym("upstream")
+ req.name = upstream_name
+ req.slots = req.slots or SLOTS
+ req.id = upstream_id
+ bp.upstreams:insert(req)
+ return upstream_name, upstream_id
+ end
+
+ remove_upstream = function(bp, upstream_id)
+ bp.upstreams:remove({ id = upstream_id })
+ end
+
+ patch_upstream = function(upstream_id, data)
+ local res = api_send("PATCH", "/upstreams/" .. upstream_id, data)
+ assert(res == 200)
+ end
+
+ get_upstream = function(upstream_id, forced_port)
+ local path = "/upstreams/" .. upstream_id
+ local status, body = api_send("GET", path, nil, forced_port)
+ if status == 200 then
+ return body
+ end
+ end
+
+ get_upstream_health = function(upstream_id, forced_port)
+ local path = "/upstreams/" .. upstream_id .."/health"
+ local status, body = api_send("GET", path, nil, forced_port)
+ if status == 200 then
+ return body
+ end
+ end
+
+ get_balancer_health = function(upstream_id, forced_port)
+ local path = "/upstreams/" .. upstream_id .."/health?balancer_health=1"
+ local status, body = api_send("GET", path, nil, forced_port)
+ if status == 200 then
+ return body
+ end
+ end
+
+ put_target_address_health = function(upstream_id, target_id, address, mode, forced_port)
+ local path = "/upstreams/" .. upstream_id .. "/targets/" .. target_id .. "/" .. address .. "/" .. mode
+ return api_send("PUT", path, {}, forced_port)
+ end
+
+ get_router_version = function(forced_port)
+ local path = "/cache/router:version"
+ local status, body = api_send("GET", path, nil, forced_port)
+ if status == 200 then
+ return body.message
+ end
+ end
+
+ invalidate_router = function(forced_port)
+ local path = "/cache/router:version"
+ local status, body = api_send("DELETE", path, nil, forced_port)
+ if status == 204 then
+ return true
+ end
+
+ return nil, body
+ end
+
+ do
+ local host_num = 0
+ gen_multi_host = function()
+ host_num = host_num + 1
+ return "multiple-hosts-" .. tostring(host_num) .. ".test"
+ end
+ end
+
+ add_target = function(bp, upstream_id, host, port, data)
+ port = port or get_available_port()
+ local req = kong_table.cycle_aware_deep_copy(data) or {}
+ if host == "[::1]" then
+ host = "[0000:0000:0000:0000:0000:0000:0000:0001]"
+ end
+ req.target = req.target or format_host(host, port)
+ req.weight = req.weight or 10
+ req.upstream = { id = upstream_id }
+ local new_target = bp.targets:insert(req)
+ return port, new_target
+ end
+
+ update_target = function(bp, upstream_id, host, port, data)
+ local req = kong_table.cycle_aware_deep_copy(data) or {}
+ if host == "[::1]" then
+ host = "[0000:0000:0000:0000:0000:0000:0000:0001]"
+ end
+ req.target = req.target or format_host(host, port)
+ req.weight = req.weight or 10
+ req.upstream = { id = upstream_id }
+ bp.targets:update(req.id or req.target, req)
+ end
+
+ add_api = function(bp, upstream_name, opts)
+ opts = opts or {}
+ local route_id = uuid()
+ local service_id = uuid()
+ local route_host = gen_sym("host")
+ local sproto = opts.service_protocol or opts.route_protocol or "http"
+ local rproto = opts.route_protocol or "http"
+ local sport = rproto == "tcp" and 9100 or 80
+
+ local rpaths = {
+ "/",
+ "~/(?[^/]+)/(?[0-9]+)/?", -- uri capture hash value
+ }
+
+ -- add a 200 route to sync kong async thread
+ local route = bp.routes:insert {
+ hosts = { "200.test" },
+ }
+
+ bp.plugins:insert {
+ route = route,
+ name = "request-termination",
+ config = {
+ status_code = 200,
+ message = "Terminated"
+ },
+ }
+
+ bp.services:insert({
+ id = service_id,
+ host = upstream_name,
+ port = sport,
+ protocol = sproto,
+ read_timeout = opts.read_timeout,
+ write_timeout = opts.write_timeout,
+ connect_timeout = opts.connect_timeout,
+ retries = opts.retries,
+ })
+ bp.routes:insert({
+ id = route_id,
+ service = { id = service_id },
+ protocols = { rproto },
+ hosts = rproto ~= "tcp" and { route_host } or nil,
+ destinations = (rproto == "tcp") and {{ port = 9100 }} or nil,
+ paths = rproto ~= "tcp" and rpaths or nil,
+ })
+
+ bp.plugins:insert({
+ name = "post-function",
+ service = { id = service_id },
+ config = {
+ header_filter = {[[
+ local value = ngx.ctx and
+ ngx.ctx.balancer_data and
+ ngx.ctx.balancer_data.hash_value
+ if value == "" or value == nil then
+ value = "NONE"
+ end
+
+ ngx.header["x-balancer-hash-value"] = value
+ ngx.header["x-uri"] = ngx.var.request_uri
+ ]]},
+ },
+ })
+
+ return route_host, service_id, route_id
+ end
+
+ patch_api = function(bp, service_id, new_upstream, read_timeout)
+ bp.services:update(service_id, {
+ url = new_upstream,
+ read_timeout = read_timeout,
+ })
+ end
+end
+
+
+local poll_wait_health
+local poll_wait_address_health
+do
+ local function poll_wait(upstream_id, host, port, admin_port, fn)
+ if host == "[::1]" then
+ host = "[0000:0000:0000:0000:0000:0000:0000:0001]"
+ end
+ local hard_timeout = ngx.now() + 70
+ while ngx.now() < hard_timeout do
+ local health = get_upstream_health(upstream_id, admin_port)
+ if health then
+ for _, d in ipairs(health.data) do
+ if d.target == host .. ":" .. port and fn(d) then
+ return true
+ end
+ end
+ end
+ ngx.sleep(0.1) -- poll-wait
+ end
+ return false
+ end
+
+ poll_wait_health = function(upstream_id, host, port, value, admin_port)
+ local ok = poll_wait(upstream_id, host, port, admin_port, function(d)
+ return d.health == value
+ end)
+ if ok then
+ return true
+ end
+ assert(false, "timed out waiting for " .. host .. ":" .. port .. " in " ..
+ upstream_id .. " to become " .. value)
+ end
+
+ poll_wait_address_health = function(upstream_id, host, port, address_host, address_port, value)
+ local ok = poll_wait(upstream_id, host, port, nil, function(d)
+ for _, ad in ipairs(d.data.addresses) do
+ if ad.ip == address_host
+ and ad.port == address_port
+ and ad.health == value then
+ return true
+ end
+ end
+ end)
+ if ok then
+ return true
+ end
+ assert(false, "timed out waiting for " .. address_host .. ":" .. address_port .. " in " ..
+ upstream_id .. " to become " .. value)
+ end
+end
+
+
+local function wait_for_router_update(bp, old_rv, localhost, proxy_port, admin_port)
+ -- add dummy upstream just to rebuild router
+ local dummy_upstream_name, dummy_upstream_id = add_upstream(bp)
+ local dummy_port = add_target(bp, dummy_upstream_id, localhost)
+ local dummy_api_host = add_api(bp, dummy_upstream_name)
+ local dummy_server = https_server.new(dummy_port, localhost)
+ dummy_server:start()
+
+ -- forces the router to be rebuild, reduces the flakiness of the test suite
+ -- TODO: find out what's wrong with router invalidation in the particular
+ -- test setup causing the flakiness
+ assert(invalidate_router(admin_port))
+
+ helpers.wait_until(function()
+ client_requests(1, dummy_api_host, "127.0.0.1", proxy_port)
+ local rv = get_router_version(admin_port)
+ return rv ~= old_rv
+ end, 5)
+
+ dummy_server:shutdown()
+end
+
+
+local function tcp_client_requests(nreqs, host, port)
+ local fails, ok1, ok2 = 0, 0, 0
+ for _ = 1, nreqs do
+ local sock = ngx.socket.tcp()
+ assert(sock:connect(host, port))
+ assert(sock:send("hello\n"))
+ local response, err = sock:receive()
+ if err then
+ fails = fails + 1
+ elseif response:match("^1 ") then
+ ok1 = ok1 + 1
+ elseif response:match("^2 ") then
+ ok2 = ok2 + 1
+ end
+ end
+ return ok1, ok2, fails
+end
+
+
+local function begin_testcase_setup(strategy, bp)
+ if strategy == "off" then
+ bp.done()
+ end
+end
+
+local function begin_testcase_setup_update(strategy, bp)
+ if strategy == "off" then
+ bp.reset_back()
+ end
+end
+
+
+local function end_testcase_setup(strategy, bp)
+ if strategy == "off" then
+ -- setup some dummy entities for checking the config update status
+ local host = "localhost"
+ local port = get_available_port()
+
+ local server = https_server.new(port, host, "http", nil, 1)
+ server:start()
+
+ local upstream_name, upstream_id = add_upstream(bp)
+ add_target(bp, upstream_id, host, port)
+ local api_host = add_api(bp, upstream_name)
+
+ local cfg = bp.done()
+ local yaml = declarative.to_yaml_string(cfg)
+ local admin_client = helpers.admin_client()
+ local res = assert(admin_client:send {
+ method = "POST",
+ path = "/config",
+ body = {
+ config = yaml,
+ },
+ headers = {
+ ["Content-Type"] = "multipart/form-data",
+ }
+ })
+ assert(res ~= nil)
+ assert(res.status == 201)
+ admin_client:close()
+
+ local ok, err = pcall(function ()
+ -- wait for dummy config ready
+ helpers.pwait_until(function ()
+ local oks = client_requests(3, api_host)
+ assert(oks == 3)
+ end, 15)
+ end)
+
+ server:shutdown()
+
+
+ if not ok then
+ error(err)
+ end
+
+ else
+ helpers.wait_for_all_config_update()
+ end
+end
+
+
+local function get_db_utils_for_dc_and_admin_api(strategy, tables)
+ local bp = assert(helpers.get_db_utils(strategy, tables))
+ if strategy ~= "off" then
+ bp = require("spec.fixtures.admin_api")
+ end
+ return bp
+end
+
+local function setup_prefix(p)
+ prefix = p
+ local bp = require("spec.fixtures.admin_api")
+ bp.set_prefix(prefix)
+end
+
+
+local function teardown_prefix()
+ prefix = ""
+ local bp = require("spec.fixtures.admin_api")
+ bp.set_prefix(prefix)
+end
+
+local localhosts = {
+ ipv4 = "127.0.0.1",
+ ipv6 = "[::1]",
+ hostname = "localhost",
+}
+
+
+local consistencies = {"strict", "eventual"}
+
+
+local balancer_utils = {}
+--balancer_utils.
+balancer_utils.add_certificate = add_certificate
+balancer_utils.add_api = add_api
+balancer_utils.add_target = add_target
+balancer_utils.update_target = update_target
+balancer_utils.add_upstream = add_upstream
+balancer_utils.remove_upstream = remove_upstream
+balancer_utils.begin_testcase_setup = begin_testcase_setup
+balancer_utils.begin_testcase_setup_update = begin_testcase_setup_update
+balancer_utils.client_requests = client_requests
+balancer_utils.consistencies = consistencies
+balancer_utils.CONSISTENCY_FREQ = CONSISTENCY_FREQ
+balancer_utils.direct_request = direct_request
+balancer_utils.end_testcase_setup = end_testcase_setup
+balancer_utils.gen_multi_host = gen_multi_host
+balancer_utils.get_available_port = get_available_port
+balancer_utils.get_balancer_health = get_balancer_health
+balancer_utils.get_db_utils_for_dc_and_admin_api = get_db_utils_for_dc_and_admin_api
+balancer_utils.get_router_version = get_router_version
+balancer_utils.get_upstream = get_upstream
+balancer_utils.get_upstream_health = get_upstream_health
+balancer_utils.healthchecks_config = healthchecks_config
+balancer_utils.HEALTHCHECK_INTERVAL = HEALTHCHECK_INTERVAL
+balancer_utils.localhosts = localhosts
+balancer_utils.patch_api = patch_api
+balancer_utils.patch_upstream = patch_upstream
+balancer_utils.poll_wait_address_health = poll_wait_address_health
+balancer_utils.poll_wait_health = poll_wait_health
+balancer_utils.put_target_address_health = put_target_address_health
+balancer_utils.put_target_endpoint = put_target_endpoint
+balancer_utils.SLOTS = SLOTS
+balancer_utils.tcp_client_requests = tcp_client_requests
+balancer_utils.wait_for_router_update = wait_for_router_update
+
+-- XXX: EE [[
+balancer_utils.setup_prefix = setup_prefix
+balancer_utils.teardown_prefix = teardown_prefix
+-- ]]
+
+return balancer_utils
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/blueprints.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/blueprints.lua
new file mode 100644
index 00000000..4ebd5f6e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/blueprints.lua
@@ -0,0 +1,705 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ssl_fixtures = require "spec.fixtures.ssl"
+local utils = require "kong.tools.utils"
+local cjson = require "cjson"
+
+
+local cycle_aware_deep_merge = require("kong.tools.table").cycle_aware_deep_merge
+local fmt = string.format
+
+
+local Blueprint = {}
+Blueprint.__index = Blueprint
+
+
+-- TODO: port this back to OSS since it should be useful there too
+function Blueprint:defaults(defaults)
+ self._defaults = defaults
+end
+
+function Blueprint:build(overrides)
+ overrides = overrides or {}
+ if self._defaults then
+ overrides = cycle_aware_deep_merge(self._defaults, overrides)
+ end
+
+ return cycle_aware_deep_merge(self.build_function(overrides), overrides)
+end
+
+
+function Blueprint:insert(overrides, options)
+ local entity, err = self.dao:insert(self:build(overrides), options)
+ if err then
+ error(err, 2)
+ end
+ return entity
+end
+
+
+-- insert blueprint in workspace specified by `ws`
+function Blueprint:insert_ws(overrides, workspace)
+ local old_workspace = ngx.ctx.workspace
+
+ ngx.ctx.workspace = workspace.id
+ local entity = self:insert(overrides)
+ ngx.ctx.workspace = old_workspace
+
+ return entity
+end
+
+
+function Blueprint:remove(overrides, options)
+ local entity, err = self.dao:remove({ id = overrides.id }, options)
+ if err then
+ error(err, 2)
+ end
+ return entity
+end
+
+
+function Blueprint:update(id, overrides, options)
+ local entity, err = self.dao:update(id, overrides, options)
+ if err then
+ error(err, 2)
+ end
+ return entity
+end
+
+
+function Blueprint:upsert(id, overrides, options)
+ local entity, err = self.dao:upsert(id, overrides, options)
+ if err then
+ error(err, 2)
+ end
+ return entity
+end
+
+
+function Blueprint:insert_n(n, overrides, options)
+ local res = {}
+ for i=1,n do
+ res[i] = self:insert(overrides, options)
+ end
+ return res
+end
+
+function Blueprint:truncate()
+ local _, err = self.dao:truncate()
+ if err then
+ error(err, 2)
+ end
+ return true
+end
+
+local function new_blueprint(dao, build_function)
+ return setmetatable({
+ dao = dao,
+ build_function = build_function,
+ }, Blueprint)
+end
+
+
+local Sequence = {}
+Sequence.__index = Sequence
+
+
+function Sequence:next()
+ return fmt(self.sequence_string, self:gen())
+end
+
+function Sequence:gen()
+ self.count = self.count + 1
+ return self.count
+end
+
+local function new_sequence(sequence_string, gen)
+ return setmetatable({
+ count = 0,
+ sequence_string = sequence_string,
+ gen = gen,
+ }, Sequence)
+end
+
+
+local _M = {}
+
+
+function _M.new(db)
+ local res = {}
+
+ -- prepare Sequences and random values
+ local acl_group_seq = new_sequence("acl-group-%d")
+ local consumer_custom_id_seq = new_sequence("consumer-id-%s")
+ local consumer_username_seq = new_sequence("consumer-username-%s", utils.uuid)
+ local consumer_group_name_seq = new_sequence("consumer-group-name-%d")
+ local developer_email_seq = new_sequence("dev-%d@example.com")
+ local file_name_seq = new_sequence("file-path-%d.txt")
+ local group_name_seq = new_sequence("group-name-%d")
+ local hmac_username_seq = new_sequence("hmac-username-%d")
+ local jwt_key_seq = new_sequence("jwt-key-%d")
+ local key_sets_seq = new_sequence("key-sets-%d")
+ local keys_seq = new_sequence("keys-%d")
+ local keyauth_key_seq = new_sequence("keyauth-key-%d")
+ local named_service_name_seq = new_sequence("service-name-%d")
+ local named_service_host_seq = new_sequence("service-host-%d.test")
+ local named_route_name_seq = new_sequence("route-name-%d")
+ local named_route_host_seq = new_sequence("route-host-%d.test")
+ local oauth_code_seq = new_sequence("oauth-code-%d")
+ local plugin_name_seq = new_sequence("custom-plugin-%d")
+ local rbac_role_endpoint_seq = new_sequence("/rbac-role-endpoint-%d")
+ local rbac_user_name_seq = new_sequence("rbac-user-%d")
+ local rbac_roles_seq = new_sequence("rbac-role-%d")
+ local sni_seq = new_sequence("sni-%s", utils.uuid)
+ local upstream_name_seq = new_sequence("upstream-%s", utils.uuid)
+ local workspace_name_seq = new_sequence("workspace-name-%d")
+
+ local random_ip = tostring(math.random(1, 255)) .. "." ..
+ tostring(math.random(1, 255)) .. "." ..
+ tostring(math.random(1, 255)) .. "." ..
+ tostring(math.random(1, 255))
+ local random_target = random_ip .. ":" .. tostring(math.random(1, 65535))
+
+ res.snis = new_blueprint(db.snis, function(overrides)
+ return {
+ name = overrides.name or sni_seq:next(),
+ certificate = overrides.certificate or res.certificates:insert(),
+ }
+ end)
+
+ res.certificates = new_blueprint(db.certificates, function()
+ return {
+ cert = ssl_fixtures.cert,
+ key = ssl_fixtures.key,
+ }
+ end)
+
+ res.ca_certificates = new_blueprint(db.ca_certificates, function()
+ return {
+ cert = ssl_fixtures.cert_ca,
+ }
+ end)
+
+ res.upstreams = new_blueprint(db.upstreams, function(overrides)
+ return {
+ name = overrides.name or upstream_name_seq:next(),
+ slots = overrides.slots or 100,
+ host_header = overrides.host_header,
+ }
+ end)
+
+ res.consumers = new_blueprint(db.consumers, function()
+ return {
+ custom_id = consumer_custom_id_seq:next(),
+ username = consumer_username_seq:next(),
+ }
+ end)
+
+ res.developers = new_blueprint(db.developers, function()
+ return {
+ email = developer_email_seq:next(),
+ }
+ end)
+
+ res.targets = new_blueprint(db.targets, function(overrides)
+ return {
+ weight = overrides.weight or 10,
+ upstream = overrides.upstream or res.upstreams:insert(),
+ target = overrides.target or random_target,
+ }
+ end)
+
+ res.plugins = new_blueprint(db.plugins, function(overrides)
+ -- we currently don't know which plugin is enabled
+ return overrides or {}
+ end)
+
+ res.routes = new_blueprint(db.routes, function(overrides)
+ local service = overrides.service
+ local protocols = overrides.protocols
+
+ local route = {
+ service = service,
+ }
+
+ if overrides.no_service then
+ service = nil
+ overrides.no_service = nil
+ return {
+ service = service,
+ }
+ end
+
+
+ if type(service) == "table" then
+ -- set route.protocols from service
+ if service.protocol == "ws" or
+ service.protocol == "wss" and
+ not protocols
+ then
+ route.protocols = { service.protocol }
+ end
+
+ else
+ service = {}
+
+ -- set service.protocol from route.protocols
+ if type(protocols) == "table" then
+ for _, proto in ipairs(protocols) do
+ if proto == "ws" or proto == "wss" then
+ service.protocol = proto
+ break
+ end
+ end
+ end
+
+ service = res.services:insert(service)
+
+ -- reverse: set route.protocols based on the inserted service, which
+ -- may have inherited some defaults
+ if protocols == nil and
+ (service.protocol == "ws" or service.protocol == "wss")
+ then
+ route.protocols = { service.protocol }
+ end
+
+ route.service = service
+ end
+
+ return route
+ end)
+
+ res.services = new_blueprint(db.services, function(overrides)
+ local service = {
+ protocol = "http",
+ host = "127.0.0.1",
+ port = 15555,
+ }
+
+ service.protocol = overrides.protocol or service.protocol
+
+ if service.protocol == "ws" then
+ service.port = 3000
+
+ elseif service.protocol == "wss" then
+ service.port = 3001
+ end
+
+ return service
+ end)
+
+ res.vaults = new_blueprint(db.vaults, function(overrides)
+ local vault = {
+ name = "env",
+ prefix = "env-1",
+ description = "description",
+ }
+
+ vault.prefix = overrides.prefix or vault.prefix
+ vault.description = overrides.description or vault.description
+
+ return vault
+ end)
+
+ res.rbac_role_entities = new_blueprint(db.rbac_role_entities, function(overrides)
+ return {
+ role = overrides.role or res.rbac_roles:insert(),
+ entity_id = overrides.entity_id or res.routes:insert().id,
+ entity_type = overrides.entity_type or "route",
+ actions = overrides.actions or 15,
+ }
+ end)
+
+ res.rbac_role_endpoints = new_blueprint(db.rbac_role_endpoints, function (overrides)
+ return {
+ role = overrides.role or res.rbac_roles:insert(),
+ endpoint = overrides.endpoint or rbac_role_endpoint_seq:next(),
+ actions = overrides.action or 15,
+ }
+ end)
+
+ res.parameters = new_blueprint(db.parameters, function ()
+ return {
+ key = utils.uuid(),
+ value = utils.uuid(),
+ }
+ end)
+
+ res.login_attempts = new_blueprint(db.login_attempts, function (overrides)
+ return {
+ consumer = overrides.consumer or res.consumers:insert(),
+ attempts = { [random_ip] = math.random(1, 255) },
+ }
+ end)
+
+ res.legacy_files = new_blueprint(db.legacy_files, function (overrides)
+ return {
+ name = overrides.name or utils.uuid(),
+ type = overrides.type or "page",
+ contents = overrides.contents or utils.random_string(),
+ }
+ end)
+
+ res.keyring_meta = new_blueprint(db.keyring_meta, function ()
+ return {
+ id = utils.uuid()
+ }
+ end)
+
+ res.groups = new_blueprint(db.groups, function ()
+ return {
+ name = group_name_seq:next(),
+ }
+ end)
+
+ res.group_rbac_roles = new_blueprint(db.group_rbac_roles, function (overrides)
+ return {
+ group = overrides.group or res.groups:insert(),
+ rbac_role = overrides.rbac or res.rbac_roles:insert(),
+ workspace = overrides.workspace or res.workspaces:insert(),
+ }
+ end)
+
+ res.document_objects = new_blueprint(db.document_objects, function (overrides)
+ return {
+ service = overrides.service or res.services:insert(),
+ path = overrides.path or res.files:insert().path,
+ }
+ end)
+
+ res.files = new_blueprint(db.files, function ()
+ return {
+ path = file_name_seq:next(),
+ contents = utils.random_string(),
+ }
+ end)
+
+ res.event_hooks = new_blueprint(db.event_hooks, function (overrides)
+ local HANDLER = {
+ handler_name = "webhook",
+ config = {
+ url = "http://localhost/",
+ }
+ }
+ local event_hook = {
+ -- source = event_hook_source_seq:next(),
+ source = overrides.source,
+ handler = overrides.handler or HANDLER.handler_name,
+ config = overrides.config or HANDLER.config,
+ }
+ return event_hook
+ end)
+
+ res.credentials = new_blueprint(db.credentials, function(overrides)
+ local credential = {
+ consumer = overrides.consumer or res.consumers:insert(),
+ plugin = overrides.plugin_name or plugin_name_seq:next(),
+ credential_data = cjson.encode({ [utils.random_string()] = utils.random_string() }),
+ }
+ return credential
+ end)
+
+ res.consumer_groups = new_blueprint(db.consumer_groups, function(overrides)
+ return {
+ name = overrides.name or consumer_group_name_seq:next(),
+ id = overrides.id or utils.uuid()
+ }
+ end)
+
+ res.consumer_group_plugins = new_blueprint(db.consumer_group_plugins, function(overrides)
+ local consumer_group_plugins = {
+ consumer_group = overrides.consumer_group or res.consumer_groups:insert(),
+ name = "consumer-group-" .. utils.uuid(),
+ config = {
+ window_size = { math.random(1,100) },
+ window_type = overrides.config and overrides.config.window_type or "fixed",
+ limit = { math.random(1,100) },
+ }
+ }
+ return consumer_group_plugins
+ end)
+
+ res.consumer_group_consumers = new_blueprint(db.consumer_group_consumers, function(overrides)
+ return {
+ consumer = overrides.consumer or res.consumers:insert(),
+ consumer_group = overrides.consumer_group or res.consumer_groups:insert(),
+ }
+ end)
+
+ res.clustering_data_planes = new_blueprint(db.clustering_data_planes, function()
+ return {
+ hostname = "dp.example.com",
+ ip = "127.0.0.1",
+ config_hash = "a9a166c59873245db8f1a747ba9a80a7",
+ }
+ end)
+
+ res.named_services = new_blueprint(db.services, function()
+ return {
+ protocol = "http",
+ name = named_service_name_seq:next(),
+ host = named_service_host_seq:next(),
+ port = 15555,
+ }
+ end)
+
+ res.named_routes = new_blueprint(db.routes, function(overrides)
+ return {
+ name = named_route_name_seq:next(),
+ hosts = { named_route_host_seq:next() },
+ service = overrides.service or res.services:insert(),
+ }
+ end)
+
+ res.acl_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "acl",
+ config = {},
+ }
+ end)
+
+ res.acls = new_blueprint(db.acls, function()
+ return {
+ group = acl_group_seq:next(),
+ }
+ end)
+
+ res.cors_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "cors",
+ config = {
+ origins = { "example.com" },
+ methods = { "GET" },
+ headers = { "origin", "type", "accepts"},
+ exposed_headers = { "x-auth-token" },
+ max_age = 23,
+ credentials = true,
+ }
+ }
+ end)
+
+ res.loggly_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "loggly",
+ config = {}, -- all fields have default values already
+ }
+ end)
+
+ res.tcp_log_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "tcp-log",
+ config = {
+ host = "127.0.0.1",
+ port = 35001,
+ },
+ }
+ end)
+
+ res.udp_log_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "udp-log",
+ config = {
+ host = "127.0.0.1",
+ port = 35001,
+ },
+ }
+ end)
+
+ res.jwt_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "jwt",
+ config = {},
+ }
+ end)
+
+ res.jwt_secrets = new_blueprint(db.jwt_secrets, function()
+ return {
+ key = jwt_key_seq:next(),
+ secret = "secret",
+ }
+ end)
+
+ res.oauth2_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "oauth2",
+ config = {
+ scopes = { "email", "profile" },
+ enable_authorization_code = true,
+ mandatory_scope = true,
+ provision_key = "provision123",
+ token_expiration = 5,
+ enable_implicit_grant = true,
+ }
+ }
+ end)
+
+ res.oauth2_credentials = new_blueprint(db.oauth2_credentials, function()
+ return {
+ name = "oauth2 credential",
+ client_secret = "secret",
+ }
+ end)
+
+ res.oauth2_authorization_codes = new_blueprint(db.oauth2_authorization_codes, function()
+ return {
+ code = oauth_code_seq:next(),
+ scope = "default",
+ }
+ end)
+
+ res.oauth2_tokens = new_blueprint(db.oauth2_tokens, function()
+ return {
+ token_type = "bearer",
+ expires_in = 1000000000,
+ scope = "default",
+ }
+ end)
+
+ res.key_auth_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "key-auth",
+ config = {},
+ }
+ end)
+
+ res.keyauth_credentials = new_blueprint(db.keyauth_credentials, function()
+ return {
+ key = keyauth_key_seq:next(),
+ }
+ end)
+
+ local keyauth_enc_key_seq = new_sequence("keyauth-enc-key-%d")
+ res.keyauth_enc_credentials = new_blueprint(db.keyauth_enc_credentials, function()
+ return {
+ key = keyauth_enc_key_seq:next(),
+ }
+ end)
+
+ res.keyauth_enc_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "key-auths-enc",
+ config = {},
+ }
+ end)
+
+ res.basicauth_credentials = new_blueprint(db.basicauth_credentials, function()
+ return {}
+ end)
+
+ res.hmac_auth_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "hmac-auth",
+ config = {},
+ }
+ end)
+
+ res.hmacauth_credentials = new_blueprint(db.hmacauth_credentials, function()
+ return {
+ username = hmac_username_seq:next(),
+ secret = "secret",
+ }
+ end)
+
+ res.rate_limiting_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "rate-limiting",
+ config = {},
+ }
+ end)
+
+ res.response_ratelimiting_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "response-ratelimiting",
+ config = {},
+ }
+ end)
+
+ res.datadog_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "datadog",
+ config = {},
+ }
+ end)
+
+ res.statsd_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "statsd",
+ config = {},
+ }
+ end)
+
+ res.workspaces = new_blueprint(db.workspaces, function()
+ return {
+ name = workspace_name_seq:next(),
+ }
+ end)
+
+ res.rewriter_plugins = new_blueprint(db.plugins, function()
+ return {
+ name = "rewriter",
+ config = {},
+ }
+ end)
+
+ res.rbac_users = new_blueprint(db.rbac_users, function()
+ return {
+ name = rbac_user_name_seq:next(),
+ user_token = utils.uuid(),
+ }
+ end)
+
+ res.rbac_roles = new_blueprint(db.rbac_roles, function()
+ return {
+ name = rbac_roles_seq:next(),
+ }
+ end)
+
+ res.key_sets = new_blueprint(db.key_sets, function()
+ return {
+ name = key_sets_seq:next(),
+ }
+ end)
+
+ res.keys = new_blueprint(db.keys, function()
+ return {
+ name = keys_seq:next(),
+ }
+ end)
+
+ res.vaults = new_blueprint(db.vaults, function()
+ return {}
+ end)
+
+ local filter_chains_seq = new_sequence("filter-chains-%d")
+ res.filter_chains = new_blueprint(db.filter_chains, function()
+ return {
+ name = filter_chains_seq:next(),
+ }
+ end)
+
+ res.audit_requests = new_blueprint(db.audit_requests, function (overrides)
+ return {
+ method = overrides.method or "GET",
+ status = overrides.status or 200,
+ path = overrides.path or "/services",
+ client_ip = overrides.client_ip or "127.0.0.1",
+ }
+ end)
+
+ res.audit_objects = new_blueprint(db.audit_objects, function (overrides)
+ return {
+ operation = overrides.operation or "create",
+ dao_name = overrides.dao_name or "services"
+ }
+ end)
+
+ return res
+end
+
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/burst.yml b/kong-versions/test9.9.9.3/kong/spec/fixtures/burst.yml
new file mode 100644
index 00000000..46f03c31
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/burst.yml
@@ -0,0 +1,23649 @@
+_format_version: "1.1"
+services:
+- connect_timeout: 60000
+ host: something-static.4test-any.svc
+ name: 4test-any.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-any.example.test
+ name: 4test-any.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-any.svc
+ name: 4test-any.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-any.example.test
+ name: 4test-any.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-any.svc
+ name: 4test-any.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-any-admin.example.test
+ name: 4test-any.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-any.example.test
+ name: 4test-any.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-any.svc
+ name: 4test-any.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-any-admin.example.test
+ name: 4test-any.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-any.svc
+ name: 4test-any.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-any.example.test
+ name: 4test-any.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing1-otherthing.svc
+ name: 4test-athing1-otherthing.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-otherthing.example.test
+ name: 4test-athing1-otherthing.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing1-otherthing.svc
+ name: 4test-athing1-otherthing.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-otherthing.example.test
+ name: 4test-athing1-otherthing.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing1-otherthing.svc
+ name: 4test-athing1-otherthing.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-otherthing-admin.example.test
+ name: 4test-athing1-otherthing.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing1-otherthing.example.test
+ name: 4test-athing1-otherthing.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing1-otherthing.svc
+ name: 4test-athing1-otherthing.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-otherthing-admin.example.test
+ name: 4test-athing1-otherthing.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing1-otherthing.svc
+ name: 4test-athing1-otherthing.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-otherthing.example.test
+ name: 4test-athing1-otherthing.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing1-name.svc
+ name: 4test-athing1-name.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-name.example.test
+ name: 4test-athing1-name.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing1-name.svc
+ name: 4test-athing1-name.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-name.example.test
+ name: 4test-athing1-name.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing1-name.svc
+ name: 4test-athing1-name.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-name-admin.example.test
+ name: 4test-athing1-name.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing1-name.example.test
+ name: 4test-athing1-name.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing1-name.svc
+ name: 4test-athing1-name.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-name-admin.example.test
+ name: 4test-athing1-name.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing1-name.svc
+ name: 4test-athing1-name.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-name.example.test
+ name: 4test-athing1-name.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing1-othername.svc
+ name: 4test-athing1-othername.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-othername.example.test
+ name: 4test-athing1-othername.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing1-othername.svc
+ name: 4test-athing1-othername.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-othername.example.test
+ name: 4test-athing1-othername.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing1-othername.svc
+ name: 4test-athing1-othername.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-othername-admin.example.test
+ name: 4test-athing1-othername.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing1-othername.example.test
+ name: 4test-athing1-othername.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing1-othername.svc
+ name: 4test-athing1-othername.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-othername-admin.example.test
+ name: 4test-athing1-othername.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing1-othername.svc
+ name: 4test-athing1-othername.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-othername.example.test
+ name: 4test-athing1-othername.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing1-morenames.svc
+ name: 4test-athing1-morenames.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-morenames.example.test
+ name: 4test-athing1-morenames.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing1-morenames.svc
+ name: 4test-athing1-morenames.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-morenames.example.test
+ name: 4test-athing1-morenames.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing1-morenames.svc
+ name: 4test-athing1-morenames.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-morenames-admin.example.test
+ name: 4test-athing1-morenames.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing1-morenames.example.test
+ name: 4test-athing1-morenames.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing1-morenames.svc
+ name: 4test-athing1-morenames.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-morenames-admin.example.test
+ name: 4test-athing1-morenames.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing1-morenames.svc
+ name: 4test-athing1-morenames.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing1-morenames.example.test
+ name: 4test-athing1-morenames.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-thatthing.svc
+ name: 4test-thatthing.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-thatthing.example.test
+ name: 4test-thatthing.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-thatthing.svc
+ name: 4test-thatthing.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-thatthing.example.test
+ name: 4test-thatthing.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-thatthing.svc
+ name: 4test-thatthing.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-thatthing-admin.example.test
+ name: 4test-thatthing.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-thatthing.example.test
+ name: 4test-thatthing.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-thatthing.svc
+ name: 4test-thatthing.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-thatthing-admin.example.test
+ name: 4test-thatthing.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-thatthing.svc
+ name: 4test-thatthing.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-thatthing.example.test
+ name: 4test-thatthing.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing2-suchthing.svc
+ name: 4test-athing2-suchthing.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-suchthing.example.test
+ name: 4test-athing2-suchthing.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing2-suchthing.svc
+ name: 4test-athing2-suchthing.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-suchthing.example.test
+ name: 4test-athing2-suchthing.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing2-suchthing.svc
+ name: 4test-athing2-suchthing.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-suchthing-admin.example.test
+ name: 4test-athing2-suchthing.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing2-suchthing.example.test
+ name: 4test-athing2-suchthing.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing2-suchthing.svc
+ name: 4test-athing2-suchthing.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-suchthing-admin.example.test
+ name: 4test-athing2-suchthing.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing2-suchthing.svc
+ name: 4test-athing2-suchthing.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-suchthing.example.test
+ name: 4test-athing2-suchthing.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing2-wow.svc
+ name: 4test-athing2-wow.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-wow.example.test
+ name: 4test-athing2-wow.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing2-wow.svc
+ name: 4test-athing2-wow.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-wow.example.test
+ name: 4test-athing2-wow.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing2-wow.svc
+ name: 4test-athing2-wow.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-wow-admin.example.test
+ name: 4test-athing2-wow.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing2-wow.example.test
+ name: 4test-athing2-wow.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing2-wow.svc
+ name: 4test-athing2-wow.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-wow-admin.example.test
+ name: 4test-athing2-wow.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing2-wow.svc
+ name: 4test-athing2-wow.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing2-wow.example.test
+ name: 4test-athing2-wow.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-manythings.svc
+ name: 4test-manythings.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-manythings.example.test
+ name: 4test-manythings.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-manythings.svc
+ name: 4test-manythings.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-manythings.example.test
+ name: 4test-manythings.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-manythings.svc
+ name: 4test-manythings.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-manythings-admin.example.test
+ name: 4test-manythings.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-manythings.example.test
+ name: 4test-manythings.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-manythings.svc
+ name: 4test-manythings.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-manythings-admin.example.test
+ name: 4test-manythings.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-manythings.svc
+ name: 4test-manythings.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-manythings.example.test
+ name: 4test-manythings.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-verythings.svc
+ name: 4test-verythings.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-verythings.example.test
+ name: 4test-verythings.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-verythings.svc
+ name: 4test-verythings.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-verythings.example.test
+ name: 4test-verythings.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-verythings.svc
+ name: 4test-verythings.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-verythings-admin.example.test
+ name: 4test-verythings.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-verythings.example.test
+ name: 4test-verythings.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-verythings.svc
+ name: 4test-verythings.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-verythings-admin.example.test
+ name: 4test-verythings.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-verythings.svc
+ name: 4test-verythings.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-verythings.example.test
+ name: 4test-verythings.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing3-tester2.svc
+ name: 4test-athing3-tester2.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-tester2.example.test
+ name: 4test-athing3-tester2.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing3-tester2.svc
+ name: 4test-athing3-tester2.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-tester2.example.test
+ name: 4test-athing3-tester2.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing3-tester2.svc
+ name: 4test-athing3-tester2.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-tester2-admin.example.test
+ name: 4test-athing3-tester2.testing-ingress-admin.01
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing3-tester2.example.test
+ name: 4test-athing3-tester2.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static.4test-athing3-tester2.svc
+ name: 4test-athing3-tester2.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-tester2-admin.example.test
+ name: 4test-athing3-tester2.testing-ingress-admin.00
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing3-tester2.example.test
+ name: 4test-athing3-tester2.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing3-oldthings.svc
+ name: 4test-athing3-oldthings.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-oldthings.example.test
+ name: 4test-athing3-oldthings.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing3-oldthings.svc
+ name: 4test-athing3-oldthings.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-oldthings.example.test
+ name: 4test-athing3-oldthings.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing3-oldthings.svc
+ name: 4test-athing3-oldthings.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-oldthings-admin.example.test
+ name: 4test-athing3-oldthings.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing3-oldthings.example.test
+ name: 4test-athing3-oldthings.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing3-oldthings.svc
+ name: 4test-athing3-oldthings.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-oldthings-admin.example.test
+ name: 4test-athing3-oldthings.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing3-oldthings.svc
+ name: 4test-athing3-oldthings.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing3-oldthings.example.test
+ name: 4test-athing3-oldthings.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-demo1.svc
+ name: 4test-demo1.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo1.example.test
+ name: 4test-demo1.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-demo1.svc
+ name: 4test-demo1.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo1.example.test
+ name: 4test-demo1.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-demo1.svc
+ name: 4test-demo1.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo1-admin.example.test
+ name: 4test-demo1.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-demo1.example.test
+ name: 4test-demo1.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-demo1.svc
+ name: 4test-demo1.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo1-admin.example.test
+ name: 4test-demo1.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-demo1.svc
+ name: 4test-demo1.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo1.example.test
+ name: 4test-demo1.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-demo2.svc
+ name: 4test-demo2.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo2.example.test
+ name: 4test-demo2.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-demo2.svc
+ name: 4test-demo2.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo2.example.test
+ name: 4test-demo2.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-demo2.svc
+ name: 4test-demo2.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo2-admin.example.test
+ name: 4test-demo2.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-demo2.example.test
+ name: 4test-demo2.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-demo2.svc
+ name: 4test-demo2.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo2-admin.example.test
+ name: 4test-demo2.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-demo2.svc
+ name: 4test-demo2.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-demo2.example.test
+ name: 4test-demo2.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-dev.svc
+ name: 4test-dev.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-dev.example.test
+ name: 4test-dev.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-dev.svc
+ name: 4test-dev.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-dev.example.test
+ name: 4test-dev.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-dev.svc
+ name: 4test-dev.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-dev-admin.example.test
+ name: 4test-dev.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-dev.example.test
+ name: 4test-dev.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-dev.svc
+ name: 4test-dev.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-dev-admin.example.test
+ name: 4test-dev.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-dev.svc
+ name: 4test-dev.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-dev.example.test
+ name: 4test-dev.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing4-sothings.svc
+ name: 4test-athing4-sothings.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing4-sothings.example.test
+ name: 4test-athing4-sothings.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing4-sothings.svc
+ name: 4test-athing4-sothings.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing4-sothings.example.test
+ name: 4test-athing4-sothings.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing4-sothings.svc
+ name: 4test-athing4-sothings.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing4-sothings-admin.example.test
+ name: 4test-athing4-sothings.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing4-sothings.example.test
+ name: 4test-athing4-sothings.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing4-sothings.svc
+ name: 4test-athing4-sothings.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing4-sothings-admin.example.test
+ name: 4test-athing4-sothings.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing4-sothings.svc
+ name: 4test-athing4-sothings.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing4-sothings.example.test
+ name: 4test-athing4-sothings.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing5-cousin.svc
+ name: 4test-athing5-cousin.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-cousin.example.test
+ name: 4test-athing5-cousin.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing5-cousin.svc
+ name: 4test-athing5-cousin.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-cousin.example.test
+ name: 4test-athing5-cousin.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing5-cousin.svc
+ name: 4test-athing5-cousin.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-cousin-admin.example.test
+ name: 4test-athing5-cousin.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing5-cousin.example.test
+ name: 4test-athing5-cousin.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing5-cousin.svc
+ name: 4test-athing5-cousin.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-cousin-admin.example.test
+ name: 4test-athing5-cousin.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing5-cousin.svc
+ name: 4test-athing5-cousin.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-cousin.example.test
+ name: 4test-athing5-cousin.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing5-verythings.svc
+ name: 4test-athing5-verythings.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-verythings.example.test
+ name: 4test-athing5-verythings.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing5-verythings.svc
+ name: 4test-athing5-verythings.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-verythings.example.test
+ name: 4test-athing5-verythings.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing5-verythings.svc
+ name: 4test-athing5-verythings.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-verythings-admin.example.test
+ name: 4test-athing5-verythings.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing5-verythings.example.test
+ name: 4test-athing5-verythings.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing5-verythings.svc
+ name: 4test-athing5-verythings.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-verythings-admin.example.test
+ name: 4test-athing5-verythings.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing5-verythings.svc
+ name: 4test-athing5-verythings.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-verythings.example.test
+ name: 4test-athing5-verythings.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing5-ygbkm.svc
+ name: 4test-athing5-ygbkm.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-ygbkm.example.test
+ name: 4test-athing5-ygbkm.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing5-ygbkm.svc
+ name: 4test-athing5-ygbkm.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-ygbkm.example.test
+ name: 4test-athing5-ygbkm.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing5-ygbkm.svc
+ name: 4test-athing5-ygbkm.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-ygbkm-admin.example.test
+ name: 4test-athing5-ygbkm.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing5-ygbkm.example.test
+ name: 4test-athing5-ygbkm.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing5-ygbkm.svc
+ name: 4test-athing5-ygbkm.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-ygbkm-admin.example.test
+ name: 4test-athing5-ygbkm.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing5-ygbkm.svc
+ name: 4test-athing5-ygbkm.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-ygbkm.example.test
+ name: 4test-athing5-ygbkm.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing5-gothings.svc
+ name: 4test-athing5-gothings.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-gothings.example.test
+ name: 4test-athing5-gothings.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing5-gothings.svc
+ name: 4test-athing5-gothings.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-gothings.example.test
+ name: 4test-athing5-gothings.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing5-gothings.svc
+ name: 4test-athing5-gothings.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-gothings-admin.example.test
+ name: 4test-athing5-gothings.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing5-gothings.example.test
+ name: 4test-athing5-gothings.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing5-gothings.svc
+ name: 4test-athing5-gothings.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-gothings-admin.example.test
+ name: 4test-athing5-gothings.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing5-gothings.svc
+ name: 4test-athing5-gothings.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-gothings.example.test
+ name: 4test-athing5-gothings.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing5-clever.svc
+ name: 4test-athing5-clever.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-clever.example.test
+ name: 4test-athing5-clever.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing5-clever.svc
+ name: 4test-athing5-clever.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-clever.example.test
+ name: 4test-athing5-clever.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing5-clever.svc
+ name: 4test-athing5-clever.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-clever-admin.example.test
+ name: 4test-athing5-clever.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing5-clever.example.test
+ name: 4test-athing5-clever.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing5-clever.svc
+ name: 4test-athing5-clever.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-clever-admin.example.test
+ name: 4test-athing5-clever.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing5-clever.svc
+ name: 4test-athing5-clever.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing5-clever.example.test
+ name: 4test-athing5-clever.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing6-itis.svc
+ name: 4test-athing6-itis.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing6-itis.example.test
+ name: 4test-athing6-itis.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing6-itis.svc
+ name: 4test-athing6-itis.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing6-itis.example.test
+ name: 4test-athing6-itis.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing6-itis.svc
+ name: 4test-athing6-itis.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing6-itis-admin.example.test
+ name: 4test-athing6-itis.testing-ingress-admin.01
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing6-itis.example.test
+ name: 4test-athing6-itis.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static.4test-athing6-itis.svc
+ name: 4test-athing6-itis.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing6-itis-admin.example.test
+ name: 4test-athing6-itis.testing-ingress-admin.00
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing6-itis.example.test
+ name: 4test-athing6-itis.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-tldr.svc
+ name: 4test-tldr.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-tldr.example.test
+ name: 4test-tldr.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-tldr.svc
+ name: 4test-tldr.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-tldr.example.test
+ name: 4test-tldr.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-tldr.svc
+ name: 4test-tldr.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-tldr-admin.example.test
+ name: 4test-tldr.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-tldr.example.test
+ name: 4test-tldr.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-tldr.svc
+ name: 4test-tldr.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-tldr-admin.example.test
+ name: 4test-tldr.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-tldr.svc
+ name: 4test-tldr.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-tldr.example.test
+ name: 4test-tldr.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-shock.svc
+ name: 4test-shock.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-shock.example.test
+ name: 4test-shock.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-shock.svc
+ name: 4test-shock.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-shock.example.test
+ name: 4test-shock.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-shock.svc
+ name: 4test-shock.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-shock-admin.example.test
+ name: 4test-shock.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-shock.example.test
+ name: 4test-shock.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-shock.svc
+ name: 4test-shock.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-shock-admin.example.test
+ name: 4test-shock.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-shock.svc
+ name: 4test-shock.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-shock.example.test
+ name: 4test-shock.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-local.svc
+ name: 4test-local.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-local.example.test
+ name: 4test-local.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-local.svc
+ name: 4test-local.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-local.example.test
+ name: 4test-local.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-local.svc
+ name: 4test-local.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-local-admin.example.test
+ name: 4test-local.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-local.example.test
+ name: 4test-local.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-local.svc
+ name: 4test-local.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-local-admin.example.test
+ name: 4test-local.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-local.svc
+ name: 4test-local.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-local.example.test
+ name: 4test-local.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-newthings.svc
+ name: 4test-newthings.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-newthings.example.test
+ name: 4test-newthings.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-newthings.svc
+ name: 4test-newthings.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-newthings.example.test
+ name: 4test-newthings.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-newthings.svc
+ name: 4test-newthings.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-newthings-admin.example.test
+ name: 4test-newthings.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-newthings.example.test
+ name: 4test-newthings.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-newthings.svc
+ name: 4test-newthings.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-newthings-admin.example.test
+ name: 4test-newthings.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-newthings.svc
+ name: 4test-newthings.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-newthings.example.test
+ name: 4test-newthings.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing7-halftest.svc
+ name: 4test-athing7-halftest.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-halftest.example.test
+ name: 4test-athing7-halftest.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing7-halftest.svc
+ name: 4test-athing7-halftest.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-halftest.example.test
+ name: 4test-athing7-halftest.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing7-halftest.svc
+ name: 4test-athing7-halftest.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-halftest-admin.example.test
+ name: 4test-athing7-halftest.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing7-halftest.example.test
+ name: 4test-athing7-halftest.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing7-halftest.svc
+ name: 4test-athing7-halftest.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-halftest-admin.example.test
+ name: 4test-athing7-halftest.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing7-halftest.svc
+ name: 4test-athing7-halftest.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-halftest.example.test
+ name: 4test-athing7-halftest.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing7-moartests.svc
+ name: 4test-athing7-moartests.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-moartests.example.test
+ name: 4test-athing7-moartests.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing7-moartests.svc
+ name: 4test-athing7-moartests.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-moartests.example.test
+ name: 4test-athing7-moartests.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing7-moartests.svc
+ name: 4test-athing7-moartests.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-moartests-admin.example.test
+ name: 4test-athing7-moartests.testing-ingress-admin.01
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing7-moartests.example.test
+ name: 4test-athing7-moartests.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static.4test-athing7-moartests.svc
+ name: 4test-athing7-moartests.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-moartests-admin.example.test
+ name: 4test-athing7-moartests.testing-ingress-admin.00
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing7-moartests.example.test
+ name: 4test-athing7-moartests.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing7-gettests.svc
+ name: 4test-athing7-gettests.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-gettests.example.test
+ name: 4test-athing7-gettests.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing7-gettests.svc
+ name: 4test-athing7-gettests.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-gettests.example.test
+ name: 4test-athing7-gettests.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing7-gettests.svc
+ name: 4test-athing7-gettests.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-gettests-admin.example.test
+ name: 4test-athing7-gettests.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing7-gettests.example.test
+ name: 4test-athing7-gettests.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing7-gettests.svc
+ name: 4test-athing7-gettests.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-gettests-admin.example.test
+ name: 4test-athing7-gettests.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing7-gettests.svc
+ name: 4test-athing7-gettests.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing7-gettests.example.test
+ name: 4test-athing7-gettests.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-gottests.svc
+ name: 4test-gottests.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-gottests.example.test
+ name: 4test-gottests.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-gottests.svc
+ name: 4test-gottests.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-gottests.example.test
+ name: 4test-gottests.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-gottests.svc
+ name: 4test-gottests.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-gottests-admin.example.test
+ name: 4test-gottests.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-gottests.example.test
+ name: 4test-gottests.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-gottests.svc
+ name: 4test-gottests.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-gottests-admin.example.test
+ name: 4test-gottests.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-gottests.svc
+ name: 4test-gottests.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-gottests.example.test
+ name: 4test-gottests.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-fam.svc
+ name: 4test-fam.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-fam.example.test
+ name: 4test-fam.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-fam.svc
+ name: 4test-fam.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-fam.example.test
+ name: 4test-fam.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-fam.svc
+ name: 4test-fam.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-fam-admin.example.test
+ name: 4test-fam.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-fam.example.test
+ name: 4test-fam.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-fam.svc
+ name: 4test-fam.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-fam-admin.example.test
+ name: 4test-fam.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-fam.svc
+ name: 4test-fam.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-fam.example.test
+ name: 4test-fam.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing8-2007.svc
+ name: 4test-athing8-2007.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing8-2007.example.test
+ name: 4test-athing8-2007.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing8-2007.svc
+ name: 4test-athing8-2007.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing8-2007.example.test
+ name: 4test-athing8-2007.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing8-2007.svc
+ name: 4test-athing8-2007.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing8-2007-admin.example.test
+ name: 4test-athing8-2007.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing8-2007.example.test
+ name: 4test-athing8-2007.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing8-2007.svc
+ name: 4test-athing8-2007.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing8-2007-admin.example.test
+ name: 4test-athing8-2007.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing8-2007.svc
+ name: 4test-athing8-2007.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing8-2007.example.test
+ name: 4test-athing8-2007.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-grantests.svc
+ name: 4test-grantests.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-grantests.example.test
+ name: 4test-grantests.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-grantests.svc
+ name: 4test-grantests.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-grantests.example.test
+ name: 4test-grantests.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-grantests.svc
+ name: 4test-grantests.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-grantests-admin.example.test
+ name: 4test-grantests.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-grantests.example.test
+ name: 4test-grantests.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-grantests.svc
+ name: 4test-grantests.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-grantests-admin.example.test
+ name: 4test-grantests.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-grantests.svc
+ name: 4test-grantests.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-grantests.example.test
+ name: 4test-grantests.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-btests.svc
+ name: 4test-btests.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-btests.example.test
+ name: 4test-btests.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-btests.svc
+ name: 4test-btests.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-btests.example.test
+ name: 4test-btests.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-btests.svc
+ name: 4test-btests.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-btests-admin.example.test
+ name: 4test-btests.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-btests.example.test
+ name: 4test-btests.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-btests.svc
+ name: 4test-btests.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-btests-admin.example.test
+ name: 4test-btests.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-btests.svc
+ name: 4test-btests.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-btests.example.test
+ name: 4test-btests.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-lttl.svc
+ name: 4test-lttl.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-lttl.example.test
+ name: 4test-lttl.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-lttl.svc
+ name: 4test-lttl.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-lttl.example.test
+ name: 4test-lttl.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-lttl.svc
+ name: 4test-lttl.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-lttl-admin.example.test
+ name: 4test-lttl.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-lttl.example.test
+ name: 4test-lttl.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-lttl.svc
+ name: 4test-lttl.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-lttl-admin.example.test
+ name: 4test-lttl.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-lttl.svc
+ name: 4test-lttl.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-lttl.example.test
+ name: 4test-lttl.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing9-valid.svc
+ name: 4test-athing9-valid.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-valid.example.test
+ name: 4test-athing9-valid.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing9-valid.svc
+ name: 4test-athing9-valid.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-valid.example.test
+ name: 4test-athing9-valid.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing9-valid.svc
+ name: 4test-athing9-valid.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-valid-admin.example.test
+ name: 4test-athing9-valid.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing9-valid.example.test
+ name: 4test-athing9-valid.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing9-valid.svc
+ name: 4test-athing9-valid.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-valid-admin.example.test
+ name: 4test-athing9-valid.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing9-valid.svc
+ name: 4test-athing9-valid.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-valid.example.test
+ name: 4test-athing9-valid.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing9-morevalid.svc
+ name: 4test-athing9-morevalid.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-morevalid.example.test
+ name: 4test-athing9-morevalid.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing9-morevalid.svc
+ name: 4test-athing9-morevalid.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-morevalid.example.test
+ name: 4test-athing9-morevalid.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing9-morevalid.svc
+ name: 4test-athing9-morevalid.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-morevalid-admin.example.test
+ name: 4test-athing9-morevalid.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing9-morevalid.example.test
+ name: 4test-athing9-morevalid.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing9-morevalid.svc
+ name: 4test-athing9-morevalid.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-morevalid-admin.example.test
+ name: 4test-athing9-morevalid.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing9-morevalid.svc
+ name: 4test-athing9-morevalid.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-morevalid.example.test
+ name: 4test-athing9-morevalid.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing9-wanttotest.svc
+ name: 4test-athing9-wanttotest.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-wanttotest.example.test
+ name: 4test-athing9-wanttotest.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing9-wanttotest.svc
+ name: 4test-athing9-wanttotest.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-wanttotest.example.test
+ name: 4test-athing9-wanttotest.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing9-wanttotest.svc
+ name: 4test-athing9-wanttotest.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-wanttotest-admin.example.test
+ name: 4test-athing9-wanttotest.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing9-wanttotest.example.test
+ name: 4test-athing9-wanttotest.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing9-wanttotest.svc
+ name: 4test-athing9-wanttotest.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-wanttotest-admin.example.test
+ name: 4test-athing9-wanttotest.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing9-wanttotest.svc
+ name: 4test-athing9-wanttotest.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-wanttotest.example.test
+ name: 4test-athing9-wanttotest.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing9-whichtest.svc
+ name: 4test-athing9-whichtest.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-whichtest.example.test
+ name: 4test-athing9-whichtest.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing9-whichtest.svc
+ name: 4test-athing9-whichtest.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-whichtest.example.test
+ name: 4test-athing9-whichtest.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing9-whichtest.svc
+ name: 4test-athing9-whichtest.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-whichtest-admin.example.test
+ name: 4test-athing9-whichtest.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing9-whichtest.example.test
+ name: 4test-athing9-whichtest.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing9-whichtest.svc
+ name: 4test-athing9-whichtest.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-whichtest-admin.example.test
+ name: 4test-athing9-whichtest.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing9-whichtest.svc
+ name: 4test-athing9-whichtest.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-whichtest.example.test
+ name: 4test-athing9-whichtest.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing9-frank.svc
+ name: 4test-athing9-frank.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-frank.example.test
+ name: 4test-athing9-frank.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing9-nsf.svc
+ name: 4test-athing9-nsf.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-nsf.example.test
+ name: 4test-athing9-nsf.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing9-nsf.svc
+ name: 4test-athing9-nsf.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-nsf.example.test
+ name: 4test-athing9-nsf.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing9-nsf.svc
+ name: 4test-athing9-nsf.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-nsf-admin.example.test
+ name: 4test-athing9-nsf.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing9-nsf.example.test
+ name: 4test-athing9-nsf.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing9-nsf.svc
+ name: 4test-athing9-nsf.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-nsf-admin.example.test
+ name: 4test-athing9-nsf.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing9-nsf.svc
+ name: 4test-athing9-nsf.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing9-nsf.example.test
+ name: 4test-athing9-nsf.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing10-jnk.svc
+ name: 4test-athing10-jnk.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-jnk.example.test
+ name: 4test-athing10-jnk.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing10-jnk.svc
+ name: 4test-athing10-jnk.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-jnk.example.test
+ name: 4test-athing10-jnk.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing10-jnk.svc
+ name: 4test-athing10-jnk.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-jnk-admin.example.test
+ name: 4test-athing10-jnk.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing10-jnk.example.test
+ name: 4test-athing10-jnk.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing10-jnk.svc
+ name: 4test-athing10-jnk.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-jnk-admin.example.test
+ name: 4test-athing10-jnk.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing10-jnk.svc
+ name: 4test-athing10-jnk.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-jnk.example.test
+ name: 4test-athing10-jnk.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing10-local.svc
+ name: 4test-athing10-local.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-local.example.test
+ name: 4test-athing10-local.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing10-local.svc
+ name: 4test-athing10-local.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-local.example.test
+ name: 4test-athing10-local.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing10-local.svc
+ name: 4test-athing10-local.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-local-admin.example.test
+ name: 4test-athing10-local.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing10-local.example.test
+ name: 4test-athing10-local.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing10-local.svc
+ name: 4test-athing10-local.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-local-admin.example.test
+ name: 4test-athing10-local.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing10-local.svc
+ name: 4test-athing10-local.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-local.example.test
+ name: 4test-athing10-local.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing10-bbitw.svc
+ name: 4test-athing10-bbitw.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-bbitw.example.test
+ name: 4test-athing10-bbitw.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing10-bbitw.svc
+ name: 4test-athing10-bbitw.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-bbitw.example.test
+ name: 4test-athing10-bbitw.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing10-bbitw.svc
+ name: 4test-athing10-bbitw.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-bbitw-admin.example.test
+ name: 4test-athing10-bbitw.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing10-bbitw.example.test
+ name: 4test-athing10-bbitw.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing10-bbitw.svc
+ name: 4test-athing10-bbitw.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-bbitw-admin.example.test
+ name: 4test-athing10-bbitw.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing10-bbitw.svc
+ name: 4test-athing10-bbitw.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-bbitw.example.test
+ name: 4test-athing10-bbitw.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing10-gettests.svc
+ name: 4test-athing10-gettests.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-gettests.example.test
+ name: 4test-athing10-gettests.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing10-gettests.svc
+ name: 4test-athing10-gettests.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-gettests.example.test
+ name: 4test-athing10-gettests.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing10-gettests.svc
+ name: 4test-athing10-gettests.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-gettests-admin.example.test
+ name: 4test-athing10-gettests.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing10-gettests.example.test
+ name: 4test-athing10-gettests.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing10-gettests.svc
+ name: 4test-athing10-gettests.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-gettests-admin.example.test
+ name: 4test-athing10-gettests.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing10-gettests.svc
+ name: 4test-athing10-gettests.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-gettests.example.test
+ name: 4test-athing10-gettests.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-athing10-phn.svc
+ name: 4test-athing10-phn.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-phn.example.test
+ name: 4test-athing10-phn.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-athing10-phn.svc
+ name: 4test-athing10-phn.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-phn.example.test
+ name: 4test-athing10-phn.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-athing10-phn.svc
+ name: 4test-athing10-phn.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-tim-admin.example.test
+ name: 4test-athing10-phn.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-athing10-phn.example.test
+ name: 4test-athing10-phn.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-athing10-phn.svc
+ name: 4test-athing10-phn.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-tim-admin.example.test
+ name: 4test-athing10-phn.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-athing10-phn.svc
+ name: 4test-athing10-phn.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-athing10-phn.example.test
+ name: 4test-athing10-phn.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-test1.svc
+ name: 4test-test1.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test1.example.test
+ name: 4test-test1.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-test1.svc
+ name: 4test-test1.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test1.example.test
+ name: 4test-test1.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-test1.svc
+ name: 4test-test1.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test1-admin.example.test
+ name: 4test-test1.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-test1.example.test
+ name: 4test-test1.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-test1.svc
+ name: 4test-test1.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test1-admin.example.test
+ name: 4test-test1.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-test1.svc
+ name: 4test-test1.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test1.example.test
+ name: 4test-test1.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: something-static.4test-test2.svc
+ name: 4test-test2.something-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test2.example.test
+ name: 4test-test2.something-ingress-static.00
+ paths:
+ - /something/4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing-ws.4test-test2.svc
+ name: 4test-test2.testing-testing-ws.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test2.example.test
+ name: 4test-test2.testing-ingress-app.01
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/ws
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-testing.4test-test2.svc
+ name: 4test-test2.testing-testing.8000
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test2-admin.example.test
+ name: 4test-test2.testing-ingress-admin.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+ - hosts:
+ - 4test-test2.example.test
+ name: 4test-test2.testing-ingress-app.00
+ methods:
+ - POST
+ - GET
+ - PATCH
+ - PUT
+ - DELETE
+ paths:
+ - /4test/api
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: false
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: testing-static-admin.4test-test2.svc
+ name: 4test-test2.testing-static-admin.80
+ path: /static/admin
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test2-admin.example.test
+ name: 4test-test2.testing-ingress-admin-static.00
+ methods:
+ - GET
+ paths:
+ - /4test/static/admin
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+ plugins:
+ - name: ip-restriction
+ config:
+ deny: null
+ allow:
+ - 0.0.0.0/0
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
+- connect_timeout: 60000
+ host: testing-static.4test-test2.svc
+ name: 4test-test2.testing-static.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - 4test-test2.example.test
+ name: 4test-test2.testing-ingress-static.00
+ paths:
+ - /4test
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: konga.kong.svc
+ name: kong.konga.http
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - konga-k8s-demo01-kong.example.test
+ name: kong.konga.00
+ paths:
+ - /
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: grafana.monitoring.svc
+ name: monitoring.grafana.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - monitor-k8s-demo01-kong.example.test
+ name: monitoring.grafana.00
+ paths:
+ - /
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+- connect_timeout: 60000
+ host: prometheus-alertmanager.monitoring.svc
+ name: monitoring.prometheus-alertmanager.80
+ path: /
+ port: 80
+ protocol: http
+ read_timeout: 60000
+ retries: 5
+ write_timeout: 60000
+ routes:
+ - hosts:
+ - alerts-k8s-demo01.example.test
+ name: monitoring.prometheus-alertmanager.00
+ paths:
+ - /
+ preserve_host: true
+ protocols:
+ - http
+ - https
+ regex_priority: 0
+ strip_path: true
+ https_redirect_status_code: 426
+upstreams:
+- name: grafana.monitoring.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.48:3000
+ weight: 100
+- name: konga.kong.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.223.36:1337
+ weight: 100
+- name: prometheus-alertmanager.monitoring.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.142:9093
+ weight: 100
+- name: something-static.4test-any.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.117:80
+ weight: 100
+- name: something-static.4test-athing1-otherthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.249:80
+ weight: 100
+- name: something-static.4test-athing1-name.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.172:80
+ weight: 100
+- name: something-static.4test-athing1-othername.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.96.92.92:80
+ weight: 100
+- name: something-static.4test-athing1-morenames.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.209:80
+ weight: 100
+- name: something-static.4test-thatthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.31.239:80
+ weight: 100
+- name: something-static.4test-athing2-suchthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.134:80
+ weight: 100
+- name: something-static.4test-athing2-wow.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.159:80
+ weight: 100
+- name: something-static.4test-manythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.180.113:80
+ weight: 100
+- name: something-static.4test-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.52:80
+ weight: 100
+- name: something-static.4test-athing3-tester2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.20:80
+ weight: 100
+- name: something-static.4test-athing3-oldthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.132:80
+ weight: 100
+- name: something-static.4test-demo1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.35:80
+ weight: 100
+- name: something-static.4test-demo2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.235:80
+ weight: 100
+- name: something-static.4test-dev.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.172:80
+ weight: 100
+- name: something-static.4test-athing4-sothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.74:80
+ weight: 100
+- name: something-static.4test-athing5-cousin.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.47:80
+ weight: 100
+- name: something-static.4test-athing5-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.126:80
+ weight: 100
+- name: something-static.4test-athing5-ygbkm.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.107:80
+ weight: 100
+- name: something-static.4test-athing5-gothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.30:80
+ weight: 100
+- name: something-static.4test-athing5-clever.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.72:80
+ weight: 100
+- name: something-static.4test-athing6-itis.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.101.121:80
+ weight: 100
+- name: something-static.4test-tldr.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.100:80
+ weight: 100
+- name: something-static.4test-shock.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.171:80
+ weight: 100
+- name: something-static.4test-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.107.57.110:80
+ weight: 100
+- name: something-static.4test-newthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.49:80
+ weight: 100
+- name: something-static.4test-athing7-halftest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.152:80
+ weight: 100
+- name: something-static.4test-athing7-moartests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.119.243.142:80
+ weight: 100
+- name: something-static.4test-athing7-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.107.57.83:80
+ weight: 100
+- name: something-static.4test-gottests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.20:80
+ weight: 100
+- name: something-static.4test-fam.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.96.92.105:80
+ weight: 100
+- name: something-static.4test-athing8-2007.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.132:80
+ weight: 100
+- name: something-static.4test-grantests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.89:80
+ weight: 100
+- name: something-static.4test-btests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.205:80
+ weight: 100
+- name: something-static.4test-lttl.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.119.243.166:80
+ weight: 100
+- name: something-static.4test-athing9-valid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.150:80
+ weight: 100
+- name: something-static.4test-athing9-morevalid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.100:80
+ weight: 100
+- name: something-static.4test-athing9-wanttotest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.121:80
+ weight: 100
+- name: something-static.4test-athing9-whichtest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.119:80
+ weight: 100
+- name: something-static.4test-athing9-frank.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.141:80
+ weight: 100
+- name: something-static.4test-athing9-nsf.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.187:80
+ weight: 100
+- name: something-static.4test-athing10-jnk.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.40:80
+ weight: 100
+- name: something-static.4test-athing10-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.99.213.12:80
+ weight: 100
+- name: something-static.4test-athing10-bbitw.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.5:80
+ weight: 100
+- name: something-static.4test-athing10-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.69:80
+ weight: 100
+- name: something-static.4test-athing10-phn.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.106:80
+ weight: 100
+- name: something-static.4test-test1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.223.44:80
+ weight: 100
+- name: something-static.4test-test2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.136:80
+ weight: 100
+- name: testing-testing-ws.4test-any.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.24:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing1-otherthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.169:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing1-name.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.174:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing1-othername.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.91:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing1-morenames.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.180.74:8000
+ weight: 100
+- name: testing-testing-ws.4test-thatthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.216:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing2-suchthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.85:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing2-wow.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.179:8000
+ weight: 100
+- name: testing-testing-ws.4test-manythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.108:8000
+ weight: 100
+- name: testing-testing-ws.4test-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.92:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing3-tester2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.105:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing3-oldthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.109:8000
+ weight: 100
+- name: testing-testing-ws.4test-demo1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.15:8000
+ weight: 100
+- name: testing-testing-ws.4test-demo2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.107.57.97:8000
+ weight: 100
+- name: testing-testing-ws.4test-dev.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.21.148:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing4-sothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.12:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing5-cousin.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.17:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing5-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.99:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing5-ygbkm.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.172:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing5-gothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.165:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing5-clever.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.173:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing6-itis.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.23:8000
+ weight: 100
+- name: testing-testing-ws.4test-tldr.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.119.243.162:8000
+ weight: 100
+- name: testing-testing-ws.4test-shock.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.107.57.114:8000
+ weight: 100
+- name: testing-testing-ws.4test-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.221:8000
+ weight: 100
+- name: testing-testing-ws.4test-newthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.180.117:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing7-halftest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.31.196:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing7-moartests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.71:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing7-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.147:8000
+ weight: 100
+- name: testing-testing-ws.4test-gottests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.179:8000
+ weight: 100
+- name: testing-testing-ws.4test-fam.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.60:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing8-2007.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.96.92.72:8000
+ weight: 100
+- name: testing-testing-ws.4test-grantests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.151:8000
+ weight: 100
+- name: testing-testing-ws.4test-btests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.232:8000
+ weight: 100
+- name: testing-testing-ws.4test-lttl.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.166:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing9-valid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.15:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing9-morevalid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.63:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing9-wanttotest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.106:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing9-whichtest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.88:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing9-nsf.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.115:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing10-jnk.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.31.225:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing10-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.99.213.13:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing10-bbitw.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.179:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing10-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.120.185.28:8000
+ weight: 100
+- name: testing-testing-ws.4test-athing10-phn.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.99.213.46:8000
+ weight: 100
+- name: testing-testing-ws.4test-test1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.137:8000
+ weight: 100
+- name: testing-testing-ws.4test-test2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.223.49:8000
+ weight: 100
+- name: testing-testing.4test-any.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.54:8000
+ weight: 100
+- name: testing-testing.4test-athing1-otherthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.169:8000
+ weight: 100
+- name: testing-testing.4test-athing1-name.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.101.70:8000
+ weight: 100
+- name: testing-testing.4test-athing1-othername.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.11:8000
+ weight: 100
+- name: testing-testing.4test-athing1-morenames.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.96.92.101:8000
+ weight: 100
+- name: testing-testing.4test-thatthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.11:8000
+ weight: 100
+- name: testing-testing.4test-athing2-suchthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.202:8000
+ weight: 100
+- name: testing-testing.4test-athing2-wow.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.180.98:8000
+ weight: 100
+- name: testing-testing.4test-manythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.81:8000
+ weight: 100
+- name: testing-testing.4test-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.168:8000
+ weight: 100
+- name: testing-testing.4test-athing3-tester2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.21.161:8000
+ weight: 100
+- name: testing-testing.4test-athing3-oldthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.21.152:8000
+ weight: 100
+- name: testing-testing.4test-demo1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.119.243.153:8000
+ weight: 100
+- name: testing-testing.4test-demo2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.107.57.116:8000
+ weight: 100
+- name: testing-testing.4test-dev.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.26:8000
+ weight: 100
+- name: testing-testing.4test-athing4-sothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.119.243.159:8000
+ weight: 100
+- name: testing-testing.4test-athing5-cousin.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.127:8000
+ weight: 100
+- name: testing-testing.4test-athing5-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.109:8000
+ weight: 100
+- name: testing-testing.4test-athing5-ygbkm.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.101.122:8000
+ weight: 100
+- name: testing-testing.4test-athing5-gothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.59:8000
+ weight: 100
+- name: testing-testing.4test-athing5-clever.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.228.184:8000
+ weight: 100
+- name: testing-testing.4test-athing6-itis.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.21.179:8000
+ weight: 100
+- name: testing-testing.4test-tldr.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.29:8000
+ weight: 100
+- name: testing-testing.4test-shock.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.225:8000
+ weight: 100
+- name: testing-testing.4test-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.34:8000
+ weight: 100
+ - target: 100.114.171.74:8000
+ weight: 100
+- name: testing-testing.4test-newthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.36:8000
+ weight: 100
+- name: testing-testing.4test-athing7-halftest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.101.113:8000
+ weight: 100
+- name: testing-testing.4test-athing7-moartests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.96.92.109:8000
+ weight: 100
+- name: testing-testing.4test-athing7-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.188:8000
+ weight: 100
+- name: testing-testing.4test-gottests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.119.243.160:8000
+ weight: 100
+- name: testing-testing.4test-fam.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.59:8000
+ weight: 100
+- name: testing-testing.4test-athing8-2007.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.96.92.117:8000
+ weight: 100
+- name: testing-testing.4test-grantests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.58:8000
+ weight: 100
+- name: testing-testing.4test-btests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.237:8000
+ weight: 100
+- name: testing-testing.4test-lttl.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.146:8000
+ weight: 100
+- name: testing-testing.4test-athing9-valid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.18:8000
+ weight: 100
+- name: testing-testing.4test-athing9-morevalid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.255:8000
+ weight: 100
+- name: testing-testing.4test-athing9-wanttotest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.99:8000
+ weight: 100
+- name: testing-testing.4test-athing9-whichtest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.36:8000
+ weight: 100
+- name: testing-testing.4test-athing9-nsf.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.114:8000
+ weight: 100
+- name: testing-testing.4test-athing10-jnk.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.120.185.31:8000
+ weight: 100
+- name: testing-testing.4test-athing10-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.99.213.15:8000
+ weight: 100
+- name: testing-testing.4test-athing10-bbitw.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.120.185.24:8000
+ weight: 100
+- name: testing-testing.4test-athing10-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.120.185.27:8000
+ weight: 100
+- name: testing-testing.4test-athing10-phn.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.99.213.47:8000
+ weight: 100
+- name: testing-testing.4test-test1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.223.25:8000
+ weight: 100
+- name: testing-testing.4test-test2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.165:8000
+ weight: 100
+- name: testing-static-admin.4test-any.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.98:80
+ weight: 100
+- name: testing-static-admin.4test-athing1-otherthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.251:80
+ weight: 100
+- name: testing-static-admin.4test-athing1-name.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.147:80
+ weight: 100
+- name: testing-static-admin.4test-athing1-othername.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.72:80
+ weight: 100
+- name: testing-static-admin.4test-athing1-morenames.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.132:80
+ weight: 100
+- name: testing-static-admin.4test-thatthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.95:80
+ weight: 100
+- name: testing-static-admin.4test-athing2-suchthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.87:80
+ weight: 100
+- name: testing-static-admin.4test-athing2-wow.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.71:80
+ weight: 100
+- name: testing-static-admin.4test-manythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.180.73:80
+ weight: 100
+- name: testing-static-admin.4test-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.17:80
+ weight: 100
+- name: testing-static-admin.4test-athing3-oldthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.21.183:80
+ weight: 100
+- name: testing-static-admin.4test-demo1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.32:80
+ weight: 100
+- name: testing-static-admin.4test-demo2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.50:80
+ weight: 100
+- name: testing-static-admin.4test-dev.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.167:80
+ weight: 100
+- name: testing-static-admin.4test-athing4-sothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.157:80
+ weight: 100
+- name: testing-static-admin.4test-athing5-cousin.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.89:80
+ weight: 100
+- name: testing-static-admin.4test-athing5-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.118:80
+ weight: 100
+- name: testing-static-admin.4test-athing5-ygbkm.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.105:80
+ weight: 100
+- name: testing-static-admin.4test-athing5-gothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.27:80
+ weight: 100
+- name: testing-static-admin.4test-athing5-clever.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.237:80
+ weight: 100
+- name: testing-static-admin.4test-tldr.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.94:80
+ weight: 100
+- name: testing-static-admin.4test-shock.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.170:80
+ weight: 100
+- name: testing-static-admin.4test-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.125:80
+ weight: 100
+- name: testing-static-admin.4test-newthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.51:80
+ weight: 100
+- name: testing-static-admin.4test-athing7-halftest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.132:80
+ weight: 100
+- name: testing-static-admin.4test-athing7-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.44:80
+ weight: 100
+- name: testing-static-admin.4test-gottests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.207:80
+ weight: 100
+- name: testing-static-admin.4test-fam.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.149:80
+ weight: 100
+- name: testing-static-admin.4test-athing8-2007.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.34:80
+ weight: 100
+- name: testing-static-admin.4test-grantests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.13:80
+ weight: 100
+- name: testing-static-admin.4test-btests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.222:80
+ weight: 100
+- name: testing-static-admin.4test-lttl.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.244:80
+ weight: 100
+- name: testing-static-admin.4test-athing9-valid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.5:80
+ weight: 100
+- name: testing-static-admin.4test-athing9-morevalid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.178:80
+ weight: 100
+- name: testing-static-admin.4test-athing9-wanttotest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.100:80
+ weight: 100
+- name: testing-static-admin.4test-athing9-whichtest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.77:80
+ weight: 100
+- name: testing-static-admin.4test-athing9-nsf.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.116:80
+ weight: 100
+- name: testing-static-admin.4test-athing10-jnk.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.31.222:80
+ weight: 100
+- name: testing-static-admin.4test-athing10-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.99.213.14:80
+ weight: 100
+- name: testing-static-admin.4test-athing10-bbitw.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.38:80
+ weight: 100
+- name: testing-static-admin.4test-athing10-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.120.185.29:80
+ weight: 100
+- name: testing-static-admin.4test-athing10-phn.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.79:80
+ weight: 100
+- name: testing-static-admin.4test-test1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.228:80
+ weight: 100
+- name: testing-static-admin.4test-test2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.189:80
+ weight: 100
+- name: testing-static.4test-any.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.98:80
+ weight: 100
+- name: testing-static.4test-athing1-otherthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.251:80
+ weight: 100
+- name: testing-static.4test-athing1-name.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.147:80
+ weight: 100
+- name: testing-static.4test-athing1-othername.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.72:80
+ weight: 100
+- name: testing-static.4test-athing1-morenames.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.132:80
+ weight: 100
+- name: testing-static.4test-thatthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.95:80
+ weight: 100
+- name: testing-static.4test-athing2-suchthing.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.87:80
+ weight: 100
+- name: testing-static.4test-athing2-wow.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.91.71:80
+ weight: 100
+- name: testing-static.4test-manythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.180.73:80
+ weight: 100
+- name: testing-static.4test-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.17:80
+ weight: 100
+- name: testing-static.4test-athing3-tester2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.21.160:80
+ weight: 100
+- name: testing-static.4test-athing3-oldthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.127.21.183:80
+ weight: 100
+- name: testing-static.4test-demo1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.32:80
+ weight: 100
+- name: testing-static.4test-demo2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.50:80
+ weight: 100
+- name: testing-static.4test-dev.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.167:80
+ weight: 100
+- name: testing-static.4test-athing4-sothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.157:80
+ weight: 100
+- name: testing-static.4test-athing5-cousin.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.89:80
+ weight: 100
+- name: testing-static.4test-athing5-verythings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.118:80
+ weight: 100
+- name: testing-static.4test-athing5-ygbkm.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.105:80
+ weight: 100
+- name: testing-static.4test-athing5-gothings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.27:80
+ weight: 100
+- name: testing-static.4test-athing5-clever.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.237:80
+ weight: 100
+- name: testing-static.4test-athing6-itis.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.115.236.22:80
+ weight: 100
+- name: testing-static.4test-tldr.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.94:80
+ weight: 100
+- name: testing-static.4test-shock.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.222.170:80
+ weight: 100
+- name: testing-static.4test-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.97.79.125:80
+ weight: 100
+- name: testing-static.4test-newthings.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.85.51:80
+ weight: 100
+- name: testing-static.4test-athing7-halftest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.132:80
+ weight: 100
+- name: testing-static.4test-athing7-moartests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.122.180.94:80
+ weight: 100
+- name: testing-static.4test-athing7-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.121.148.44:80
+ weight: 100
+- name: testing-static.4test-gottests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.207:80
+ weight: 100
+- name: testing-static.4test-fam.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.1.149:80
+ weight: 100
+- name: testing-static.4test-athing8-2007.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.34:80
+ weight: 100
+- name: testing-static.4test-grantests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.13:80
+ weight: 100
+- name: testing-static.4test-btests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.197.222:80
+ weight: 100
+- name: testing-static.4test-lttl.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.244:80
+ weight: 100
+- name: testing-static.4test-athing9-valid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.103.246.5:80
+ weight: 100
+- name: testing-static.4test-athing9-morevalid.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.105.108.178:80
+ weight: 100
+- name: testing-static.4test-athing9-wanttotest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.123.141.100:80
+ weight: 100
+- name: testing-static.4test-athing9-whichtest.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.77:80
+ weight: 100
+- name: testing-static.4test-athing9-nsf.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.229.116:80
+ weight: 100
+- name: testing-static.4test-athing10-jnk.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.113.31.222:80
+ weight: 100
+- name: testing-static.4test-athing10-local.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.99.213.14:80
+ weight: 100
+- name: testing-static.4test-athing10-bbitw.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.112.140.38:80
+ weight: 100
+- name: testing-static.4test-athing10-gettests.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.120.185.29:80
+ weight: 100
+- name: testing-static.4test-athing10-phn.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.114.171.79:80
+ weight: 100
+- name: testing-static.4test-test1.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.98.136.228:80
+ weight: 100
+- name: testing-static.4test-test2.svc
+ algorithm: round-robin
+ slots: 10000
+ healthchecks:
+ active:
+ concurrency: 10
+ healthy:
+ http_statuses:
+ - 200
+ - 302
+ interval: 0
+ successes: 0
+ http_path: /
+ https_verify_certificate: true
+ type: http
+ timeout: 1
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 404
+ - 500
+ - 501
+ - 502
+ - 503
+ - 504
+ - 505
+ tcp_failures: 0
+ timeouts: 0
+ interval: 0
+ passive:
+ healthy:
+ http_statuses:
+ - 200
+ - 201
+ - 202
+ - 203
+ - 204
+ - 205
+ - 206
+ - 207
+ - 208
+ - 226
+ - 300
+ - 301
+ - 302
+ - 303
+ - 304
+ - 305
+ - 306
+ - 307
+ - 308
+ successes: 0
+ unhealthy:
+ http_failures: 0
+ http_statuses:
+ - 429
+ - 500
+ - 503
+ tcp_failures: 0
+ timeouts: 0
+ hash_on: none
+ hash_fallback: none
+ hash_on_cookie_path: /
+ targets:
+ - target: 100.100.77.189:80
+ weight: 100
+plugins:
+- name: prometheus
+ enabled: true
+ protocols:
+ - grpc
+ - grpcs
+ - http
+ - https
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_nginx.template b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_nginx.template
new file mode 100644
index 00000000..41aadf76
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_nginx.template
@@ -0,0 +1,99 @@
+# This is a custom nginx configuration template for Kong specs
+
+pid pids/nginx.pid; # mandatory even for custom config templates
+
+> if wasm and wasm_dynamic_module then
+load_module $(wasm_dynamic_module);
+> end
+
+error_log ${{PROXY_ERROR_LOG}} ${{LOG_LEVEL}};
+
+env KONG_LICENSE_DATA;
+env KONG_LICENSE_PATH;
+
+# injected nginx_main_* directives
+> for _, el in ipairs(nginx_main_directives) do
+$(el.name) $(el.value);
+> end
+
+> if database == "off" then
+lmdb_environment_path ${{LMDB_ENVIRONMENT_PATH}};
+lmdb_map_size ${{LMDB_MAP_SIZE}};
+
+> if lmdb_validation_tag then
+lmdb_validation_tag $(lmdb_validation_tag);
+> end
+
+> end
+
+events {
+ # injected nginx_events_* directives
+> for _, el in ipairs(nginx_events_directives) do
+ $(el.name) $(el.value);
+> end
+}
+
+> if wasm then
+wasm {
+> for _, el in ipairs(nginx_wasm_main_shm_kv_directives) do
+ shm_kv $(el.name) $(el.value);
+> end
+
+> for _, module in ipairs(wasm_modules_parsed) do
+ module $(module.name) $(module.path);
+> end
+
+> for _, el in ipairs(nginx_wasm_main_directives) do
+> if el.name == "shm_kv" then
+ shm_kv * $(el.value);
+> else
+ $(el.name) $(el.value);
+> end
+> end
+
+> if #nginx_wasm_wasmtime_directives > 0 or wasmtime_cache_config_file then
+ wasmtime {
+> if wasmtime_cache_config_file then
+ cache_config $(quote(wasmtime_cache_config_file));
+> end
+
+> for _, el in ipairs(nginx_wasm_wasmtime_directives) do
+ flag $(el.name) $(el.value);
+> end
+ }
+> end -- wasmtime
+
+> if #nginx_wasm_v8_directives > 0 then
+ v8 {
+> for _, el in ipairs(nginx_wasm_v8_directives) do
+ flag $(el.name) $(el.value);
+> end
+ }
+> end -- v8
+
+> if #nginx_wasm_wasmer_directives > 0 then
+ wasmer {
+> for _, el in ipairs(nginx_wasm_wasmer_directives) do
+ flag $(el.name) $(el.value);
+> end
+ }
+> end -- wasmer
+
+}
+> end
+
+
+
+> if role == "control_plane" or #proxy_listeners > 0 or #admin_listeners > 0 or #status_listeners > 0 then
+http {
+ include 'nginx-kong.conf';
+ include '*http.test.conf';
+}
+> end
+
+> if #stream_listeners > 0 or cluster_ssl_tunnel then
+stream {
+ include 'nginx-kong-stream.conf';
+ include '*stream.test.conf';
+}
+> end
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/api.lua
new file mode 100644
index 00000000..c14315d7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/api.lua
@@ -0,0 +1,16 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ ["/method_without_exit"] = {
+ GET = function()
+ kong.response.set_status(201)
+ kong.response.set_header("x-foo", "bar")
+ ngx.print("hello")
+ end,
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/handler.lua
new file mode 100644
index 00000000..b9ea64bb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/handler.lua
@@ -0,0 +1,15 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- a plugin fixture to test a method on the admin api
+
+local AdminApiMethod = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+return AdminApiMethod
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/schema.lua
new file mode 100644
index 00000000..e004e219
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/schema.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "admin-api-method",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { foo = { type = "string" } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/status_api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/status_api.lua
new file mode 100644
index 00000000..6b05e5ff
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/admin-api-method/status_api.lua
@@ -0,0 +1,14 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ ["/hello"] = {
+ GET = function()
+ kong.response.exit(200, { hello = "from status api" })
+ end,
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/api.lua
new file mode 100644
index 00000000..22cb9923
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/api.lua
@@ -0,0 +1,32 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local kong = kong
+
+
+return {
+ ["/routes"] = {
+ schema = kong.db.routes.schema,
+ GET = function(_, _, _, parent)
+ kong.response.set_header("Kong-Api-Override", "ok")
+ return parent()
+ end,
+ POST = function(_, _, _, parent)
+ kong.response.set_header("Kong-Api-Override", "ok")
+ return parent()
+ end,
+ },
+ ["/services"] = {
+ schema = kong.db.services.schema,
+ methods = {
+ GET = function(_, _, _, parent)
+ kong.response.set_header("Kong-Api-Override", "ok")
+ return parent()
+ end
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/handler.lua
new file mode 100644
index 00000000..5e2dd0d6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/handler.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ PRIORITY = 1000,
+ VERSION = "1.0",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/schema.lua
new file mode 100644
index 00000000..77906b0a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/api-override/schema.lua
@@ -0,0 +1,34 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "api-override",
+ fields = {
+ {
+ protocols = typedefs.protocols {
+ default = {
+ "http",
+ "https",
+ "tcp",
+ "tls",
+ "grpc",
+ "grpcs"
+ },
+ },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/daos.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/daos.lua
new file mode 100644
index 00000000..7c270b71
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/daos.lua
@@ -0,0 +1,38 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ {
+ name = "ck_vs_ek_testcase",
+ primary_key = { "id" },
+ endpoint_key = "name",
+ cache_key = { "route", "service" },
+ fields = {
+ { id = typedefs.uuid },
+ { name = typedefs.utf8_name }, -- this typedef declares 'unique = true'
+ { service = { type = "foreign", reference = "services", on_delete = "cascade",
+ default = ngx.null, unique = true }, },
+ { route = { type = "foreign", reference = "routes", on_delete = "cascade",
+ default = ngx.null, unique = true }, },
+ },
+ entity_checks = {
+ { mutually_exclusive = {
+ "service",
+ "route",
+ }
+ },
+ { at_least_one_of = {
+ "service",
+ "route",
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/handler.lua
new file mode 100644
index 00000000..05c25c48
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/handler.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ PRIORITY = 1,
+ VERSION = "1.0",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/migrations/000_base_cache_key_vs_endpoint_key.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/migrations/000_base_cache_key_vs_endpoint_key.lua
new file mode 100644
index 00000000..2dd73af5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/migrations/000_base_cache_key_vs_endpoint_key.lua
@@ -0,0 +1,32 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ CREATE TABLE IF NOT EXISTS "ck_vs_ek_testcase" (
+ "id" UUID PRIMARY KEY,
+ "name" TEXT,
+ "route_id" UUID REFERENCES "routes" ("id") ON DELETE CASCADE,
+ "service_id" UUID REFERENCES "services" ("id") ON DELETE CASCADE,
+ "cache_key" TEXT UNIQUE
+ );
+
+ DO $$
+ BEGIN
+ CREATE UNIQUE INDEX IF NOT EXISTS "ck_vs_ek_testcase_name_idx"
+ ON "ck_vs_ek_testcase" ("name");
+ END$$;
+
+ DO $$
+ BEGIN
+ CREATE UNIQUE INDEX IF NOT EXISTS "ck_vs_ek_testcase_cache_key_idx"
+ ON "ck_vs_ek_testcase" ("cache_key");
+ END$$;
+ ]],
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/migrations/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/migrations/init.lua
new file mode 100644
index 00000000..9dc9bec5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/migrations/init.lua
@@ -0,0 +1,10 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ "000_base_unique_foreign",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/schema.lua
new file mode 100644
index 00000000..2f6c9e0e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache-key-vs-endpoint-key/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "cache-key-vs-endpoint-key",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache/handler.lua
new file mode 100644
index 00000000..cbec08d1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache/handler.lua
@@ -0,0 +1,50 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local type = type
+
+
+local CacheHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+
+function CacheHandler:access(conf)
+ ngx.req.read_body()
+
+ local args, err = ngx.req.get_post_args()
+ if not args then
+ kong.log.err(err)
+ return kong.response.exit(500, { message = "An unexpected error occurred" })
+ end
+
+ local cache_key = args.cache_key
+ if not cache_key then
+ return kong.response.exit(400, { message = "missing cache_key" })
+ end
+
+ local cache_value = args.cache_value
+ if not cache_value then
+ return kong.response.exit(400, { message = "missing cache_value" })
+ end
+
+ local function cb()
+ return cache_value
+ end
+
+ local value, err = kong.cache:get(cache_key, nil, cb)
+ if err then
+ kong.log.err(err)
+ return kong.response.exit(500, { message = "An unexpected error occurred" })
+ end
+
+ return kong.response.exit(200, type(value) == "table" and value or { message = value })
+end
+
+
+return CacheHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache/schema.lua
new file mode 100644
index 00000000..47012869
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cache/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "cache",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cluster-error-reporting/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cluster-error-reporting/handler.lua
new file mode 100644
index 00000000..f193665e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cluster-error-reporting/handler.lua
@@ -0,0 +1,39 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local Plugin = {
+ VERSION = "6.6.6",
+ PRIORITY = 1000,
+}
+
+local clustering = require("kong.clustering")
+local saved_init_dp_worker = clustering.init_dp_worker
+
+-- monkey-patch cluster initializer so that konnect_mode is
+-- always enabled
+clustering.init_dp_worker = function(self, ...)
+ self.conf.konnect_mode = true
+ return saved_init_dp_worker(self, ...)
+end
+
+
+local declarative = require("kong.db.declarative")
+local saved_load_into_cache = declarative.load_into_cache_with_events
+
+-- ...and monkey-patch this to throw an exception on demand
+declarative.load_into_cache_with_events = function(...)
+ local fh = io.open(kong.configuration.prefix .. "/throw-an-exception")
+ if fh then
+ local err = fh:read("*a") or "oh no!"
+ ngx.log(ngx.ERR, "throwing an exception!")
+ error(err)
+ end
+
+ return saved_load_into_cache(...)
+end
+
+return Plugin
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cluster-error-reporting/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cluster-error-reporting/schema.lua
new file mode 100644
index 00000000..99a3a45b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/cluster-error-reporting/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "cluster-error-reporting",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker-last/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker-last/handler.lua
new file mode 100644
index 00000000..47a3b4c5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker-last/handler.lua
@@ -0,0 +1,22 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local CtxCheckerHandler = require "spec.fixtures.custom_plugins.kong.plugins.ctx-checker.handler"
+
+
+local CtxCheckerLastHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 0,
+ _name = "ctx-checker-last",
+}
+
+
+CtxCheckerLastHandler.access = CtxCheckerHandler.access
+CtxCheckerLastHandler.header_filter = CtxCheckerHandler.header_filter
+
+
+return CtxCheckerLastHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker-last/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker-last/schema.lua
new file mode 100644
index 00000000..39014001
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker-last/schema.lua
@@ -0,0 +1,27 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "ctx-checker-last",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { ctx_set_field = { type = "string" } },
+ { ctx_set_value = { type = "string", default = "set_by_ctx_checker" } },
+ { ctx_set_array = { type = "array", elements = { type = "string" } } },
+ { ctx_check_field = { type = "string" } },
+ { ctx_check_value = { type = "string" } },
+ { ctx_check_array = { type = "array", elements = { type = "string" } } },
+ { ctx_kind = { type = "string", default = "ngx.ctx" } },
+ { ctx_throw_error = { type = "boolean", default = false } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker/handler.lua
new file mode 100644
index 00000000..89f55517
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker/handler.lua
@@ -0,0 +1,120 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local tablex = require "pl.tablex"
+local inspect = require "inspect"
+
+
+local ngx = ngx
+local kong = kong
+local type = type
+local error = error
+local tostring = tostring
+
+
+local CtxCheckerHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+ _name = "ctx-checker",
+}
+
+
+local function get_ctx(ctx_kind)
+ if ctx_kind == "kong.ctx.shared" then
+ return kong.ctx.shared
+ end
+
+ if ctx_kind == "kong.ctx.plugin" then
+ return kong.ctx.plugin
+ end
+
+ return ngx.ctx
+end
+
+
+local function set_header(conf, name, value)
+ if conf.ctx_kind == "kong.ctx.shared"
+ or conf.ctx_kind == "kong.ctx.plugin" then
+ return kong.response.set_header(name, value)
+ end
+
+ ngx.header[name] = value
+end
+
+
+function CtxCheckerHandler:access(conf)
+ local set_field = conf.ctx_set_field
+ if not set_field then
+ return
+ end
+
+ local ctx = get_ctx(conf.ctx_kind)
+ local existing = ctx[set_field]
+ if existing ~= nil and conf.throw_error then
+ if type(existing) == "table" then
+ existing = inspect(existing)
+ end
+
+ error("Expected to be able to set" ..
+ conf.ctx_kind ..
+ "['" .. set_field ..
+ "'] but it was already set. Found value: " ..
+ tostring(existing))
+ end
+
+
+ if type(conf.ctx_set_array) == "table" then
+ ctx[set_field] = conf.ctx_set_array
+ elseif type(conf.ctx_set_map) == "table" then
+ ctx[set_field] = conf.ctx_set_map
+ elseif type(conf.ctx_set_array_of_maps) == "table" then
+ ctx[set_field] = conf.ctx_set_array_of_maps
+ elseif type(conf.ctx_set_value) == "string" then
+ ctx[set_field] = conf.ctx_set_value
+ end
+end
+
+
+function CtxCheckerHandler:header_filter(conf)
+ local check_field = conf.ctx_check_field
+ if not check_field then
+ return
+ end
+
+ local ctx = get_ctx(conf.ctx_kind)
+ local val = ctx[check_field]
+
+ local ok
+ if conf.ctx_check_array then
+ if type(val) == "table" then
+ ok = tablex.compare(val, conf.ctx_check_array, "==")
+ else
+ ok = false
+ end
+
+ elseif conf.ctx_check_value then
+ ok = val == conf.ctx_check_value
+ else
+ ok = true
+ end
+
+ if type(val) == "table" then
+ val = inspect(val)
+ end
+
+ if ok then
+ return set_header(conf, self._name .."-" .. check_field, tostring(val))
+ end
+
+ if conf.throw_error then
+ error("Expected " .. conf.ctx_kind .. "['" .. check_field ..
+ "'] to be set, but it was " .. tostring(val))
+ end
+end
+
+
+return CtxCheckerHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker/schema.lua
new file mode 100644
index 00000000..63c9d40c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-checker/schema.lua
@@ -0,0 +1,28 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "ctx-checker",
+ fields = {
+ { config = {
+ type = "record",
+ fields = {
+ { ctx_set_field = { type = "string" }, },
+ { ctx_set_value = { type = "string", default = "set_by_ctx_checker" } },
+ { ctx_set_array = { type = "array", elements = { type = "string" } } },
+ { ctx_set_map = { type = "map", keys = { type = "string" }, values = { type = "string" } } },
+ { ctx_set_array_of_maps = { type = "array", elements =
+ { type = "map", keys = { type = "string" },
+ values = { type = "string"}} }},
+ { ctx_check_field = { type = "string" } },
+ { ctx_check_value = { type = "string" } },
+ { ctx_check_array = { type = "array", elements = { type = "string" } } },
+ { ctx_kind = { type = "string", default = "ngx.ctx" } },
+ { ctx_throw_error = { type = "boolean", default = false } },
+ },
+ }, }, },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests-response/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests-response/handler.lua
new file mode 100644
index 00000000..1d45bc50
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests-response/handler.lua
@@ -0,0 +1,480 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ngx = ngx
+local kong = kong
+local type = type
+local assert = assert
+local subsystem = ngx.config.subsystem
+local math = math
+
+
+local function is_nil(ctx, name)
+ if ctx[name] ~= nil then
+ return false, "[ctx-tests] " .. name .. " is not a nil"
+ end
+
+ return true
+end
+
+
+local function is_true(ctx, name)
+ if ctx[name] ~= true then
+ return false, "[ctx-tests] " .. name .. " is not true"
+ end
+
+ return true
+end
+
+
+local function is_positive_integer(ctx, name)
+ local value = ctx[name]
+ if type(value) ~= "number" then
+ return false, "[ctx-tests] " .. name .. " is not a number"
+ end
+
+ if math.floor(value) ~= value then
+ return false, "[ctx-tests] " .. name .. " is not an integer"
+ end
+
+ if value <= 0 then
+ return false, "[ctx-tests] " .. name .. " is not a positive integer"
+ end
+
+ return true
+end
+
+
+local function is_non_negative_integer(ctx, name)
+ local value = ctx[name]
+ if value == 0 then
+ return true
+ end
+
+ return is_positive_integer(ctx, name)
+end
+
+
+local function is_equal_to_start_time(ctx, name)
+ local ok, err = is_positive_integer(ctx, name)
+ if not ok then
+ return ok, err
+ end
+
+ if ctx[name] < ctx.KONG_PROCESSING_START then
+ return false, "[ctx-tests] " .. name .. " is less than the processing start"
+ end
+
+ if subsystem ~= "stream" then
+ if ctx[name] ~= (ngx.req.start_time() * 1000) then
+ return false, "[ctx-tests] " .. name .. " is less than the request start time"
+ end
+ end
+
+ return true
+end
+
+
+local function is_greater_or_equal_to_start_time(ctx, name)
+ local ok, err = is_positive_integer(ctx, name)
+ if not ok then
+ return ok, err
+ end
+
+ if ctx[name] < ctx.KONG_PROCESSING_START then
+ return false, "[ctx-tests] " .. name .. " is less than the processing start"
+ end
+
+ if subsystem ~= "stream" then
+ if ctx[name] < (ngx.req.start_time() * 1000) then
+ return false, "[ctx-tests] " .. name .. " is less than the request start time"
+ end
+ end
+
+ return true
+end
+
+
+local function is_greater_or_equal_to_ctx_value(ctx, name, greater_name)
+ local ok, err = is_positive_integer(ctx, name)
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_positive_integer(ctx, greater_name)
+ if not ok then
+ return ok, err
+ end
+
+ if ctx[greater_name] < ctx[name] then
+ return false, "[ctx-tests] " .. name .. " is greater than " .. greater_name
+ end
+
+ return true
+end
+
+
+local function has_correct_proxy_latency(ctx)
+ local ok, err = is_positive_integer(ctx, "KONG_BALANCER_ENDED_AT")
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_non_negative_integer(ctx, "KONG_PROXY_LATENCY")
+ if not ok then
+ return ok, err
+ end
+
+ if ctx.KONG_BALANCER_ENDED_AT < ctx.KONG_PROCESSING_START then
+ return false, "[ctx-tests] KONG_BALANCER_ENDED_AT is less than the processing start"
+ end
+
+ local latency = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_PROCESSING_START
+ if ctx.KONG_PROXY_LATENCY ~= latency then
+ return false, "[ctx-tests] KONG_PROXY_LATENCY is not calculated correctly"
+ end
+
+ if subsystem ~= "stream" then
+ latency = ctx.KONG_BALANCER_ENDED_AT - ngx.req.start_time() * 1000
+ if ctx.KONG_PROXY_LATENCY ~= latency then
+ return false, "[ctx-tests] KONG_PROXY_LATENCY is not calculated correctly (request start time)"
+ end
+ end
+
+ return true
+end
+
+
+local function has_correct_waiting_time(ctx)
+ local err
+ local ok = is_positive_integer(ctx, "KONG_RESPONSE_START")
+ if not ok then
+ ok, err = is_positive_integer(ctx, "KONG_HEADER_FILTER_START")
+ if not ok then
+ return ok, err
+ end
+ end
+
+ ok, err = is_positive_integer(ctx, "KONG_BALANCER_ENDED_AT")
+ if not ok then
+ return ok, err
+ end
+
+ local waiting_time = (ctx.KONG_RESPONSE_START or ctx.KONG_HEADER_FILTER_START) -
+ ctx.KONG_BALANCER_ENDED_AT
+
+ if ctx.KONG_WAITING_TIME ~= waiting_time then
+ return false, "[ctx-tests] KONG_WAITING_TIME is not calculated correctly"
+ end
+
+ return true
+end
+
+
+local function has_correct_receive_time(ctx)
+ local ok, err = is_positive_integer(ctx, "KONG_BODY_FILTER_ENDED_AT")
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_positive_integer(ctx, "KONG_HEADER_FILTER_START")
+ if not ok then
+ return ok, err
+ end
+
+ local receive_time = ctx.KONG_BODY_FILTER_ENDED_AT -
+ (ctx.KONG_RESPONSE_START or ctx.KONG_HEADER_FILTER_START)
+
+ if ctx.KONG_RECEIVE_TIME ~= receive_time then
+ return false, "[ctx-tests] KONG_RECEIVE_TIME is not calculated correctly"
+ end
+
+ return true
+end
+
+
+local function has_correct_upstream_dns_time(ctx)
+ local ok, err = is_positive_integer(ctx, "KONG_UPSTREAM_DNS_END_AT")
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_positive_integer(ctx, "KONG_UPSTREAM_DNS_START")
+ if not ok then
+ return ok, err
+ end
+
+ local upstream_dns_time = ctx.KONG_UPSTREAM_DNS_END_AT - ctx.KONG_UPSTREAM_DNS_START
+
+ if ctx.KONG_UPSTREAM_DNS_TIME ~= upstream_dns_time then
+ return false, "[ctx-tests] KONG_UPSTREAM_DNS_TIME is not calculated correctly"
+ end
+
+ return true
+end
+
+local CtxTests = {
+ PRIORITY = -1000000,
+ VERSION = "1.0",
+}
+
+
+function CtxTests:preread()
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_REWRITE_START"))
+ assert(is_nil(ctx, "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_REWRITE_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_BALANCER_START"))
+ assert(is_nil(ctx, "KONG_BALANCER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BALANCER_TIME"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_START"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_PROXIED"))
+ assert(is_nil(ctx, "KONG_PROXY_LATENCY"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:rewrite()
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_REWRITE_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_BALANCER_START"))
+ assert(is_nil(ctx, "KONG_BALANCER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BALANCER_TIME"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_START"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_PROXIED"))
+ assert(is_nil(ctx, "KONG_PROXY_LATENCY"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:access(config)
+ if config.buffered then
+ kong.service.request.enable_buffering()
+ end
+
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_REWRITE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_ENDED_AT", "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_BALANCER_START"))
+ assert(is_nil(ctx, "KONG_BALANCER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BALANCER_TIME"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_START"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_PROXIED"))
+ assert(is_nil(ctx, "KONG_PROXY_LATENCY"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:response(config)
+ -- assert(config.buffered == true, "response should only be executed when buffering the response was requested")
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_REWRITE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_ENDED_AT", "KONG_ACCESS_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_START", "KONG_ACCESS_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_ACCESS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_ENDED_AT", "KONG_BALANCER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_START", "KONG_BALANCER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BALANCER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_true(ctx, "KONG_PROXIED"))
+ assert(has_correct_proxy_latency(ctx))
+ assert(has_correct_waiting_time(ctx))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:log(config)
+ local ctx = ngx.ctx
+ if subsystem == "stream" then
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_PREREAD_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PREREAD_START", "KONG_PREREAD_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_PREREAD_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PREREAD_ENDED_AT", "KONG_BALANCER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_START", "KONG_BALANCER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BALANCER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_LOG_START"))
+ if (not is_nil(ctx, "KONG_UPSTREAM_DNS_START") and not is_nil(ctx, "KONG_BALANCER_ENDED_AT")) then
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_START", "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_END_AT", "KONG_LOG_START"))
+ assert(has_correct_upstream_dns_time(ctx))
+ end
+ assert(is_true(ctx, "KONG_PROXIED"))
+ assert(has_correct_proxy_latency(ctx))
+ assert(is_nil(ctx, "KONG_REWRITE_START"))
+ assert(is_nil(ctx, "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_REWRITE_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+
+ -- TODO: ngx.var.upstream_first_byte_time?
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+
+
+ -- TODO: ngx.ctx.KONG_LOG_START - (ngx.ctx.BALANCER_ENDED_AT + ngx.var.upstream_first_byte_time)?
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+
+ else
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_REWRITE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_ENDED_AT", "KONG_ACCESS_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_START", "KONG_ACCESS_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_ACCESS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_ENDED_AT", "KONG_BALANCER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_START", "KONG_BALANCER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BALANCER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_HEADER_FILTER_START"))
+
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_RESPONSE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_START", "KONG_RESPONSE_ENDED_AT"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ assert(is_non_negative_integer(ctx, "KONG_RESPONSE_TIME"))
+
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_HEADER_FILTER_START", "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_HEADER_FILTER_ENDED_AT", "KONG_BODY_FILTER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BODY_FILTER_START", "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BODY_FILTER_ENDED_AT", "KONG_LOG_START"))
+ if (not is_nil(ctx, "KONG_UPSTREAM_DNS_START") and not is_nil(ctx, "KONG_BALANCER_ENDED_AT")) then
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_START", "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_END_AT", "KONG_LOG_START"))
+ assert(has_correct_upstream_dns_time(ctx))
+ end
+ assert(is_true(ctx, "KONG_PROXIED"))
+ assert(has_correct_proxy_latency(ctx))
+ assert(has_correct_waiting_time(ctx))
+ assert(has_correct_receive_time(ctx))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ end
+
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+return CtxTests
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests-response/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests-response/schema.lua
new file mode 100644
index 00000000..9fb4932a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests-response/schema.lua
@@ -0,0 +1,34 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+-- TODO: At the moment this tests the happy case. Perhaps it could be extended to work
+-- even with unhappy cases, e.g. together with error-generator plugin. Or the plugin
+-- could be made to error by itself.
+return {
+ name = "ctx-tests-response",
+ fields = {
+ {
+ protocols = typedefs.protocols { default = { "http", "https", "tcp", "tls", "grpc", "grpcs" } },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ {
+ buffered = {
+ type = "boolean",
+ default = false,
+ },
+ },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests/handler.lua
new file mode 100644
index 00000000..89de69f9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests/handler.lua
@@ -0,0 +1,547 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ngx = ngx
+local kong = kong
+local type = type
+local assert = assert
+local subsystem = ngx.config.subsystem
+local math = math
+local get_phase = ngx.get_phase
+
+
+local function is_nil(ctx, name)
+ if ctx[name] ~= nil then
+ return false, "[ctx-tests] " .. name .. " is not a nil"
+ end
+
+ return true
+end
+
+
+local function is_true(ctx, name)
+ if ctx[name] ~= true then
+ return false, "[ctx-tests] " .. name .. " is not true"
+ end
+
+ return true
+end
+
+
+local function is_positive_integer(ctx, name)
+ local value = ctx[name]
+ if type(value) ~= "number" then
+ return false, "[ctx-tests] " .. name .. " is not a number"
+ end
+
+ if math.floor(value) ~= value then
+ return false, "[ctx-tests] " .. name .. " is not an integer"
+ end
+
+ if value <= 0 then
+ return false, "[ctx-tests] " .. name .. " is not a positive integer"
+ end
+
+ return true
+end
+
+
+local function is_non_negative_integer(ctx, name)
+ local value = ctx[name]
+ if value == 0 then
+ return true
+ end
+
+ return is_positive_integer(ctx, name)
+end
+
+
+local function is_equal_to_start_time(ctx, name)
+ local ok, err = is_positive_integer(ctx, name)
+ if not ok then
+ return ok, err
+ end
+
+ if ctx[name] < ctx.KONG_PROCESSING_START then
+ return false, "[ctx-tests] " .. name .. " is less than the processing start"
+ end
+
+ if subsystem ~= "stream" then
+ if ctx[name] ~= (ngx.req.start_time() * 1000) then
+ return false, "[ctx-tests] " .. name .. " is less than the request start time"
+ end
+ end
+
+ return true
+end
+
+
+local function is_greater_or_equal_to_start_time(ctx, name)
+ local ok, err = is_positive_integer(ctx, name)
+ if not ok then
+ return ok, err
+ end
+
+ if ctx[name] < ctx.KONG_PROCESSING_START then
+ return false, "[ctx-tests] " .. name .. " is less than the processing start"
+ end
+
+ if subsystem ~= "stream" then
+ if ctx[name] < (ngx.req.start_time() * 1000) then
+ return false, "[ctx-tests] " .. name .. " is less than the request start time"
+ end
+ end
+
+ return true
+end
+
+
+local function is_greater_or_equal_to_ctx_value(ctx, name, greater_name)
+ local ok, err = is_positive_integer(ctx, name)
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_positive_integer(ctx, greater_name)
+ if not ok then
+ return ok, err
+ end
+
+ if ctx[greater_name] < ctx[name] then
+ return false, "[ctx-tests] " .. name .. " is greater than " .. greater_name
+ end
+
+ return true
+end
+
+
+local function has_correct_proxy_latency(ctx)
+ local ok, err = is_positive_integer(ctx, "KONG_BALANCER_ENDED_AT")
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_non_negative_integer(ctx, "KONG_PROXY_LATENCY")
+ if not ok then
+ return ok, err
+ end
+
+ if ctx.KONG_BALANCER_ENDED_AT < ctx.KONG_PROCESSING_START then
+ return false, "[ctx-tests] KONG_BALANCER_ENDED_AT is less than the processing start"
+ end
+
+ local latency = ctx.KONG_BALANCER_ENDED_AT - ctx.KONG_PROCESSING_START
+ if ctx.KONG_PROXY_LATENCY ~= latency then
+ return false, "[ctx-tests] KONG_PROXY_LATENCY is not calculated correctly"
+ end
+
+ if subsystem ~= "stream" then
+ latency = ctx.KONG_BALANCER_ENDED_AT - ngx.req.start_time() * 1000
+ if ctx.KONG_PROXY_LATENCY ~= latency then
+ return false, "[ctx-tests] KONG_PROXY_LATENCY is not calculated correctly (request start time)"
+ end
+ end
+
+ if get_phase() == "log" then
+ local log = kong.log.serialize()
+ if ctx.KONG_PROXY_LATENCY > log.latencies.kong then
+ return false, "[ctx-tests] kong.log.serialize() latency is less than KONG_PROXY_LATENCY"
+ end
+ end
+
+ return true
+end
+
+
+local function has_correct_waiting_time(ctx)
+ local err
+ local ok = is_positive_integer(ctx, "KONG_RESPONSE_START")
+ if not ok then
+ ok, err = is_positive_integer(ctx, "KONG_HEADER_FILTER_START")
+ if not ok then
+ return ok, err
+ end
+ end
+
+ ok, err = is_positive_integer(ctx, "KONG_BALANCER_ENDED_AT")
+ if not ok then
+ return ok, err
+ end
+
+ local waiting_time = (ctx.KONG_RESPONSE_START or ctx.KONG_HEADER_FILTER_START) -
+ ctx.KONG_BALANCER_ENDED_AT
+
+ if ctx.KONG_WAITING_TIME ~= waiting_time then
+ return false, "[ctx-tests] KONG_WAITING_TIME is not calculated correctly"
+ end
+
+ return true
+end
+
+
+local function has_correct_receive_time(ctx)
+ local ok, err = is_positive_integer(ctx, "KONG_BODY_FILTER_ENDED_AT")
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_positive_integer(ctx, "KONG_HEADER_FILTER_START")
+ if not ok then
+ return ok, err
+ end
+
+ local receive_time = ctx.KONG_BODY_FILTER_ENDED_AT -
+ (ctx.KONG_RESPONSE_START or ctx.KONG_HEADER_FILTER_START)
+
+ if ctx.KONG_RECEIVE_TIME ~= receive_time then
+ return false, "[ctx-tests] KONG_RECEIVE_TIME is not calculated correctly"
+ end
+
+ return true
+end
+
+
+local CtxTests = {
+ PRIORITY = -1000000,
+ VERSION = "1.0",
+}
+
+
+local function has_correct_upstream_dns_time(ctx)
+ local ok, err = is_positive_integer(ctx, "KONG_UPSTREAM_DNS_END_AT")
+ if not ok then
+ return ok, err
+ end
+
+ ok, err = is_positive_integer(ctx, "KONG_UPSTREAM_DNS_START")
+ if not ok then
+ return ok, err
+ end
+
+ local upstream_dns_time = ctx.KONG_UPSTREAM_DNS_END_AT - ctx.KONG_UPSTREAM_DNS_START
+
+ if ctx.KONG_UPSTREAM_DNS_TIME ~= upstream_dns_time then
+ return false, "[ctx-tests] KONG_UPSTREAM_DNS_TIME is not calculated correctly"
+ end
+
+ return true
+end
+
+
+function CtxTests:preread()
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_REWRITE_START"))
+ assert(is_nil(ctx, "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_REWRITE_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_BALANCER_START"))
+ assert(is_nil(ctx, "KONG_BALANCER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BALANCER_TIME"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_START"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_PROXIED"))
+ assert(is_nil(ctx, "KONG_PROXY_LATENCY"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:rewrite()
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_REWRITE_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_BALANCER_START"))
+ assert(is_nil(ctx, "KONG_BALANCER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BALANCER_TIME"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_START"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_PROXIED"))
+ assert(is_nil(ctx, "KONG_PROXY_LATENCY"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:access(config)
+ if config.buffered then
+ kong.service.request.enable_buffering()
+ end
+
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_REWRITE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_ENDED_AT", "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_BALANCER_START"))
+ assert(is_nil(ctx, "KONG_BALANCER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BALANCER_TIME"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_START"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_nil(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_PROXIED"))
+ assert(is_nil(ctx, "KONG_PROXY_LATENCY"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:header_filter(config)
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_REWRITE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_ENDED_AT", "KONG_ACCESS_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_START", "KONG_ACCESS_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_ACCESS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_ENDED_AT", "KONG_BALANCER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_START", "KONG_BALANCER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BALANCER_TIME"))
+ if config.buffered then
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_RESPONSE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_START", "KONG_RESPONSE_ENDED_AT"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ assert(is_non_negative_integer(ctx, "KONG_RESPONSE_TIME"))
+ else
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ end
+ assert(is_true(ctx, "KONG_PROXIED"))
+ assert(has_correct_proxy_latency(ctx))
+ assert(has_correct_waiting_time(ctx))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:body_filter(config)
+ if not ngx.arg[2] then
+ return
+ end
+
+ local ctx = ngx.ctx
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_REWRITE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_ENDED_AT", "KONG_ACCESS_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_START", "KONG_ACCESS_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_ACCESS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_ENDED_AT", "KONG_BALANCER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_START", "KONG_BALANCER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BALANCER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ if config.buffered then
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_RESPONSE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_START", "KONG_RESPONSE_ENDED_AT"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ assert(is_non_negative_integer(ctx, "KONG_RESPONSE_TIME"))
+ else
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ end
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_HEADER_FILTER_START", "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_HEADER_FILTER_ENDED_AT", "KONG_BODY_FILTER_START"))
+ assert(is_true(ctx, "KONG_PROXIED"))
+ assert(has_correct_proxy_latency(ctx))
+ assert(has_correct_waiting_time(ctx))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_START"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+function CtxTests:log(config)
+ local ctx = ngx.ctx
+ if subsystem == "stream" then
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_PREREAD_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PREREAD_START", "KONG_PREREAD_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_PREREAD_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PREREAD_ENDED_AT", "KONG_BALANCER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_START", "KONG_BALANCER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BALANCER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_LOG_START"))
+ if (not is_nil(ctx, "KONG_UPSTREAM_DNS_START") and not is_nil(ctx, "KONG_BALANCER_ENDED_AT")) then
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_START", "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_END_AT", "KONG_LOG_START"))
+ assert(has_correct_upstream_dns_time(ctx))
+ end
+ assert(is_true(ctx, "KONG_PROXIED"))
+ assert(has_correct_proxy_latency(ctx))
+ assert(is_nil(ctx, "KONG_REWRITE_START"))
+ assert(is_nil(ctx, "KONG_REWRITE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_REWRITE_TIME"))
+ assert(is_nil(ctx, "KONG_ACCESS_START"))
+ assert(is_nil(ctx, "KONG_ACCESS_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_ACCESS_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_START"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_START"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ assert(is_nil(ctx, "KONG_RESPONSE_LATENCY"))
+
+ -- TODO: ngx.var.upstream_first_byte_time?
+ assert(is_nil(ctx, "KONG_WAITING_TIME"))
+
+
+ -- TODO: ngx.ctx.KONG_LOG_START - (ngx.ctx.BALANCER_ENDED_AT + ngx.var.upstream_first_byte_time)?
+ assert(is_nil(ctx, "KONG_RECEIVE_TIME"))
+
+ else
+ assert(is_equal_to_start_time(ctx, "KONG_PROCESSING_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_PROCESSING_START", "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_start_time(ctx, "KONG_REWRITE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_START", "KONG_REWRITE_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_REWRITE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_REWRITE_ENDED_AT", "KONG_ACCESS_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_START", "KONG_ACCESS_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_ACCESS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_ACCESS_ENDED_AT", "KONG_BALANCER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_START", "KONG_BALANCER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BALANCER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ if config.buffered then
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_RESPONSE_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_START", "KONG_RESPONSE_ENDED_AT"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_RESPONSE_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ assert(is_non_negative_integer(ctx, "KONG_RESPONSE_TIME"))
+ else
+ assert(is_nil(ctx, "KONG_RESPONSE_START"))
+ assert(is_nil(ctx, "KONG_RESPONSE_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_RESPONSE_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BALANCER_ENDED_AT", "KONG_HEADER_FILTER_START"))
+ end
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_HEADER_FILTER_START", "KONG_HEADER_FILTER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_HEADER_FILTER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_HEADER_FILTER_ENDED_AT", "KONG_BODY_FILTER_START"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BODY_FILTER_START", "KONG_BODY_FILTER_ENDED_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_BODY_FILTER_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_BODY_FILTER_ENDED_AT", "KONG_LOG_START"))
+ if (not is_nil(ctx, "KONG_UPSTREAM_DNS_START") and not is_nil(ctx, "KONG_BALANCER_ENDED_AT")) then
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_START", "KONG_UPSTREAM_DNS_END_AT"))
+ assert(is_non_negative_integer(ctx, "KONG_UPSTREAM_DNS_TIME"))
+ assert(is_greater_or_equal_to_ctx_value(ctx, "KONG_UPSTREAM_DNS_END_AT", "KONG_LOG_START"))
+ assert(has_correct_upstream_dns_time(ctx))
+ end
+ assert(is_true(ctx, "KONG_PROXIED"))
+ assert(has_correct_proxy_latency(ctx))
+ assert(has_correct_waiting_time(ctx))
+ assert(has_correct_receive_time(ctx))
+ assert(is_nil(ctx, "KONG_PREREAD_START"))
+ assert(is_nil(ctx, "KONG_PREREAD_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_PREREAD_TIME"))
+ assert(is_nil(ctx, "KONG_LOG_ENDED_AT"))
+ assert(is_nil(ctx, "KONG_LOG_TIME"))
+ end
+
+ assert(is_positive_integer(ctx, "host_port"))
+end
+
+
+return CtxTests
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests/schema.lua
new file mode 100644
index 00000000..2da7724f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/ctx-tests/schema.lua
@@ -0,0 +1,34 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+-- TODO: At the moment this tests the happy case. Perhaps it could be extended to work
+-- even with unhappy cases, e.g. together with error-generator plugin. Or the plugin
+-- could be made to error by itself.
+return {
+ name = "ctx-tests",
+ fields = {
+ {
+ protocols = typedefs.protocols { default = { "http", "https", "tcp", "tls", "grpc", "grpcs" } },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ {
+ buffered = {
+ type = "boolean",
+ default = false,
+ },
+ },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua
new file mode 100644
index 00000000..05bf7adc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/dummy/handler.lua
@@ -0,0 +1,57 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local DummyHandler = {
+ VERSION = "9.9.9",
+ PRIORITY = 1000,
+}
+
+
+function DummyHandler:access(conf)
+ if ngx.req.get_uri_args()["send_error"] then
+ return kong.response.exit(404, { message = "Not found" })
+ end
+
+ if conf.test_try then
+ kong.vault.try(function ()
+ if conf.resp_header_value == "open_sesame" then
+ ngx.header["X-Try-Works"] = "true"
+ end
+ end, conf)
+ end
+
+ ngx.header["Dummy-Plugin-Access-Header"] = "dummy"
+end
+
+
+function DummyHandler:header_filter(conf)
+ ngx.header["Dummy-Plugin"] = conf.resp_header_value
+
+ if conf.resp_headers then
+ for header, value in pairs(conf.resp_headers) do
+ ngx.header[header] = value
+ end
+ end
+
+ if conf.resp_code then
+ ngx.status = conf.resp_code
+ end
+
+ if conf.append_body then
+ ngx.header["Content-Length"] = nil
+ end
+end
+
+
+function DummyHandler:body_filter(conf)
+ if conf.append_body and not ngx.arg[2] then
+ ngx.arg[1] = string.sub(ngx.arg[1], 1, -2) .. conf.append_body
+ end
+end
+
+
+return DummyHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua
new file mode 100644
index 00000000..ca12aa81
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/dummy/schema.lua
@@ -0,0 +1,39 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+return {
+ name = "dummy",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { resp_header_value = { type = "string", default = "1", referenceable = true } },
+ { resp_headers = {
+ type = "map",
+ keys = typedefs.header_name,
+ values = {
+ type = "string",
+ referenceable = true,
+ }
+ }},
+ { append_body = { type = "string" } },
+ { resp_code = { type = "number" } },
+ { test_try = { type = "boolean", default = false}},
+ { old_field = {
+ type = "number",
+ deprecation = {
+ message = "dummy: old_field is deprecated",
+ removal_in_version = "x.y.z",
+ old_default = 42 }, }, }
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering-response/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering-response/handler.lua
new file mode 100644
index 00000000..a3b792e3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering-response/handler.lua
@@ -0,0 +1,41 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ngx = ngx
+local kong = kong
+
+
+local EnableBuffering = {
+ PRIORITY = 1000000,
+ VERSION = "1.0",
+}
+
+
+function EnableBuffering:access()
+ kong.service.request.enable_buffering()
+end
+
+
+function EnableBuffering:response(conf)
+ if conf.phase == "response" then
+ if conf.mode == "modify-json" then
+ local body = assert(kong.service.response.get_body())
+ body.modified = true
+ return kong.response.exit(kong.service.response.get_status(), body, {
+ Modified = "yes",
+ })
+ end
+
+ if conf.mode == "md5-header" then
+ local body = kong.service.response.get_raw_body()
+ kong.response.set_header("MD5", ngx.md5(body))
+ end
+ end
+end
+
+
+return EnableBuffering
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering-response/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering-response/schema.lua
new file mode 100644
index 00000000..1358e1d6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering-response/schema.lua
@@ -0,0 +1,30 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "enable-buffering-response",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ {
+ phase = {
+ type = "string",
+ default = "header_filter",
+ },
+ },
+ {
+ mode = {
+ type = "string",
+ },
+ },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering/handler.lua
new file mode 100644
index 00000000..110c770d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering/handler.lua
@@ -0,0 +1,41 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ngx = ngx
+local kong = kong
+
+
+local EnableBuffering = {
+ PRIORITY = 1000000,
+ VERSION = "1.0",
+}
+
+
+function EnableBuffering:access()
+ kong.service.request.enable_buffering()
+end
+
+
+function EnableBuffering:header_filter(conf)
+ if conf.phase == "header_filter" then
+ if conf.mode == "modify-json" then
+ local body = assert(kong.service.response.get_body())
+ body.modified = true
+ return kong.response.exit(kong.response.get_status(), body, {
+ Modified = "yes",
+ })
+ end
+
+ if conf.mode == "md5-header" then
+ local body = kong.service.response.get_raw_body()
+ kong.response.set_header("MD5", ngx.md5(body))
+ end
+ end
+end
+
+
+return EnableBuffering
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering/schema.lua
new file mode 100644
index 00000000..b59b4a76
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/enable-buffering/schema.lua
@@ -0,0 +1,30 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "enable-buffering",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ {
+ phase = {
+ type = "string",
+ default = "header_filter",
+ },
+ },
+ {
+ mode = {
+ type = "string",
+ },
+ },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/encrypted-field/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/encrypted-field/handler.lua
new file mode 100644
index 00000000..ff137ce8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/encrypted-field/handler.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local kong = kong
+
+
+local EncryptedHandler = {
+ PRIORITY = 0,
+ VERSION = "1.0.0",
+}
+
+function EncryptedHandler:access(conf)
+ kong.response.exit(200, { message = conf.message })
+end
+
+
+return EncryptedHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/encrypted-field/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/encrypted-field/schema.lua
new file mode 100644
index 00000000..1d6e5d86
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/encrypted-field/schema.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+return {
+ name = "encrypted-field",
+ fields = {
+ { protocols = typedefs.protocols },
+ { config = {
+ type = "record",
+ fields = {
+ { message = { type = "string", required = true, encrypted = true} }
+ }
+ } }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator-last/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator-last/handler.lua
new file mode 100644
index 00000000..13950acf
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator-last/handler.lua
@@ -0,0 +1,72 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local error = error
+
+
+local ErrorGeneratorLastHandler = {}
+
+
+ErrorGeneratorLastHandler.PRIORITY = -1000000
+ErrorGeneratorLastHandler.VERSION = "1.0"
+
+function ErrorGeneratorLastHandler:init_worker()
+end
+
+
+function ErrorGeneratorLastHandler:certificate(conf)
+ if conf.certificate then
+ error("[error-generator-last] certificate")
+ end
+end
+
+
+function ErrorGeneratorLastHandler:rewrite(conf)
+ if conf.rewrite then
+ error("[error-generator-last] rewrite")
+ end
+end
+
+
+function ErrorGeneratorLastHandler:preread(conf)
+ if conf.preread then
+ error("[error-generator-last] preread")
+ end
+end
+
+
+
+function ErrorGeneratorLastHandler:access(conf)
+ if conf.access then
+ error("[error-generator-last] access")
+ end
+end
+
+
+function ErrorGeneratorLastHandler:header_filter(conf)
+ if conf.header_filter then
+ error("[error-generator-last] header_filter")
+ end
+end
+
+
+function ErrorGeneratorLastHandler:body_filter(conf)
+ if conf.header_filter then
+ error("[error-generator-last] body_filter")
+ end
+end
+
+
+function ErrorGeneratorLastHandler:log(conf)
+ if conf.log then
+ error("[error-generator] body_filter")
+ end
+end
+
+
+return ErrorGeneratorLastHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator-last/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator-last/schema.lua
new file mode 100644
index 00000000..bc5f76d1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator-last/schema.lua
@@ -0,0 +1,25 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "error-generator-last",
+ fields = {
+ { config = {
+ type = "record",
+ fields = {
+ { certificate = { type = "boolean", required = false, default = false } },
+ { rewrite = { type = "boolean", required = false, default = false } },
+ { preread = { type = "boolean", required = false, default = false } },
+ { access = { type = "boolean", required = false, default = false } },
+ { header_filter = { type = "boolean", required = false, default = false } },
+ { body_filter = { type = "boolean", required = false, default = false } },
+ { log = { type = "boolean", required = false, default = false } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator/handler.lua
new file mode 100644
index 00000000..24b478a3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator/handler.lua
@@ -0,0 +1,71 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local error = error
+
+
+local ErrorGeneratorHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000000,
+}
+
+
+function ErrorGeneratorHandler:init_worker()
+end
+
+
+function ErrorGeneratorHandler:certificate(conf)
+ if conf.certificate then
+ error("[error-generator] certificate")
+ end
+end
+
+
+function ErrorGeneratorHandler:rewrite(conf)
+ if conf.rewrite then
+ error("[error-generator] rewrite")
+ end
+end
+
+
+function ErrorGeneratorHandler:preread(conf)
+ if conf.preread then
+ error("[error-generator] preread")
+ end
+end
+
+
+function ErrorGeneratorHandler:access(conf)
+ if conf.access then
+ error("[error-generator] access")
+ end
+end
+
+
+function ErrorGeneratorHandler:header_filter(conf)
+ if conf.header_filter then
+ error("[error-generator] header_filter")
+ end
+end
+
+
+function ErrorGeneratorHandler:body_filter(conf)
+ if conf.header_filter then
+ error("[error-generator] body_filter")
+ end
+end
+
+
+function ErrorGeneratorHandler:log(conf)
+ if conf.log then
+ error("[error-generator] body_filter")
+ end
+end
+
+
+
+return ErrorGeneratorHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator/schema.lua
new file mode 100644
index 00000000..866d3725
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-generator/schema.lua
@@ -0,0 +1,30 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "error-generator",
+ fields = {
+ { consumer = typedefs.no_consumer },
+ { protocols = typedefs.protocols_http },
+ { config = {
+ type = "record",
+ fields = {
+ { certificate = { type = "boolean", required = false, default = false } },
+ { rewrite = { type = "boolean", required = false, default = false } },
+ { preread = { type = "boolean", required = false, default = false } },
+ { access = { type = "boolean", required = false, default = false } },
+ { header_filter = { type = "boolean", required = false, default = false } },
+ { body_filter = { type = "boolean", required = false, default = false } },
+ { log = { type = "boolean", required = false, default = false } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-handler-log/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-handler-log/handler.lua
new file mode 100644
index 00000000..c8f5bff4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-handler-log/handler.lua
@@ -0,0 +1,59 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cjson = require("cjson")
+local ngx = ngx
+
+
+local ErrorHandlerLog = {}
+
+
+ErrorHandlerLog.PRIORITY = 1000
+ErrorHandlerLog.VERSION = "1.0"
+
+local function register(phase)
+ local ws_id = ngx.ctx.workspace or kong.default_workspace
+ local phases = ngx.ctx.err_handler_log_phases or {}
+ local in_ws = phases[ws_id] or {}
+ phases[ws_id] = in_ws
+ table.insert(in_ws, phase)
+ ngx.ctx.err_handler_log_phases = phases
+end
+
+
+function ErrorHandlerLog:rewrite(conf)
+ register("rewrite")
+end
+
+
+function ErrorHandlerLog:access(conf)
+ register("access")
+end
+
+
+function ErrorHandlerLog:header_filter(conf)
+ register("header_filter")
+
+ local phases = ngx.ctx.err_handler_log_phases or {}
+
+
+ ngx.header["Content-Length"] = nil
+ ngx.header["Log-Plugin-Phases"] = table.concat(phases[ngx.ctx.workspace] or {}, ",")
+ ngx.header["Log-Plugin-Workspaces"] = cjson.encode(phases)
+
+ ngx.header["Log-Plugin-Service-Matched"] = ngx.ctx.service and ngx.ctx.service.name
+end
+
+
+function ErrorHandlerLog:body_filter(conf)
+ if not ngx.arg[2] then
+ ngx.arg[1] = "body_filter"
+ end
+end
+
+
+return ErrorHandlerLog
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-handler-log/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-handler-log/schema.lua
new file mode 100644
index 00000000..b1c91f5f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/error-handler-log/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "error-handler-log",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/fail-once-auth/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/fail-once-auth/handler.lua
new file mode 100644
index 00000000..3bb887d8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/fail-once-auth/handler.lua
@@ -0,0 +1,24 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- a plugin fixture to force one authentication failure
+
+local FailOnceAuth = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+local failed = {}
+
+function FailOnceAuth:access(conf)
+ if not failed[conf.service_id] then
+ failed[conf.service_id] = true
+ return kong.response.exit(401, { message = conf.message })
+ end
+end
+
+return FailOnceAuth
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/fail-once-auth/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/fail-once-auth/schema.lua
new file mode 100644
index 00000000..5ea0aada
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/fail-once-auth/schema.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "fail-once-auth",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { message = { type = "string", default = "try again!" } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/api.lua
new file mode 100644
index 00000000..7d1171dd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/api.lua
@@ -0,0 +1,42 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local kong = kong
+
+local foreign_entities = kong.db.foreign_entities
+
+local function select_by_name(params)
+ return params.dao:select_by_name(params.name)
+end
+
+local function get_cached(self, dao, cb)
+ local name = self.params[dao.schema.name]
+
+ local cache_key = dao:cache_key(name)
+ local entity, err = kong.cache:get(cache_key, nil, cb, { dao = dao, name = name })
+
+ if err then
+ kong.log.debug(err)
+ end
+
+ if not entity then
+ return kong.response.exit(404)
+ end
+
+ return kong.response.exit(200, entity)
+end
+
+return {
+ ["/foreign_entities_cache_warmup/:foreign_entities"] = {
+ schema = foreign_entities.schema,
+ methods = {
+ GET = function(self, db)
+ return get_cached(self, foreign_entities, select_by_name)
+ end,
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/daos.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/daos.lua
new file mode 100644
index 00000000..49996e50
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/daos.lua
@@ -0,0 +1,35 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ {
+ name = "foreign_entities",
+ primary_key = { "id" },
+ endpoint_key = "name",
+ cache_key = { "name" },
+ admin_api_name = "foreign-entities",
+ fields = {
+ { id = typedefs.uuid },
+ { name = { type = "string", unique = true } },
+ { same = typedefs.uuid },
+ },
+ },
+ {
+ name = "foreign_references",
+ primary_key = { "id" },
+ endpoint_key = "name",
+ admin_api_name = "foreign-references",
+ fields = {
+ { id = typedefs.uuid },
+ { name = { type = "string", unique = true } },
+ { same = { type = "foreign", reference = "foreign_entities", on_delete = "cascade" } },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/handler.lua
new file mode 100644
index 00000000..05c25c48
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/handler.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ PRIORITY = 1,
+ VERSION = "1.0",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/migrations/000_base_foreign_entity.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/migrations/000_base_foreign_entity.lua
new file mode 100644
index 00000000..f5a11c2b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/migrations/000_base_foreign_entity.lua
@@ -0,0 +1,31 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ CREATE TABLE IF NOT EXISTS "foreign_entities" (
+ "id" UUID PRIMARY KEY,
+ "name" TEXT UNIQUE,
+ "same" UUID
+ );
+
+ CREATE TABLE IF NOT EXISTS "foreign_references" (
+ "id" UUID PRIMARY KEY,
+ "name" TEXT UNIQUE,
+ "same_id" UUID REFERENCES "foreign_entities" ("id") ON DELETE CASCADE
+ );
+
+ DO $$
+ BEGIN
+ CREATE INDEX IF NOT EXISTS "foreign_references_fkey_same" ON "foreign_references" ("same_id");
+ EXCEPTION WHEN UNDEFINED_COLUMN THEN
+ -- Do nothing, accept existing state
+ END$$;
+ ]],
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/migrations/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/migrations/init.lua
new file mode 100644
index 00000000..ce8fe5ae
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/migrations/init.lua
@@ -0,0 +1,10 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ "000_base_foreign_entity",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/schema.lua
new file mode 100644
index 00000000..c24e8412
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/foreign-entity/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "foreign-entity",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/get-plugin-id/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/get-plugin-id/handler.lua
new file mode 100644
index 00000000..73148629
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/get-plugin-id/handler.lua
@@ -0,0 +1,17 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local PluginConfigDumpHandler = {
+ VERSION = "1.0.0",
+ PRIORITY = 1,
+}
+
+function PluginConfigDumpHandler:access(conf)
+ kong.response.exit(200, kong.plugin.get_id())
+end
+
+return PluginConfigDumpHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/get-plugin-id/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/get-plugin-id/schema.lua
new file mode 100644
index 00000000..ac85052d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/get-plugin-id/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "get-plugin-id",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/init-worker-lua-error/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/init-worker-lua-error/handler.lua
new file mode 100644
index 00000000..d2ef6ae6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/init-worker-lua-error/handler.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local InitWorkerLuaError = {}
+
+
+InitWorkerLuaError.PRIORITY = 1000
+InitWorkerLuaError.VERSION = "1.0"
+
+
+function InitWorkerLuaError:init_worker(conf)
+ error("this fails intentionally")
+end
+
+
+return InitWorkerLuaError
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/init-worker-lua-error/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/init-worker-lua-error/schema.lua
new file mode 100644
index 00000000..25c51020
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/init-worker-lua-error/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "init-worker-lua-error",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalid-schema/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalid-schema/handler.lua
new file mode 100644
index 00000000..e0eb6bca
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalid-schema/handler.lua
@@ -0,0 +1,13 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local InvalidSchemaHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+return InvalidSchemaHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalid-schema/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalid-schema/schema.lua
new file mode 100644
index 00000000..70fe40e6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalid-schema/schema.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "invalid-schema",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { foo = { type = "bar" } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalidations/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalidations/handler.lua
new file mode 100644
index 00000000..ee3486c8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalidations/handler.lua
@@ -0,0 +1,37 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local kong = kong
+local assert = assert
+
+
+local counts = {}
+
+
+local Invalidations = {
+ PRIORITY = 0,
+ VERSION = "1.0",
+}
+
+
+function Invalidations:init_worker()
+ assert(kong.cluster_events:subscribe("invalidations", function(key)
+ counts[key] = (counts[key] or 0) + 1
+ end))
+
+ assert(kong.cluster_events:subscribe("invalidations_kong_core_db_cache", function(key)
+ counts[key] = (counts[key] or 0) + 1
+ end))
+end
+
+
+function Invalidations:access(_)
+ return kong.response.exit(200, counts)
+end
+
+
+return Invalidations
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalidations/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalidations/schema.lua
new file mode 100644
index 00000000..a0031ec6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/invalidations/schema.lua
@@ -0,0 +1,34 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "invalidations",
+ fields = {
+ {
+ protocols = typedefs.protocols {
+ default = {
+ "http",
+ "https",
+ "tcp",
+ "tls",
+ "grpc",
+ "grpcs"
+ },
+ },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/license-error-validation/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/license-error-validation/handler.lua
new file mode 100644
index 00000000..7b7a3cc6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/license-error-validation/handler.lua
@@ -0,0 +1,29 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local Plugin = {
+ VERSION = "1.0.0",
+ PRIORITY = 1000,
+}
+
+local license_utils = require "kong.enterprise_edition.license_utils"
+local saved_validate_kong_license = license_utils.validate_kong_license
+
+-- monkey-patch license_utils so that we return custom license validation error
+license_utils.validate_kong_license = function(...)
+ local fh = io.open(kong.configuration.prefix .. "/license-error-validation")
+ if fh then
+ local content = fh:read("*a") or "oh no!"
+ fh:close()
+ ngx.log(ngx.WARN, "Using development (e.g. not a release) license validation: ", content)
+ return content
+ end
+
+ return saved_validate_kong_license(...)
+end
+
+return Plugin
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/license-error-validation/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/license-error-validation/schema.lua
new file mode 100644
index 00000000..0d988786
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/license-error-validation/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "license-error-validation",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger-last/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger-last/handler.lua
new file mode 100644
index 00000000..1c5abf4c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger-last/handler.lua
@@ -0,0 +1,27 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local LoggerHandler = require "spec.fixtures.custom_plugins.kong.plugins.logger.handler"
+
+local LoggerLastHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 0,
+}
+
+
+LoggerLastHandler.init_worker = LoggerHandler.init_worker
+LoggerLastHandler.configure = LoggerHandler.configure
+LoggerLastHandler.certificate = LoggerHandler.certificate
+LoggerLastHandler.preread = LoggerHandler.preread
+LoggerLastHandler.rewrite = LoggerHandler.rewrite
+LoggerLastHandler.access = LoggerHandler.access
+LoggerLastHandler.header_filter = LoggerHandler.header_filter
+LoggerLastHandler.body_filter = LoggerHandler.body_filter
+LoggerLastHandler.log = LoggerHandler.log
+
+
+return LoggerLastHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger-last/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger-last/schema.lua
new file mode 100644
index 00000000..c80ef607
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger-last/schema.lua
@@ -0,0 +1,25 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "logger-last",
+ fields = {
+ {
+ protocols = typedefs.protocols { default = { "http", "https", "tcp", "tls", "grpc", "grpcs" } },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger/handler.lua
new file mode 100644
index 00000000..105991d0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger/handler.lua
@@ -0,0 +1,59 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local LoggerHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+
+function LoggerHandler:init_worker()
+ kong.log("init_worker phase")
+end
+
+
+function LoggerHandler:configure(configs)
+ kong.log("configure phase")
+end
+
+
+function LoggerHandler:certificate(conf)
+ kong.log("certificate phase")
+end
+
+
+function LoggerHandler:preread(conf)
+ kong.log("preread phase")
+end
+
+
+function LoggerHandler:rewrite(conf)
+ kong.log("rewrite phase")
+end
+
+
+function LoggerHandler:access(conf)
+ kong.log("access phase")
+end
+
+
+function LoggerHandler:header_filter(conf)
+ kong.log("header_filter phase")
+end
+
+
+function LoggerHandler:body_filter(conf)
+ kong.log("body_filter phase")
+end
+
+
+function LoggerHandler:log(conf)
+ kong.log("log phase")
+end
+
+
+return LoggerHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger/schema.lua
new file mode 100644
index 00000000..b57026b7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/logger/schema.lua
@@ -0,0 +1,25 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "logger",
+ fields = {
+ {
+ protocols = typedefs.protocols { default = { "http", "https", "tcp", "tls", "grpc", "grpcs" } },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/max-args/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/max-args/handler.lua
new file mode 100644
index 00000000..c02d101a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/max-args/handler.lua
@@ -0,0 +1,79 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local nkeys = require "table.nkeys"
+
+
+local kong = kong
+local ngx = ngx
+
+
+
+local function get_response_headers(n)
+ local headers = {}
+
+ for i = 1, n - 2 do
+ headers["a" .. i] = "v" .. i
+ end
+
+ --headers["content-length"] = "0" (added by nginx/kong)
+ --headers["connection"] = "keep-alive" (added by nginx/kong)
+
+ return headers
+end
+
+
+local MaxArgsHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+
+function MaxArgsHandler:access(conf)
+ local client_args_count = nkeys(ngx.req.get_uri_args(0))
+
+ ngx.req.read_body()
+
+ kong.ctx.plugin.data = {
+ client_args_count = client_args_count,
+ kong = {
+ request_headers = kong.request.get_headers(),
+ uri_args = kong.request.get_query(),
+ post_args = kong.request.get_body() or {},
+ },
+ ngx = {
+ request_headers = ngx.req.get_headers(),
+ uri_args = ngx.req.get_uri_args(),
+ post_args = ngx.req.get_post_args(),
+ },
+ }
+
+ return kong.response.exit(200, "", get_response_headers(client_args_count))
+end
+
+
+function MaxArgsHandler:header_filter(conf)
+ local data = kong.ctx.plugin.data
+ return kong.response.exit(200, {
+ client_args_count = data.client_args_count,
+ kong = {
+ request_headers = data.kong.request_headers,
+ response_headers = kong.response.get_headers(),
+ uri_args = data.kong.uri_args,
+ post_args = data.kong.post_args,
+ },
+ ngx = {
+ request_headers = data.ngx.request_headers,
+ response_headers = ngx.resp.get_headers(),
+ uri_args = data.ngx.uri_args,
+ post_args = data.ngx.post_args,
+ }
+ })
+end
+
+
+return MaxArgsHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/max-args/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/max-args/schema.lua
new file mode 100644
index 00000000..777e7efd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/max-args/schema.lua
@@ -0,0 +1,25 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "max-args",
+ fields = {
+ {
+ protocols = typedefs.protocols { default = { "http", "https", "tcp", "tls", "grpc", "grpcs" } },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/muti-external-http-calls/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/muti-external-http-calls/handler.lua
new file mode 100644
index 00000000..aa688fd4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/muti-external-http-calls/handler.lua
@@ -0,0 +1,29 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local http = require "resty.http"
+
+local EnableBuffering = {
+ PRIORITY = 1000000,
+ VERSION = "1.0",
+}
+
+
+function EnableBuffering:access(conf)
+ local httpc = http.new()
+ httpc:set_timeout(1)
+
+ for suffix = 0, conf.calls - 1 do
+ local uri = "http://really.really.really.really.really.really.not.exists." .. suffix
+ pcall(function()
+ httpc:request_uri(uri)
+ end)
+ end
+end
+
+
+return EnableBuffering
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/muti-external-http-calls/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/muti-external-http-calls/schema.lua
new file mode 100644
index 00000000..46bf5d3d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/muti-external-http-calls/schema.lua
@@ -0,0 +1,25 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "muti-external-http-calls",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ {
+ calls = {
+ type = "number",
+ required = true,
+ },
+ }
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-config-dump/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-config-dump/handler.lua
new file mode 100644
index 00000000..0aea21e2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-config-dump/handler.lua
@@ -0,0 +1,18 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local PluginConfigDumpHandler = {
+ VERSION = "1.0.0",
+ PRIORITY = 1,
+}
+
+function PluginConfigDumpHandler:access(conf)
+ kong.response.exit(200, conf)
+end
+
+return PluginConfigDumpHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-config-dump/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-config-dump/schema.lua
new file mode 100644
index 00000000..95fd74a6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-config-dump/schema.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return {
+ name = "plugin-config-dump",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/custom_dao.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/custom_dao.lua
new file mode 100644
index 00000000..b19c85c7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/custom_dao.lua
@@ -0,0 +1,16 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local CustomDAO = {}
+
+
+function CustomDAO:custom_method()
+ return self.strategy:custom_method()
+end
+
+
+return CustomDAO
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/daos.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/daos.lua
new file mode 100644
index 00000000..a2a07866
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/daos.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ {
+ dao = "kong.plugins.plugin-with-custom-dao.custom_dao",
+ name = "custom_dao",
+ primary_key = { "id" },
+ fields = {
+ { id = typedefs.uuid },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/handler.lua
new file mode 100644
index 00000000..502ca42a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/handler.lua
@@ -0,0 +1,14 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local MyHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+
+return MyHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/schema.lua
new file mode 100644
index 00000000..5d877b3b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "plugin-with-custom-dao",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/strategies/postgres/custom_dao.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/strategies/postgres/custom_dao.lua
new file mode 100644
index 00000000..e890eb7a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/plugin-with-custom-dao/strategies/postgres/custom_dao.lua
@@ -0,0 +1,14 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local CustomDAO = {}
+
+function CustomDAO:custom_method()
+ return "I was implemented for postgres"
+end
+
+return CustomDAO
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/preserve-nulls/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/preserve-nulls/handler.lua
new file mode 100644
index 00000000..4d3525f7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/preserve-nulls/handler.lua
@@ -0,0 +1,24 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local kong = kong
+
+local PreserveNullsHandler = {
+ PRIORITY = 1000,
+ VERSION = "0.1.0",
+}
+
+function PreserveNullsHandler:access(plugin_conf)
+ kong.service.request.set_header(plugin_conf.request_header, "this is on a request")
+end
+
+function PreserveNullsHandler:header_filter(plugin_conf)
+ kong.response.set_header(plugin_conf.response_header, "this is on the response")
+end
+
+
+return PreserveNullsHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/preserve-nulls/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/preserve-nulls/schema.lua
new file mode 100644
index 00000000..e4557f67
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/preserve-nulls/schema.lua
@@ -0,0 +1,38 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+local PLUGIN_NAME = "PreserveNulls"
+
+local schema = {
+ name = PLUGIN_NAME,
+ fields = {
+ { consumer = typedefs.no_consumer },
+ { protocols = typedefs.protocols_http },
+ { config = {
+ type = "record",
+ fields = {
+ { request_header = typedefs.header_name {
+ required = true,
+ default = "Hello-World" } },
+ { response_header = typedefs.header_name {
+ required = true,
+ default = "Bye-World" } },
+ { large = {
+ type = "integer",
+ default = 100 } },
+ { ttl = {
+ type = "integer" } },
+ },
+ },
+ },
+ },
+}
+
+return schema
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reconfiguration-completion/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reconfiguration-completion/handler.lua
new file mode 100644
index 00000000..ee1dfeaa
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reconfiguration-completion/handler.lua
@@ -0,0 +1,36 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local kong_meta = require "kong.meta"
+
+local ReconfigurationCompletionHandler = {
+ VERSION = "1.0",
+ PRIORITY = 2000000,
+}
+
+
+function ReconfigurationCompletionHandler:rewrite(conf)
+ local status = "unknown"
+ local if_kong_configuration_version = kong.request and kong.request.get_header('if-kong-configuration-version')
+ if if_kong_configuration_version then
+ if if_kong_configuration_version ~= conf.version then
+ return kong.response.error(
+ 503,
+ "Service Unavailable",
+ {
+ ["X-Kong-Reconfiguration-Status"] = "pending",
+ ["Retry-After"] = tostring((kong.configuration.worker_state_update_frequency or 1) + 1),
+ }
+ )
+ else
+ status = "complete"
+ end
+ end
+ kong.response.set_header("X-Kong-Reconfiguration-Status", status)
+end
+
+return ReconfigurationCompletionHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reconfiguration-completion/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reconfiguration-completion/schema.lua
new file mode 100644
index 00000000..5ae96cee
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reconfiguration-completion/schema.lua
@@ -0,0 +1,23 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+return {
+ name = "reconfiguration-completion",
+ fields = {
+ { protocols = typedefs.protocols },
+ { config = {
+ type = "record",
+ fields = {
+ { version = { description = "Client-assigned version number for the current Kong Gateway configuration",
+ type = "string",
+ required = true, } },
+ },
+ }, },
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/redis-dummy/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/redis-dummy/handler.lua
new file mode 100644
index 00000000..1bc65f4c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/redis-dummy/handler.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local kong = kong
+
+local RedisDummy = {
+ PRIORITY = 1000,
+ VERSION = "0.1.0",
+}
+
+function RedisDummy:access(conf)
+ kong.log("access phase")
+end
+
+return RedisDummy
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/redis-dummy/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/redis-dummy/schema.lua
new file mode 100644
index 00000000..7666d74c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/redis-dummy/schema.lua
@@ -0,0 +1,22 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local redis_schema = require "kong.tools.redis.schema"
+
+return {
+ name = "redis-dummy",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { redis = redis_schema.config_schema },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reference-ca-cert/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reference-ca-cert/handler.lua
new file mode 100644
index 00000000..f865ebfd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reference-ca-cert/handler.lua
@@ -0,0 +1,13 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ReferenceCaCertHandler = {
+ VERSION = "1.0.0",
+ PRIORITY = 1,
+}
+
+return ReferenceCaCertHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reference-ca-cert/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reference-ca-cert/schema.lua
new file mode 100644
index 00000000..7b380450
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reference-ca-cert/schema.lua
@@ -0,0 +1,22 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "reference-ca-cert",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { pre_key = { type = "string", }, },
+ { ca_certificates = { type = "array", required = true, elements = { type = "string", uuid = true, }, }, },
+ { post_key = { type = "string", }, },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/api.lua
new file mode 100644
index 00000000..6d01be65
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/api.lua
@@ -0,0 +1,26 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local reports = require "kong.reports"
+local constants = require "kong.constants"
+
+
+return {
+ ["/reports/send-ping"] = {
+ POST = function(self)
+ -- if a port was passed, patch it in constants.REPORTS so
+ -- that tests can change the default reports port
+ if self.params.port then
+ constants.REPORTS.STATS_TLS_PORT = self.params.port
+ end
+
+ reports._sync_counter()
+ reports.send_ping()
+ kong.response.exit(200, { message = "ok" })
+ end,
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/handler.lua
new file mode 100644
index 00000000..fcbb0793
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/handler.lua
@@ -0,0 +1,22 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ReportsApiHandler = {
+ PRIORITY = 1000,
+ VERSION = "1.0",
+}
+
+function ReportsApiHandler:preread()
+ local reports = require "kong.reports"
+ reports._sync_counter()
+ reports.send_ping()
+ ngx.print("ok")
+ ngx.exit(200)
+end
+
+
+return ReportsApiHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/schema.lua
new file mode 100644
index 00000000..e8da127a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/reports-api/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "reports-api",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/handler.lua
new file mode 100644
index 00000000..a0b8c551
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/handler.lua
@@ -0,0 +1,48 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local RAT = require "kong.tools.request_aware_table"
+
+local kong = kong
+local tab
+
+local _M = {
+ PRIORITY = 1001,
+ VERSION = "1.0",
+}
+
+local function access_table()
+ -- write access
+ tab.foo = "bar"
+ tab.bar = "baz"
+ -- read/write access
+ tab.baz = tab.foo .. tab.bar
+end
+
+
+function _M:access(conf)
+ local query = kong.request.get_query()
+ if query.new_tab == "true" then
+ -- new table
+ tab = RAT.new()
+ ngx.exit(200)
+ end
+
+ if query.clear == "true" then
+ -- clear table
+ tab:clear()
+ ngx.exit(200)
+ end
+
+ -- access multiple times during same request
+ for _ = 1, 3 do
+ access_table()
+ end
+end
+
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/schema.lua
new file mode 100644
index 00000000..fc6d01b0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/request-aware-table/schema.lua
@@ -0,0 +1,18 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "request-aware-table",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = { }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/response-phase/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/response-phase/handler.lua
new file mode 100644
index 00000000..07a545b5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/response-phase/handler.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local resp_phase = {}
+
+
+resp_phase.PRIORITY = 950
+resp_phase.VERSION = "1.0-t"
+
+
+function resp_phase:access()
+end
+
+function resp_phase:response()
+end
+
+return resp_phase
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/response-phase/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/response-phase/schema.lua
new file mode 100644
index 00000000..a050c226
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/response-phase/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return {
+ name = "response-phase",
+ fields = {
+ { config = {
+ type = "record",
+ fields = {
+ },
+ }
+ }
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/rewriter/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/rewriter/handler.lua
new file mode 100644
index 00000000..5c20510b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/rewriter/handler.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- a plugin fixture to test running of the rewrite phase handler.
+
+local Rewriter = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+function Rewriter:rewrite(conf)
+ ngx.req.set_header("rewriter", conf.value)
+end
+
+return Rewriter
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/rewriter/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/rewriter/schema.lua
new file mode 100644
index 00000000..cb2a7729
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/rewriter/schema.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "rewriter",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { value = { type = "string" } },
+ { extra = { type = "string", default = "extra" } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/secret-response/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/secret-response/handler.lua
new file mode 100644
index 00000000..62108d22
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/secret-response/handler.lua
@@ -0,0 +1,29 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local kong_meta = require "kong.meta"
+local decode = require "cjson".decode
+
+
+local SecretResponse = {
+ PRIORITY = 529,
+ VERSION = kong_meta.core_version,
+}
+
+
+function SecretResponse:access()
+ local reference = kong.request.get_query_arg("reference")
+ local resp, err = kong.vault.get(reference)
+ if not resp then
+ return kong.response.exit(400, { message = err })
+ end
+ return kong.response.exit(200, decode(resp))
+end
+
+
+return SecretResponse
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/secret-response/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/secret-response/schema.lua
new file mode 100644
index 00000000..afe514ee
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/secret-response/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return {
+ name = "secret-response",
+ fields = {
+ { config = {
+ type = "record",
+ fields = {
+ },
+ }
+ }
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/set-consumer-group/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/set-consumer-group/handler.lua
new file mode 100644
index 00000000..f2a9c464
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/set-consumer-group/handler.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local SetConsumerGroup = {
+ VERSION = "9.9.9",
+ PRIORITY = 1000,
+}
+
+
+function SetConsumerGroup:access(conf)
+ local group_name = conf.group_name
+ local group_id = conf.group_id
+ kong.client.set_authenticated_consumer_groups({{ name = group_name, id = group_id }})
+ ngx.header["SetConsumerGroup-Was-Executed"] = "true"
+end
+
+return SetConsumerGroup
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/set-consumer-group/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/set-consumer-group/schema.lua
new file mode 100644
index 00000000..40093690
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/set-consumer-group/schema.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "set-consumer-group",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { group_name = { type = "string", required = true } },
+ { group_id = { type = "string", required = true } }
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/short-circuit/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/short-circuit/handler.lua
new file mode 100644
index 00000000..6ccd7f48
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/short-circuit/handler.lua
@@ -0,0 +1,47 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cjson = require "cjson"
+
+
+local kong = kong
+local tostring = tostring
+local init_worker_called = false
+
+
+local ShortCircuitHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000000,
+}
+
+
+function ShortCircuitHandler:init_worker()
+ init_worker_called = true
+end
+
+
+function ShortCircuitHandler:access(conf)
+ return kong.response.exit(conf.status, {
+ status = conf.status,
+ message = conf.message,
+ }, {
+ ["Kong-Init-Worker-Called"] = tostring(init_worker_called),
+ })
+end
+
+
+function ShortCircuitHandler:preread(conf)
+ local message = cjson.encode({
+ status = conf.status,
+ message = conf.message,
+ init_worker_called = init_worker_called,
+ })
+ return kong.response.exit(conf.status, message)
+end
+
+
+return ShortCircuitHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/short-circuit/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/short-circuit/schema.lua
new file mode 100644
index 00000000..02f301fc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/short-circuit/schema.lua
@@ -0,0 +1,27 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "short-circuit",
+ fields = {
+ {
+ protocols = typedefs.protocols { default = { "http", "https", "tcp", "tls" } },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ { status = { type = "integer", default = 503 } },
+ { message = { type = "string", default = "short-circuited" } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/api.lua
new file mode 100644
index 00000000..f9d6e2c8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/api.lua
@@ -0,0 +1,32 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ ["/slow-resource"] = {
+ GET = function(self)
+ local delay = self.params.delay or 1
+
+ if self.params.prime then
+ ngx.timer.at(0, function()
+ local _, err = kong.db.connector:query("SELECT pg_sleep(" .. delay .. ")")
+ if err then
+ ngx.log(ngx.ERR, err)
+ end
+ end)
+
+ return kong.response.exit(204)
+ end
+
+ local _, err = kong.db.connector:query("SELECT pg_sleep(" .. delay .. ")")
+ if err then
+ return kong.response.exit(500, { error = err })
+ end
+
+ return kong.response.exit(204)
+ end,
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/handler.lua
new file mode 100644
index 00000000..ecd85940
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/handler.lua
@@ -0,0 +1,14 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local SlowQueryHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+
+return SlowQueryHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/schema.lua
new file mode 100644
index 00000000..b4f37e39
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/slow-query/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "slow-query",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/api.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/api.lua
new file mode 100644
index 00000000..2a06f7b9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/api.lua
@@ -0,0 +1,26 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cjson_decode = require("cjson").decode
+
+
+return {
+ _stream = function(data)
+ local json = cjson_decode(data)
+ local action = json.action or "echo"
+
+ if action == "echo" then
+ return json.payload, json.err
+
+ elseif action == "rep" then
+ return string.rep("1", json.rep or 0)
+
+ elseif action == "throw" then
+ error(json.err or "error!")
+ end
+ end,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/handler.lua
new file mode 100644
index 00000000..787093e0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/handler.lua
@@ -0,0 +1,12 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return {
+ PRIORITY = 1000,
+ VERSION = "1.0",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/schema.lua
new file mode 100644
index 00000000..00ce3b2e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/stream-api-echo/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "stream-api-echo",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/tcp-trace-exporter/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/tcp-trace-exporter/handler.lua
new file mode 100644
index 00000000..a5f1bc50
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/tcp-trace-exporter/handler.lua
@@ -0,0 +1,180 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cjson = require "cjson"
+local str = require "resty.string"
+local http = require "resty.http"
+
+local ngx = ngx
+local kong = kong
+local table = table
+local insert = table.insert
+local to_hex = str.to_hex
+
+local _M = {
+ PRIORITY = 1001,
+ VERSION = "1.0",
+}
+
+local tracer_name = "tcp-trace-exporter"
+
+function _M:rewrite(config)
+ if not config.custom_spans then
+ return
+ end
+
+ local tracer = kong.tracing(tracer_name)
+
+ local span = tracer.start_span("rewrite", {
+ parent = kong.tracing.active_span(),
+ })
+ tracer.set_active_span(span)
+
+ -- tracing DNS!
+ local httpc = http.new()
+ -- Single-shot requests use the `request_uri` interface.
+ local res, err = httpc:request_uri("https://konghq.com", {
+ method = "GET",
+ })
+
+ if not res then
+ ngx.log(ngx.ERR, "request failed: ", err)
+ end
+end
+
+
+function _M:access(config)
+ local tracer = kong.tracing(tracer_name)
+
+ local span
+ if config.custom_spans then
+ span = tracer.start_span("access")
+ tracer.set_active_span(span)
+ end
+
+ kong.db.routes:page()
+
+ if span then
+ span:finish()
+ end
+end
+
+
+function _M:header_filter(config)
+ local tracer = kong.tracing(tracer_name)
+
+ local span
+ if config.custom_spans then
+ span = tracer.start_span("header_filter")
+ tracer.set_active_span(span)
+ end
+
+ if span then
+ span:finish()
+ end
+end
+
+
+local function push_data(premature, data, config)
+ if premature then
+ return
+ end
+
+ local tcpsock = ngx.socket.tcp()
+ tcpsock:settimeouts(10000, 10000, 10000)
+ local ok, err = tcpsock:connect(config.host, config.port)
+ if not ok then
+ kong.log.err("connect err: ".. err)
+ return
+ end
+ local _, err = tcpsock:send(data .. "\n")
+ if err then
+ kong.log.err(err)
+ end
+ tcpsock:close()
+end
+
+function _M:log(config)
+ local tracer = kong.tracing(tracer_name)
+ local span = tracer.active_span()
+
+ if span then
+ kong.log.debug("Exit span name: ", span.name)
+ span:finish()
+ end
+
+ kong.log.debug("Total spans: ", ngx.ctx.KONG_SPANS and #ngx.ctx.KONG_SPANS)
+
+ local spans = {}
+ local process_span = function (span)
+ if span.should_sample == false then
+ return
+ end
+ local s = table.clone(span)
+ s.tracer = nil
+ s.parent = nil
+ s.trace_id = to_hex(s.trace_id)
+ s.parent_id = s.parent_id and to_hex(s.parent_id)
+ s.span_id = to_hex(s.span_id)
+ insert(spans, s)
+ end
+ tracer.process_span(process_span)
+ kong.tracing.process_span(process_span)
+
+ local sort_by_start_time = function(a,b)
+ return a.start_time_ns < b.start_time_ns
+ end
+ table.sort(spans, sort_by_start_time)
+
+ local data = cjson.encode(spans)
+
+ local ok, err = ngx.timer.at(0, push_data, data, config)
+ if not ok then
+ kong.log.err("failed to create timer: ", err)
+ end
+end
+
+
+function _M:ws_close(config)
+ local tracer = kong.tracing(tracer_name)
+ local span = tracer.active_span()
+
+ if span then
+ kong.log.debug("Exit span name: ", span.name)
+ span:finish()
+ end
+
+ kong.log.debug("Total spans: ", ngx.ctx.KONG_SPANS and #ngx.ctx.KONG_SPANS)
+
+ local spans = {}
+ for _, span in ipairs(ngx.ctx.KONG_SPANS or {}) do
+ if span.should_sample == false then
+ return
+ end
+ local s = table.clone(span)
+ s.tracer = nil
+ s.parent = nil
+ s.trace_id = to_hex(s.trace_id)
+ s.parent_id = s.parent_id and to_hex(s.parent_id)
+ s.span_id = to_hex(s.span_id)
+ insert(spans, s)
+ end
+
+ local sort_by_start_time = function(a,b)
+ return a.start_time_ns < b.start_time_ns
+ end
+ table.sort(spans, sort_by_start_time)
+
+ local data = cjson.encode(spans)
+
+ local ok, err = ngx.timer.at(0, push_data, data, config)
+ if not ok then
+ kong.log.err("failed to create timer: ", err)
+ end
+end
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/tcp-trace-exporter/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/tcp-trace-exporter/schema.lua
new file mode 100644
index 00000000..b4e75475
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/tcp-trace-exporter/schema.lua
@@ -0,0 +1,24 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+return {
+ name = "tcp-trace-exporter",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { host = typedefs.host({ required = true }), },
+ { port = typedefs.port({ required = true }), },
+ { custom_spans = { type = "boolean", default = false }, }
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/daos.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/daos.lua
new file mode 100644
index 00000000..6de1f50a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/daos.lua
@@ -0,0 +1,79 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ {
+ name = "transformations",
+ primary_key = { "id" },
+ endpoint_key = "name",
+ fields = {
+ { id = typedefs.uuid },
+ { name = { type = "string" }, },
+ { secret = { type = "string", required = false, auto = true }, },
+ { hash_secret = { type = "boolean", required = true, default = false }, },
+ { meta = { type = "string", required = false, referenceable = true }, },
+ { case = { type = "string", required = false, referenceable = true }, },
+ },
+ transformations = {
+ {
+ input = { "hash_secret" },
+ needs = { "secret" },
+ on_write = function(hash_secret, client_secret)
+ if not hash_secret then
+ return {}
+ end
+ local hash = assert(ngx.md5(client_secret))
+ return {
+ secret = hash,
+ }
+ end,
+ },
+ {
+ input = { "meta" },
+ on_write = function(meta)
+ if not meta or meta == ngx.null then
+ return {}
+ end
+ return {
+ meta = string.reverse(meta),
+ }
+ end,
+ on_read = function(meta)
+ if not meta or meta == ngx.null then
+ return {}
+ end
+ return {
+ meta = string.reverse(meta),
+ }
+ end,
+ },
+ {
+ on_write = function(entity)
+ local case = entity.case
+ if not case or case == ngx.null then
+ return {}
+ end
+ return {
+ case = string.upper(case),
+ }
+ end,
+ on_read = function(entity)
+ local case = entity.case
+ if not case or case == ngx.null then
+ return {}
+ end
+ return {
+ case = string.lower(case),
+ }
+ end,
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/handler.lua
new file mode 100644
index 00000000..05c25c48
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/handler.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ PRIORITY = 1,
+ VERSION = "1.0",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/migrations/000_base_transformations.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/migrations/000_base_transformations.lua
new file mode 100644
index 00000000..c291cd83
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/migrations/000_base_transformations.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ CREATE TABLE IF NOT EXISTS "transformations" (
+ "id" UUID PRIMARY KEY,
+ "name" TEXT,
+ "secret" TEXT,
+ "hash_secret" BOOLEAN,
+ "meta" TEXT,
+ "case" TEXT
+ );
+ ]],
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/migrations/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/migrations/init.lua
new file mode 100644
index 00000000..568e1bbc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/migrations/init.lua
@@ -0,0 +1,10 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ "000_base_transformations",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/schema.lua
new file mode 100644
index 00000000..96a8a9f9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/transformations/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "transformations",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/daos.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/daos.lua
new file mode 100644
index 00000000..3b763568
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/daos.lua
@@ -0,0 +1,31 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ {
+ name = "unique_foreigns",
+ primary_key = { "id" },
+ admin_api_name = "unique-foreigns",
+ fields = {
+ { id = typedefs.uuid },
+ { name = { type = "string" }, },
+ },
+ },
+ {
+ name = "unique_references",
+ primary_key = { "id" },
+ admin_api_name = "unique-references",
+ fields = {
+ { id = typedefs.uuid },
+ { note = { type = "string" }, },
+ { unique_foreign = { type = "foreign", reference = "unique_foreigns", on_delete = "cascade", unique = true }, },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/handler.lua
new file mode 100644
index 00000000..05c25c48
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/handler.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ PRIORITY = 1,
+ VERSION = "1.0",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/migrations/000_base_unique_foreign.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/migrations/000_base_unique_foreign.lua
new file mode 100644
index 00000000..323576be
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/migrations/000_base_unique_foreign.lua
@@ -0,0 +1,23 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ CREATE TABLE IF NOT EXISTS "unique_foreigns" (
+ "id" UUID PRIMARY KEY,
+ "name" TEXT
+ );
+
+ CREATE TABLE IF NOT EXISTS "unique_references" (
+ "id" UUID PRIMARY KEY,
+ "note" TEXT,
+ "unique_foreign_id" UUID UNIQUE REFERENCES "unique_foreigns" ("id") ON DELETE CASCADE
+ );
+ ]],
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/migrations/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/migrations/init.lua
new file mode 100644
index 00000000..9dc9bec5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/migrations/init.lua
@@ -0,0 +1,10 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ "000_base_unique_foreign",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/schema.lua
new file mode 100644
index 00000000..c9d7e8e8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/unique-foreign/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "unique-foreign",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/daos.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/daos.lua
new file mode 100644
index 00000000..818f773f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/daos.lua
@@ -0,0 +1,17 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ {
+ name = "foos",
+ primary_key = { "color" },
+ fields = {
+ { color = { type = "string" } },
+ { shape = { type = "string" } },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/handler.lua
new file mode 100644
index 00000000..25dbd7ab
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/handler.lua
@@ -0,0 +1,14 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local WithMigrationHandler = {
+ VERSION = "0.1-t",
+ PRIORITY = 1000,
+}
+
+
+return WithMigrationHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/000_base_with_migrations.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/000_base_with_migrations.lua
new file mode 100644
index 00000000..cc1c0675
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/000_base_with_migrations.lua
@@ -0,0 +1,18 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ CREATE TABLE IF NOT EXISTS "foos" (
+ "color" TEXT PRIMARY KEY
+ );
+
+ INSERT INTO foos (color) values ('red') ON CONFLICT DO NOTHING;
+ ]],
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/001_14_to_15.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/001_14_to_15.lua
new file mode 100644
index 00000000..de2f62d3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/001_14_to_15.lua
@@ -0,0 +1,63 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ INSERT INTO sm_vaults ("id", "ws_id")
+ VALUES ('a79871a2-8bb4-4ed1-9f99-de53d8ad31d0', 'a79871a2-8bb4-4ed1-9f99-de53d8ad31d0')
+ ON CONFLICT DO NOTHING; -- Hack, mock data
+
+ DO $$
+ BEGIN
+ ALTER TABLE IF EXISTS ONLY "foos" ADD "shape" TEXT UNIQUE;
+ EXCEPTION WHEN DUPLICATE_COLUMN THEN
+ -- Do nothing, accept existing state
+ END;
+ $$;
+ ]],
+
+ teardown = function(connector, _)
+ local sql = [[
+ INSERT INTO sm_vaults ("id", "ws_id")
+ VALUES ('ebecaa2d-09f2-4176-9d3d-99826b85c5da', 'ebecaa2d-09f2-4176-9d3d-99826b85c5da')
+ ON CONFLICT DO NOTHING; -- Hack, mock data
+ ]]
+ assert(connector:query(sql))
+
+ -- update shape in all foos
+ for row, err in connector:iterate('SELECT * FROM "foos";') do
+ if err then
+ return nil, err
+ end
+
+ local shape = "triangle"
+ local sql = string.format([[
+ UPDATE "foos" SET "shape" = '%s' WHERE "color" = '%s';
+ ]], shape, row.color)
+ assert(connector:query(sql))
+ end
+
+
+ -- check insertion and update
+ local count = 0
+ for row, err in connector:iterate('SELECT * FROM "foos";') do
+ if err then
+ return nil, err
+ end
+
+ count = count + 1
+ assert(row.color == "red", "Wrong color: " .. tostring(row.color))
+ assert(row.shape == "triangle", "Wrong shape: " .. tostring(row.shape))
+ end
+
+ assert(count == 1, "Expected 1 foo, found " .. tostring(count))
+
+ return true
+ end,
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/init.lua
new file mode 100644
index 00000000..20767fcc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/migrations/init.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ "000_base_with_migrations",
+ "001_14_to_15",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/schema.lua
new file mode 100644
index 00000000..0615a621
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/with-migrations/schema.lua
@@ -0,0 +1,19 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "with-migrations",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/worker-events/handler.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/worker-events/handler.lua
new file mode 100644
index 00000000..615e9c2f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/worker-events/handler.lua
@@ -0,0 +1,71 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local semaphore = require "ngx.semaphore"
+local cjson = require "cjson"
+
+
+local ngx = ngx
+local kong = kong
+local table = table
+
+
+local worker_events = {}
+local sema
+
+
+local function load_data()
+ local ok, err = sema:wait(5)
+ if ok then
+ local data = table.remove(worker_events, 1)
+ if data then
+ return data
+ end
+
+ return {
+ error = "worker event data not found"
+ }
+ end
+
+ return {
+ error = err
+ }
+end
+
+
+local WorkerEventsHandler = {
+ PRIORITY = 500,
+}
+
+
+function WorkerEventsHandler.init_worker()
+ sema = semaphore.new()
+ kong.worker_events.register(function(data)
+ worker_events[#worker_events+1] = {
+ operation = data.operation,
+ entity = data.entity,
+ old_entity = data.old_entity,
+ }
+ sema:post()
+ end, "dao:crud")
+end
+
+
+function WorkerEventsHandler:preread()
+ local data = load_data()
+ local json = cjson.encode(data)
+ ngx.print(json)
+ return ngx.exit(200)
+end
+
+
+function WorkerEventsHandler:access()
+ return kong.response.exit(200, load_data())
+end
+
+
+return WorkerEventsHandler
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/worker-events/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/worker-events/schema.lua
new file mode 100644
index 00000000..db12abcd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_plugins/kong/plugins/worker-events/schema.lua
@@ -0,0 +1,25 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "worker-events",
+ fields = {
+ {
+ protocols = typedefs.protocols { default = { "http", "https", "tcp", "tls", "grpc", "grpcs" } },
+ },
+ {
+ config = {
+ type = "record",
+ fields = {
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/echo/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/echo/init.lua
new file mode 100644
index 00000000..6c2c5292
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/echo/init.lua
@@ -0,0 +1,25 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local encode = require "cjson".encode
+
+
+local function get(conf, resource, version)
+ return encode({
+ prefix = conf.prefix,
+ suffix = conf.suffix,
+ resource = resource,
+ version = version,
+ })
+end
+
+
+return {
+ VERSION = "1.0.0",
+ get = get,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/echo/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/echo/schema.lua
new file mode 100644
index 00000000..9dd43165
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/echo/schema.lua
@@ -0,0 +1,22 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return {
+ name = "echo",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { prefix = { type = "string" } },
+ { suffix = { type = "string" } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mock/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mock/init.lua
new file mode 100644
index 00000000..1b806a8c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mock/init.lua
@@ -0,0 +1,44 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local env = require "kong.vaults.env"
+local http = require "resty.http"
+
+
+local assert = assert
+local getenv = os.getenv
+
+
+local function init()
+ env.init()
+ assert(getenv("KONG_PROCESS_SECRETS") == nil, "KONG_PROCESS_SECRETS environment variable found")
+ assert(env.get({}, "KONG_PROCESS_SECRETS") == nil, "KONG_PROCESS_SECRETS environment variable found")
+end
+
+
+local function get(conf, resource, version)
+ local client, err = http.new()
+ if not client then
+ return nil, err
+ end
+
+ client:set_timeouts(20000, 20000, 20000)
+ assert(client:request_uri("http://mockbin.org/headers", {
+ headers = {
+ Accept = "application/json",
+ },
+ }))
+
+ return env.get(conf, resource, version)
+end
+
+
+return {
+ VERSION = "1.0.0",
+ init = init,
+ get = get,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mock/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mock/schema.lua
new file mode 100644
index 00000000..d54bf0e0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mock/schema.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ name = "mock",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { prefix = { type = "string", match = [[^[%a_][%a%d_]*$]] } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mocksocket/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mocksocket/init.lua
new file mode 100644
index 00000000..8faf52d4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mocksocket/init.lua
@@ -0,0 +1,45 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local env = require "kong.vaults.env"
+local http = require "resty.luasocket.http"
+
+
+local assert = assert
+local getenv = os.getenv
+
+
+local function init()
+ env.init()
+ assert(getenv("KONG_PROCESS_SECRETS") == nil, "KONG_PROCESS_SECRETS environment variable found")
+ assert(env.get({}, "KONG_PROCESS_SECRETS") == nil, "KONG_PROCESS_SECRETS environment variable found")
+end
+
+
+local function get(conf, resource, version)
+ local client, err = http.new()
+ if not client then
+ return nil, err
+ end
+
+ client:set_timeouts(20000, 20000, 20000)
+ assert(client:request_uri("http://mockbin.org/headers", {
+ headers = {
+ Accept = "application/json",
+ },
+ }))
+
+ return env.get(conf, resource, version)
+end
+
+
+return {
+ VERSION = "1.0.0",
+ init = init,
+ get = get,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mocksocket/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mocksocket/schema.lua
new file mode 100644
index 00000000..95a327e6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/mocksocket/schema.lua
@@ -0,0 +1,21 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return {
+ name = "mocksocket",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { prefix = { type = "string", match = [[^[%a_][%a%d_]*$]] } },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/random/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/random/init.lua
new file mode 100644
index 00000000..e7ba7f79
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/random/init.lua
@@ -0,0 +1,20 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local utils = require "kong.tools.rand"
+
+local function get(conf, resource, version)
+ -- Return a random string every time
+ kong.log.err("get() called")
+ return utils.random_string()
+end
+
+
+return {
+ VERSION = "1.0.0",
+ get = get,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/random/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/random/schema.lua
new file mode 100644
index 00000000..6748d43d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/random/schema.lua
@@ -0,0 +1,26 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local typedefs = require "kong.db.schema.typedefs"
+
+return {
+ name = "random",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { prefix = { type = "string" } },
+ { suffix = { type = "string" } },
+ { ttl = typedefs.ttl },
+ { neg_ttl = typedefs.ttl },
+ { resurrect_ttl = typedefs.ttl },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/test/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/test/init.lua
new file mode 100644
index 00000000..f9bcffee
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/test/init.lua
@@ -0,0 +1,227 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local cjson = require "cjson"
+local http = require "resty.http"
+
+local fmt = string.format
+
+
+local DEFAULTS_CONSUMED
+
+
+---
+-- Fake vault for integration tests.
+local test = {
+ VERSION = "1.0.0",
+ SHM_NAME = "test_vault",
+ PORT = 9876,
+}
+
+
+local function key_for(secret, version)
+ assert(secret ~= nil, "missing secret")
+ version = version or 1
+
+ return fmt("secrets:%s:%s", secret, version)
+end
+
+
+local function get_from_shm(secret, version)
+ local key = key_for(secret, version)
+ local shm = assert(ngx.shared[test.SHM_NAME])
+
+ local raw, err = shm:get(key)
+ assert(err == nil, err)
+
+ if raw then
+ return cjson.decode(raw)
+ end
+end
+
+
+local function delete_from_shm(secret)
+ local key = key_for(secret)
+ local shm = assert(ngx.shared[test.SHM_NAME])
+
+ shm:delete(key)
+end
+
+
+function test.init()
+end
+
+
+function test.get(conf, resource, version)
+ local secret = get_from_shm(resource, version)
+
+ kong.log.inspect({
+ conf = conf,
+ resource = resource,
+ version = version,
+ secret = secret,
+ })
+
+ secret = secret or {}
+
+ local latency = conf.latency or secret.latency
+ if latency then
+ ngx.sleep(latency)
+ end
+
+ local raise_error = conf.raise_error or secret.raise_error
+ local return_error = conf.return_error or secret.return_error
+
+ if raise_error then
+ error(raise_error)
+
+ elseif return_error then
+ return nil, return_error
+ end
+
+ local value = secret.value
+ local ttl = secret.ttl
+
+ if value == nil and not DEFAULTS_CONSUMED then
+ -- default values to be used only once, during startup. This is a hacky measure to make the test vault, which
+ -- uses Kong's nginx, work.
+ DEFAULTS_CONSUMED = true
+ value = conf.default_value
+ ttl = conf.default_value_ttl
+ end
+
+ return value, nil, ttl
+end
+
+
+function test.api()
+ local shm = assert(ngx.shared[test.SHM_NAME])
+ local secret = assert(ngx.var.secret)
+ local args = assert(kong.request.get_query())
+ local version = tonumber(args.version) or 1
+
+ local method = ngx.req.get_method()
+ if method == "GET" then
+ local value = get_from_shm(secret, version)
+ if value ~= nil then
+ return kong.response.exit(200, value)
+
+ else
+ return kong.response.exit(404, { message = "not found" })
+ end
+
+ elseif method == "DELETE" then
+ delete_from_shm(secret)
+ return kong.response.exit(204)
+
+ elseif method ~= "PUT" then
+ return kong.response.exit(405, { message = "method not allowed" })
+ end
+
+
+ local ttl = tonumber(args.ttl) or nil
+ local raise_error = args.raise_error or nil
+ local return_error = args.return_error or nil
+
+ local value
+ if not args.return_nil then
+ value = kong.request.get_raw_body()
+
+ if not value then
+ return kong.response.exit(400, {
+ message = "secret value expected, but the request body was empty"
+ })
+ end
+ end
+
+ local key = key_for(secret, version)
+ local object = {
+ value = value,
+ ttl = ttl,
+ raise_error = raise_error,
+ return_error = return_error,
+ }
+
+ assert(shm:set(key, cjson.encode(object)))
+
+ return kong.response.exit(201, object)
+end
+
+
+test.client = {}
+
+
+function test.client.put(secret, value, opts)
+ local client = assert(http.new())
+
+ opts = opts or {}
+
+ if value == nil then
+ opts.return_nil = true
+ end
+
+ local uri = fmt("http://127.0.0.1:%d/secret/%s", test.PORT, secret)
+
+ local res, err = client:request_uri(uri, {
+ method = "PUT",
+ body = value,
+ query = opts,
+ })
+
+ assert(err == nil, "failed PUT " .. uri .. ": " .. tostring(err))
+ assert(res.status == 201, "failed PUT " .. uri .. ": " .. res.status)
+
+ return cjson.decode(res.body)
+end
+
+
+function test.client.delete(secret)
+ local client = assert(http.new())
+
+ local uri = fmt("http://127.0.0.1:%d/secret/%s", test.PORT, secret)
+
+ local res, err = client:request_uri(uri, {
+ method = "DELETE",
+ })
+
+ assert(err == nil, "failed DELETE " .. uri .. ": " .. tostring(err))
+ assert(res.status == 204, "failed DELETE " .. uri .. ": " .. res.status)
+end
+
+
+function test.client.get(secret, version)
+ local query = version and { version = version } or nil
+
+ local client = assert(http.new())
+
+ local uri = fmt("http://127.0.0.1:%d/secret/%s", test.PORT, secret)
+
+ local res, err = client:request_uri(uri, { query = query, method = "GET" })
+ assert(err == nil, "failed GET " .. uri .. ": " .. tostring(err))
+
+ return cjson.decode(res.body)
+end
+
+
+test.http_mock = [[
+ lua_shared_dict ]] .. test.SHM_NAME .. [[ 5m;
+
+ server {
+ server_name "test-vault";
+ listen 127.0.0.1:]] .. test.PORT .. [[;
+
+ location ~^/secret/(?.+) {
+ content_by_lua_block {
+ require("kong.vaults.test").api()
+ }
+ }
+ }
+]]
+
+
+return test
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/test/schema.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/test/schema.lua
new file mode 100644
index 00000000..e752b860
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/custom_vaults/kong/vaults/test/schema.lua
@@ -0,0 +1,28 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local typedefs = require "kong.db.schema.typedefs"
+
+
+return {
+ name = "test",
+ fields = {
+ {
+ config = {
+ type = "record",
+ fields = {
+ { default_value = { type = "string", required = false } },
+ { default_value_ttl = { type = "number", required = false } },
+ { ttl = typedefs.ttl },
+ { neg_ttl = typedefs.ttl },
+ { resurrect_ttl = typedefs.ttl },
+ },
+ },
+ },
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/dc_blueprints.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/dc_blueprints.lua
new file mode 100644
index 00000000..cec6cc38
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/dc_blueprints.lua
@@ -0,0 +1,219 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local blueprints = require "spec.fixtures.blueprints"
+local assert = require "luassert"
+local cycle_aware_deep_copy = require("kong.tools.table").cycle_aware_deep_copy
+
+
+local dc_blueprints = {}
+
+
+local null = ngx.null
+
+
+local function new_config()
+ return {
+ _format_version = "3.0"
+ }
+end
+
+
+local function remove_nulls(tbl)
+ for k,v in pairs(tbl) do
+ if v == null then
+ tbl[k] = nil
+ elseif type(v) == "table" then
+ tbl[k] = remove_nulls(v)
+ end
+ end
+ return tbl
+end
+
+
+local function wrap_db(db)
+ local dc_as_db = {}
+
+ local config = new_config()
+
+ for name, _ in pairs(db.daos) do
+ dc_as_db[name] = {
+ insert = function(_, tbl)
+ tbl = cycle_aware_deep_copy(tbl)
+ if not config[name] then
+ config[name] = {}
+ end
+ local schema = db.daos[name].schema
+ tbl = schema:process_auto_fields(tbl, "insert")
+ for fname, field in schema:each_field() do
+ if field.type == "foreign" then
+ tbl[fname] = type(tbl[fname]) == "table"
+ and tbl[fname].id
+ or nil
+ end
+ end
+ table.insert(config[name], remove_nulls(tbl))
+ return cycle_aware_deep_copy(tbl)
+ end,
+ update = function(_, id, tbl)
+ if not config[name] then
+ return nil, "not found"
+ end
+ tbl = cycle_aware_deep_copy(tbl)
+ local element
+ for _, e in ipairs(config[name]) do
+ if e.id == id then
+ element = e
+ break
+ end
+ end
+ if not element then
+ return nil, "not found"
+ end
+ for k,v in pairs(tbl) do
+ element[k] = v
+ end
+ return element
+ end,
+ remove = function(_, id)
+ assert(id, "id is required")
+ if type(id) == "table" then
+ id = assert(type(id.id) == "string" and id.id)
+ end
+
+ if not config[name] then
+ return nil, "not found"
+ end
+
+ for idx, entity in ipairs(config[name]) do
+ if entity.id == id then
+ table.remove(config[name], idx)
+ return entity
+ end
+ end
+
+ return nil, "not found"
+ end,
+ }
+ end
+
+ dc_as_db.export = function()
+ return cycle_aware_deep_copy(config)
+ end
+
+ dc_as_db.import = function(input)
+ config = cycle_aware_deep_copy(input)
+ end
+
+ dc_as_db.reset = function()
+ config = new_config()
+ end
+
+ return dc_as_db
+end
+
+
+function dc_blueprints.new(db)
+ local dc_as_db = wrap_db(db)
+
+ local save_dc = new_config()
+
+ local bp = blueprints.new(dc_as_db)
+
+ bp.done = function()
+ local ret = dc_as_db.export()
+ save_dc = ret
+ dc_as_db.reset()
+ return ret
+ end
+
+ bp.reset_back = function()
+ dc_as_db.import(save_dc)
+ end
+
+ return bp
+end
+
+
+function dc_blueprints.admin_api(db, forced_port)
+ -- lazy import to avoid cyclical dependency
+ local helpers = require "spec.helpers"
+
+ db = db or helpers.db
+
+ local dc_as_db = wrap_db(db)
+ local api = {}
+
+ local function update_config()
+ local client = helpers.admin_client(nil, forced_port)
+
+ local res = client:post("/config", {
+ headers = {
+ ["Content-Type"] = "application/json",
+ },
+ body = dc_as_db.export(),
+ })
+
+ assert.response(res).has.status(201)
+ client:close()
+ return assert.response(res).has.jsonbody()
+ end
+
+ for name in pairs(db.daos) do
+ local dao = dc_as_db[name]
+
+ api[name] = {
+ insert = function(_, entity)
+ local res, err = dao:insert(entity)
+
+ if not res then
+ return nil, err
+ end
+
+ update_config()
+
+ return res
+ end,
+
+ update = function(_, id, updates)
+ local res, err = dao:update(id, updates)
+ if not res then
+ return nil, err
+ end
+
+ update_config()
+
+ return res
+ end,
+
+ remove = function(_, id)
+ local res, err = dao:remove(id)
+ if not res then
+ return nil, err
+ end
+
+ update_config()
+
+ return res
+ end,
+
+ truncate = function()
+ local config = dc_as_db.export()
+ config[name] = {}
+
+ dc_as_db.import(config)
+ update_config()
+
+ return true
+ end,
+ }
+ end
+
+ return blueprints.new(api)
+end
+
+return dc_blueprints
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/default_status_listen.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/default_status_listen.conf
new file mode 100644
index 00000000..88a615da
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/default_status_listen.conf
@@ -0,0 +1,26 @@
+# 1st digit is 9 for our test instances
+admin_listen = 127.0.0.1:9001
+proxy_listen = 0.0.0.0:9000, 0.0.0.0:9443 ssl
+
+ssl_cert = spec/fixtures/kong_spec.crt
+ssl_cert_key = spec/fixtures/kong_spec.key
+
+admin_ssl_cert = spec/fixtures/kong_spec.crt
+admin_ssl_cert_key = spec/fixtures/kong_spec.key
+
+database = postgres
+pg_host = 127.0.0.1
+pg_port = 5432
+pg_timeout = 10000
+pg_database = kong_tests
+anonymous_reports = off
+
+dns_hostsfile = spec/fixtures/hosts
+
+nginx_main_worker_processes = 1
+nginx_main_worker_rlimit_nofile = 4096
+nginx_events_worker_connections = 4096
+nginx_events_multi_accept = off
+
+prefix = servroot
+log_level = debug
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/dump_lmdb_key.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/dump_lmdb_key.lua
new file mode 100644
index 00000000..78837385
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/dump_lmdb_key.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local lmdb = require("resty.lmdb")
+local key = assert(arg[1])
+
+ngx.print(lmdb.get(key))
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.html b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.html
new file mode 100644
index 00000000..456e67b5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Custom template
+
+
+ Not the default
+ %s.
+ request_id: %s
+
+
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.json
new file mode 100644
index 00000000..e9c4c30c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.json
@@ -0,0 +1,4 @@
+{
+ "custom_template_message":"%s",
+ "request_id":"%s"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.plain b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.plain
new file mode 100644
index 00000000..fcf08c89
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.plain
@@ -0,0 +1,2 @@
+custom plain template: %s\n
+request_id: %s\n
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.xml b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.xml
new file mode 100644
index 00000000..c5039f20
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/error_templates/error_template.xml
@@ -0,0 +1,6 @@
+
+
+ custom template
+ %s
+ %s
+
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/factories/plugins.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/factories/plugins.lua
new file mode 100644
index 00000000..3e6fecf0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/factories/plugins.lua
@@ -0,0 +1,161 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local helpers = require "spec.helpers"
+
+local EntitiesFactory = {}
+
+function EntitiesFactory:setup(strategy)
+ local bp, _ = helpers.get_db_utils(strategy,
+ { "plugins",
+ "routes",
+ "services",
+ "consumers", },
+ { "key-auth", "request-transformer" })
+
+
+ local alice = assert(bp.consumers:insert {
+ custom_id = "alice"
+ })
+
+ local bob = assert(bp.consumers:insert {
+ username = "bob",
+ })
+
+ local eve = assert(bp.consumers:insert{
+ username = "eve"
+ })
+
+ assert(bp.keyauth_credentials:insert {
+ key = "bob",
+ consumer = { id = bob.id },
+ })
+ assert(bp.keyauth_credentials:insert {
+ key = "alice",
+ consumer = { id = alice.id },
+ })
+ assert(bp.keyauth_credentials:insert {
+ key = "eve",
+ consumer = { id = eve.id },
+ })
+
+ local service = assert(bp.services:insert {
+ path = "/anything",
+ })
+
+ local route = assert(bp.routes:insert {
+ service = { id = service.id },
+ hosts = { "route.test" },
+ })
+ assert(bp.key_auth_plugins:insert())
+
+ self.bp = bp
+ self.alice_id = alice.id
+ self.bob_id = bob.id
+ self.eve_id = eve.id
+ self.route_id = route.id
+ self.service_id = service.id
+ return self
+end
+
+local PluginFactory = {}
+function PluginFactory:setup(ef)
+ self.bp = ef.bp
+ self.bob_id = ef.bob_id
+ self.alice_id = ef.alice_id
+ self.eve_id = ef.eve_id
+ self.route_id = ef.route_id
+ self.service_id = ef.service_id
+ return self
+end
+
+function PluginFactory:produce(header_name, plugin_scopes)
+ local plugin_cfg = {
+ name = "request-transformer",
+ config = {
+ add = {
+ headers = { ("%s:true"):format(header_name) }
+ }
+ }
+ }
+ for k, v in pairs(plugin_scopes) do
+ plugin_cfg[k] = v
+ end
+ return assert(self.bp.plugins:insert(plugin_cfg))
+end
+
+function PluginFactory:consumer_route_service()
+ local header_name = "x-consumer-and-service-and-route"
+ self:produce(header_name, {
+ consumer = { id = self.alice_id },
+ service = { id = self.service_id },
+ route = { id = self.route_id },
+ })
+ return header_name
+end
+
+function PluginFactory:consumer_route()
+ local header_name = "x-consumer-and-route"
+ self:produce(header_name, {
+ consumer = { id = self.alice_id },
+ route = { id = self.route_id },
+ })
+ return header_name
+end
+
+function PluginFactory:consumer_service()
+ local header_name = "x-consumer-and-service"
+ self:produce(header_name, {
+ consumer = { id = self.alice_id },
+ service = { id = self.service_id },
+ })
+ return header_name
+end
+
+function PluginFactory:route_service()
+ local header_name = "x-route-and-service"
+ self:produce(header_name, {
+ route = { id = self.route_id },
+ service = { id = self.service_id },
+ })
+ return header_name
+end
+
+function PluginFactory:consumer()
+ local header_name = "x-consumer"
+ self:produce(header_name, {
+ consumer = { id = self.alice_id }
+ })
+ return header_name
+end
+
+function PluginFactory:route()
+ local header_name = "x-route"
+ self:produce(header_name, {
+ route = { id = self.route_id }
+ })
+ return header_name
+end
+
+function PluginFactory:service()
+ local header_name = "x-service"
+ self:produce(header_name, {
+ service = { id = self.service_id }
+ })
+ return header_name
+end
+
+function PluginFactory:global()
+ local header_name = "x-global"
+ self:produce(header_name, {})
+ return header_name
+end
+
+return {
+ PluginFactory = PluginFactory,
+ EntitiesFactory = EntitiesFactory
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy-server.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy-server.lua
new file mode 100644
index 00000000..ddedf576
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy-server.lua
@@ -0,0 +1,153 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local _M = {}
+
+local split = require("kong.tools.string").split
+
+local header_mt = {
+ __index = function(self, name)
+ name = name:lower():gsub("_", "-")
+ return rawget(self, name)
+ end,
+
+ __newindex = function(self, name, value)
+ name = name:lower():gsub("_", "-")
+ rawset(self, name, value)
+ end,
+}
+
+local function new_headers()
+ return setmetatable({}, header_mt)
+end
+
+-- This is a very naive forward proxy, which accepts a CONNECT over HTTP, and
+-- then starts tunnelling the bytes blind (for end-to-end SSL).
+function _M.connect(opts)
+ local req_sock = ngx.req.socket(true)
+ req_sock:settimeouts(1000, 1000, 1000)
+
+ -- receive request line
+ local req_line = req_sock:receive()
+ ngx.log(ngx.DEBUG, "request line: ", req_line)
+
+ local method, host_port = unpack(split(req_line, " "))
+ if method ~= "CONNECT" then
+ return ngx.exit(400)
+ end
+
+ local upstream_host, upstream_port = unpack(split(host_port, ":"))
+
+ local headers = new_headers()
+
+ -- receive headers
+ repeat
+ local line = req_sock:receive("*l")
+ local name, value = line:match("^([^:]+):%s*(.+)$")
+ if name and value then
+ ngx.log(ngx.DEBUG, "header: ", name, " => ", value)
+ headers[name] = value
+ end
+ until ngx.re.find(line, "^\\s*$", "jo")
+
+
+ local basic_auth = opts and opts.basic_auth
+ if basic_auth then
+ ngx.log(ngx.DEBUG, "checking proxy-authorization...")
+
+ local found = headers["proxy-authorization"]
+ if not found then
+ ngx.log(ngx.NOTICE, "client did not send proxy-authorization header")
+ ngx.print("HTTP/1.1 401 Unauthorized\r\n\r\n")
+ return ngx.exit(ngx.OK)
+ end
+
+ local auth = ngx.re.gsub(found, [[^Basic\s*]], "", "oji")
+
+ if auth ~= basic_auth then
+ ngx.log(ngx.NOTICE, "client sent incorrect proxy-authorization")
+ ngx.print("HTTP/1.1 403 Forbidden\r\n\r\n")
+ return ngx.exit(ngx.OK)
+ end
+
+ ngx.log(ngx.DEBUG, "accepted basic proxy-authorization")
+ end
+
+
+ -- Connect to requested upstream
+ local upstream_sock = ngx.socket.tcp()
+ upstream_sock:settimeouts(1000, 1000, 1000)
+ local ok, err = upstream_sock:connect(upstream_host, upstream_port)
+ if not ok then
+ ngx.log(ngx.ERR, "connect to upstream ", upstream_host, ":", upstream_port,
+ " failed: ", err)
+ return ngx.exit(504)
+ end
+
+ -- Tell the client we are good to go
+ ngx.print("HTTP/1.1 200 OK\r\n\r\n")
+ ngx.flush()
+
+ ngx.log(ngx.DEBUG, "tunneling started")
+
+ -- 10Kb in either direction should be plenty
+ local max_bytes = 10 * 1024
+
+ local should_exit = false
+
+ local upload = ngx.thread.spawn(function()
+ while not should_exit do
+ local req_data, err = req_sock:receiveany(max_bytes)
+ if req_data then
+ ngx.log(ngx.DEBUG, "client RCV ", #req_data, " bytes")
+
+ local bytes, err = upstream_sock:send(req_data)
+ if bytes then
+ ngx.log(ngx.DEBUG, "upstream SND ", bytes, " bytes")
+ elseif err then
+ ngx.log(ngx.ERR, "upstream SND failed: ", err)
+ break
+ end
+ elseif err ~= "timeout" then
+ ngx.log(ngx.ERR, "client RCV failed: ", err)
+ break
+ end
+ end
+ should_exit = true
+ end)
+
+ local download = ngx.thread.spawn(function()
+ while not should_exit do
+ local res_data, err = upstream_sock:receiveany(max_bytes)
+ if res_data then
+ ngx.log(ngx.DEBUG, "upstream RCV ", #res_data, " bytes")
+
+ local bytes, err = req_sock:send(res_data)
+ if bytes then
+ ngx.log(ngx.DEBUG, "client SND: ", bytes, " bytes")
+ elseif err then
+ ngx.log(ngx.ERR, "client SND failed: ", err)
+ break
+ end
+ elseif err ~= "timeout" then
+ ngx.log(ngx.ERR, "upstream RCV failed: ", err)
+ break
+ end
+ end
+ should_exit = true
+ end)
+
+ ngx.thread.wait(upload, download)
+ ngx.thread.kill(upload)
+ ngx.thread.kill(download)
+
+ upstream_sock:close()
+
+ ngx.log(ngx.DEBUG, "tunneling ended")
+end
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/ca.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/ca.crt
new file mode 100644
index 00000000..6b5555eb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/ca.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFoTCCA4mgAwIBAgIUQDBLwIychoRbVRO44IzBBk9R4oYwDQYJKoZIhvcNAQEL
+BQAwWDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoM
+DEtvbmcgVGVzdGluZzEdMBsGA1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcN
+MTkwNTAyMTkzNDQyWhcNMzkwNDI3MTkzNDQyWjBYMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMS29uZyBUZXN0aW5nMR0wGwYDVQQD
+DBRLb25nIFRlc3RpbmcgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
+AgoCggIBAMp6IggUp3aSNRbLAac8oOkrbUnFuxtlKGYgg8vfA2UU71qTktigdwO6
+Kod0/M+daO3RDqJJXQL2rD14NDO3MaextICanoQSEe+nYyMFUIk+QplXLD3fbshU
+nHoJcMS2w0x4cm1os4ebxR2Evndo6luz39ivcjau+BL+9iBAYL1g6+eGOjcSy7ft
+1nAMvbxcQ7dmbAH2KP6OmF8cok+eQWVqXEjqtVx5GDMDlj1BjX6Kulmh/vhNi3Hr
+NEi+kPrw/YtRgnqnN0sv3NnAyKnantxy7w0TDicFjiBsSIhjB5aUfWYErBR+Nj/m
+uumwc/kRJcHWklqDzxrZKCIyOyWcE5Dyjjr46cnF8HxhYwgZcwkmgTtaXOLpBMlo
+XUTgOQrWpm9HYg2vOJMMA/ZPUJ2tJ34/4RgiA00EJ5xG8r24suZmT775l+XFLFzp
+Ihxvs3BMbrWsXlcZkI5neNk7Q/1jLoBhWeTYjMpUS7bJ/49YVGQZFs3xu2IcLqeD
+5WsB1i+EqBAI0jm4vWEynsyX+kS2BqAiDtCsS6WYT2q00DTeP5eIHh/vHsm75jJ+
+yUEb1xFxGnNevLKNTcHUeXxPUnowdC6wqFnaJm7l09qVGDom7tLX9i6MCojgpAP0
+hMpBxzh8jLxHh+zZQdiORSFdYxNnlnWwbic2GUJruiQVLuhpseenAgMBAAGjYzBh
+MB0GA1UdDgQWBBQHT/IIheEC2kdBxI/TfGqUxWJw9zAfBgNVHSMEGDAWgBQHT/II
+heEC2kdBxI/TfGqUxWJw9zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQsFAAOCAgEAqXZjy4EltJCRtBmN0ohAHPWqH4ZJQCI2HrM3
+wHB6c4oPWcJ+M2PfmYPUJo9VMjvn4S3sZuAysyoHduvRdGDnElW4wglL1xxpoUOx
+FqoZUoYWV8hDFmUTWM5b4CtJxOPdTAd8VgypulM3iUEzBQrjR6tnMOdkiFMOmVag
+0/Nnr+Tcfk/crMCx3xsVnisYjJoQBFBH4UY+gWE/V/MS1Sya4/qTbuuCUq+Qym5P
+r8TkWAJlg7iVVLbZ2j94VUdpiQPWJEGMtJck/NEmOTruhhQlT7c1u/lqXCGj7uci
+LmhLsBVmdtWT9AWS8Rl7Qo5GXbjxKIaP3IM9axhDLm8WHwPRLx7DuIFEc+OBxJhz
+wkr0g0yLS0AMZpaC6UGbWX01ed10U01mQ/qPU5uZiB0GvruwsYWZsyL1QXUeqLz3
+/KKrx3XsXjtBu3ZG4LAnwuxfeZCNw9ofg8CqF9c20ko+7tZAv6DCu9UL+2oZnEyQ
+CboRDwpnAlQ7qJVSp2xMgunO3xxVMlhD5LZpEJz1lRT0nQV3uuLpMYNM4FS9OW/X
+MZSzwHhDdCTDWtc/iRszimOnYYV8Y0ubJcb59uhwcsHmdfnwL9DVO6X5xyzb8wsf
+wWaPbub8SN2jKnT0g6ZWuca4VwEo1fRaBkzSZDqXwhkBDWP8UBqLXMXWHdZaT8NK
+0NEO74c=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/ca.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/ca.key
new file mode 100644
index 00000000..22f7391c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/ca.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAynoiCBSndpI1FssBpzyg6SttScW7G2UoZiCDy98DZRTvWpOS
+2KB3A7oqh3T8z51o7dEOokldAvasPXg0M7cxp7G0gJqehBIR76djIwVQiT5CmVcs
+Pd9uyFSceglwxLbDTHhybWizh5vFHYS+d2jqW7Pf2K9yNq74Ev72IEBgvWDr54Y6
+NxLLt+3WcAy9vFxDt2ZsAfYo/o6YXxyiT55BZWpcSOq1XHkYMwOWPUGNfoq6WaH+
++E2Lces0SL6Q+vD9i1GCeqc3Sy/c2cDIqdqe3HLvDRMOJwWOIGxIiGMHlpR9ZgSs
+FH42P+a66bBz+RElwdaSWoPPGtkoIjI7JZwTkPKOOvjpycXwfGFjCBlzCSaBO1pc
+4ukEyWhdROA5Ctamb0diDa84kwwD9k9Qna0nfj/hGCIDTQQnnEbyvbiy5mZPvvmX
+5cUsXOkiHG+zcExutaxeVxmQjmd42TtD/WMugGFZ5NiMylRLtsn/j1hUZBkWzfG7
+Yhwup4PlawHWL4SoEAjSObi9YTKezJf6RLYGoCIO0KxLpZhParTQNN4/l4geH+8e
+ybvmMn7JQRvXEXEac168so1NwdR5fE9SejB0LrCoWdombuXT2pUYOibu0tf2LowK
+iOCkA/SEykHHOHyMvEeH7NlB2I5FIV1jE2eWdbBuJzYZQmu6JBUu6Gmx56cCAwEA
+AQKCAgBh8MQHbp42r7B4bwhEsgIP5868kaXZMYxiIjY+ZojI22CQSrQMj0oihmnO
+Dhu//Z9k8ewHOj+AkHtuXHe70FB3knECiEhHEEqWxzwgE5EKYhBrBgzDfRGkW7E5
+ItnmfZVopxaKr8uvu/yUM8LCFgDPDOopcWxo4SfkYGoD3cAtuvVBj98XBsN+G9DP
+cIpS07p5u1RheoYH5Ef2Me6dXqq5eMJdDxNdQMIg4wpIZS4hWM+dTcv8pd3e4+vt
+iCivCeVK/8mCtOH9P5Cv0B4Ac1zGu93AUEhXPcurCVXoiyZ/gyJJN9dZLlflfyFI
+qu7eOpot8jHnEL0cepB8Qhn0LlQTuv6rjJqmnl3tJA3S6rcM/mOjihkk1uo7JdDK
+vH498XR5qZPDlXZ8PVu3nI5EgXpmFIcCBuuVFS5QI63NZ32YqoGYXK37K7C9lQsL
+L/iR+YpwuQqDmM+UEETjBCIMKvxghFH0ICR041yg9tkjRhNKCAGc6n70wQDUq57s
+jjZmTQ4ZydxCsWVjLo7fCcoyQ9B7IUGPUUn8WavPUwtz1kG6VK7RDGa0KtgCD0vc
+iEwbWi9uwkZdoZdHcB8qLgCPjMGgRJLOyJ67xQ0RP+r+WkhUAjYcaucFonyyBhtv
+OrqNyEM3SEpgrzgttyyg+dP/cDvPbp4NXoxKAMyd8c7mjPploQKCAQEA+BL/qxLe
+LTKwe3TKpjAeyTB2iOxoWjtCqe3/xKbTS32Tn/VGwqhXyNeh+FTRhQu7fg5iL2C2
+JCOdYXWxRYIBwUh4HfApkgYzznUAU2vOh653MzW6LsOtDdgYF2cijN1ZFcbRTGpw
+eoA6U/cijuglwpTHF7zmRd9rSsv+PZ/fTDgY82MOdeaOUwyKuVyPUpNWfqSwxPd9
+tWEdOYjgq1llPbl1mktR0gYHIdHcSr1By7kmFw3/TQuic5Nm+FDidtfJYO36xFI1
+/CfwGVYeH42iul+KzdlITLAMRm2PAcWFjvxpw78T+xeUNpZlnZSgCIjtpfjywmXb
+uQvJoMMEX5PN1wKCAQEA0PIx4sgXiwqASa/foBB0Tk5jG3QWxucpqnLJURZeRqLQ
+BmF4WRrjs5V2y6iizegIcNmL0ZfwFBU79HwtAgFiTELLQL2xivhpMVjXL7QHeE4r
+A/9+49iO8wu8W/hwKxCDdGqXKyCKtW9b1yfUVB09j29GtApcV9v8KCTmDwYGrHI0
+DcEMtNLUbJvUeWFYFadJNFKxbsBtJPJIrYaiIyv2sL+Y3tZrYES72tTAYhMFwd0r
+9ooL5Ufrpuh4pHOxxA0Sh0EVUhNmyoq/ZJZ5wia+WB5NXBSD9JbciC5M4J8BMl/u
+Bx5RZbJSoAktYiOzev//6NHUmXsDjg3Kh9P48JVasQKCAQBVjt/k1bYQ6pmZirdV
+x+TmSLOpF7gJ3sRoLTB4V30qXR4sHgEQo9Ta7RvstPwqIdjBah6M7pMDNdFSyq+g
+JG2Mhvz+flUoCsGVZB7/pn/tpctwuwgClvQ5gR0V/TkaUkEmVJLdAxzV8yGq0eJ2
+XTSgvoVH95uH3712Z5LBGEGAXRyl3LUhDqpplDrIIVdBCJXdSdm5pQ4TH3Jf5Ihw
+MH3NYwhfdbi7cd7F2EZc9Jcbtzie3PH/VZLqv5zU6bihelz29Dz3ts7tr6yMYHo1
+Mbk9BDSwOE9KO7GQHLskxkYBAadMnrs6b3Brv0U+qwLizq7//jNjvpOgZ6Nbscbx
+W92zAoIBAQCNCK17cavSgggNtNSw6epXYLmssjMdlrKdBlW0kfCYpRTc+bWOD4Ra
+lyxUU0Nw0InCAlVJ59B4/cw2PgrzK5P5/avLyz6nmv0F/f1hiZbxMXH/hNlVWbtD
+ekxtl8e+iarxTXEz/wchaEUJeSzsicAfrPCAXe3ur+IIBr/yrBKdG4jfL8sv0o7n
+sFc+huI522yiEJ8LLn99TLyZxCJ0sxwUOX8qCnj3xe02zBv/Fu/v5yXhh1R4Mo9x
+XcDw39bBikFTYi7N86KSXAzMDHWrAxO/ztRQrthSo/G/SeFCTJE2O2IjE+fFSRRU
+SV2EvKxM/bbyo49o+YtwuwZVoFKLsYRBAoIBADaL9sx49XTHIIFGqEQP7NLEhs7D
+eJgSKP5oQ54J0iaoVpsoxng8DrTBkMVW75hiWzTW75EJnMXrauo/YfAbvsMM//3e
+BfRWvYpS5xKcHmXg2QJxy2VpvElHLg5Y2lligEZhO+5Sm2OG/hixBmiFvEvxPEB8
+8YIvYKcRAGA/HgDY9hGWSNsBP7qDXWP5kRm8qnB6zn33TVZMsXwUv6TP0cwsBKf7
+XDbnPBpOQK9nicehY7oscy9yTB9Q3bUHecYLY822ueCwaJgwJWFUH+Xe4u6xIH5l
+A/IyIfyOqxjUc34Me+37ehNmbTIxZ1BqLddppm9QsSAD7cDMurfb3pRpju4=
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example.com.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example.com.crt
new file mode 100644
index 00000000..8a3257f9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example.com.crt
@@ -0,0 +1,63 @@
+-----BEGIN CERTIFICATE-----
+MIIFTDCCAzSgAwIBAgICIAAwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzElMCMG
+A1UEAwwcS29uZyBUZXN0aW5nIEludGVybWlkaWF0ZSBDQTAeFw0xOTA1MDIxOTU1
+MjFaFw0yOTA0MjgxOTU1MjFaME8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxFDASBgNVBAMMC2V4YW1wbGUu
+Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqCozbPPts2H7CsUf
+4KlyKwbCOjqUa7ZhBcXfX5KuEHOvAUZfJOlm2TCNbO1wMTI1QHwjn6a9HM1njhBG
+4r9HH8CLckF83b247iJQbqUEjjvbb6DMTxjbC7dBunIikv6gUXeWGlRHupy/UEh8
+K0Y2KM2fm+HEbKI6zvjg/wb7zb0agzNaKV6fyEourKL0Xjz8ePm3kH58HaUmqhfk
+PPf7GnGW1xk/aIm6tsi9wzj2VBI/T3E5hVnMGrJTYnXh5DoFQrbuLvWtOB6MdZcM
+BWN/he8ISvvhKrctjWvUjpWgoZE9bRoMxkzxpHF/agM++WlHJrJ7my3yRHN3LspF
+4ER+/QIDAQABo4IBHzCCARswCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAw
+MwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZp
+Y2F0ZTAdBgNVHQ4EFgQUlpSl7QvKjdvJLx/sI3CXST3SqwowgYEGA1UdIwR6MHiA
+FAsOBA6X+G1iTyTwO8Zv0go7jRERoVykWjBYMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMS29uZyBUZXN0aW5nMR0wGwYDVQQDDBRL
+b25nIFRlc3RpbmcgUm9vdCBDQYICEAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQM
+MAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQA6kfAONRKeg0ygcyY8OwVi
+y9SGPKnKtOGjF2BSk74UF3bj63kx7utIQ0w5LZA3CwjcE2VzdPx+lQEDy/dbv442
+2bWrc7kG0/Dcr92KoUbzuuI0kPRoM/4rcOb8shKG9txFL1j44a60SWlbvkoNUD88
+vKZy2uSJ++OeQ7vuxrt2UDsfO2jRk74p/ztQibTX/cpwjvHrz2JcLTSxUZXK342x
+o7bWl1f7XMn8o7nPtNWHZq418uwFJ6OZO/rLc+FxE+31SnHYJUYC6/TSAg9kGomk
+Ws+K453QVoiPsG08Uz1JRjUQWotlEmqFAwax3kmfnrsiKmKy451CcwVAlyEIvnSb
+s2hEePHUaJltsatvFNPLnjcsOtqA46zJN0mv63BKuoa9fWAYr81D8wilcPgx534j
+KQcSv24cAoWesp/KhERK5G+F5mE0qnlCfMpFJFtfMjh+CDLbR//L4/0KQrSS/eRn
+ooeXinTpO5S2WOxk0W96rZMsBL2rBUI2qhfjBW8aQAiew4cMtddxryyUKskDlJPx
+bZXB2OmPibOOOTrTrBFkQ+tjKCuPKbOQDsIPTasZQKc2jK0boixXE8AXhN/A+3J4
+muvYnypmWGb0jMLEQT2u+RQzCNDjIOEHBP50XnoEX3jkOgEwknje89VDm/JXcClR
+l5HH4/9/AbS7rFCRnphOjQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFmjCCA4KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzEdMBsG
+A1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcNMTkwNTAyMTk0MDQ4WhcNMjkw
+NDI5MTk0MDQ4WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEV
+MBMGA1UECgwMS29uZyBUZXN0aW5nMSUwIwYDVQQDDBxLb25nIFRlc3RpbmcgSW50
+ZXJtaWRpYXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0dnj
+oHlJmNM94vQnK2FIIQJm9OAVvyMtAAkBKL7Cxt8G062GHDhq6gjQ9enuNQE0l3Vv
+mSAh7N9gNlma6YbRB9VeG54BCuRQwCxveOBiwQvC2qrTzYI34kF/AeflrDOdzuLb
+zj5cLADKXGCbGDtrSPKUwdlkuLs3pRr/YAyIQr7zJtlLz+E0GBYp0GWnLs0FiLSP
+qSBWllC9u8gt2MiKyNlXw+kZ8lofOehCJzfFr6qagVklPw+8IpU6OGmRLFQVwVhp
+zdAJmAGmSo/AGNKGqDdjzC4N2l4uYGH6n2KmY2yxsLBGZgwtLDst3fK4a3Wa5Tj7
+cUwCcGLGtfVTaIXZYbqQ0nGsaYUd/mhx3B3Jk1p3ILZ72nVYowhpj22ipPGal5hp
+ABh1MX3s/B+2ybWyDTtSaspcyhsRQsS6axB3DwLOLRy5Xp/kqEdConCtGCsjgm+U
+FzdupubXK+KIAmTKXDx8OM7Af/K7kLDfFTre40sEB6fwrWwH8yFojeqkA/Uqhn5S
+CzB0o4F3ON0xajsw2dRCziiq7pSe6ALLXetKpBr+xnVbUswH6BANUoDvh9thVPPx
+1trkv+OuoJalkruZaT+38+iV9xwdqxnR7PUawqSyvrEAxjqUo7dDPsEuOpx1DJjO
+XwRJCUjd7Ux913Iks24BqpPhEQz/rZzJLBApRVsCAwEAAaNmMGQwHQYDVR0OBBYE
+FAsOBA6X+G1iTyTwO8Zv0go7jRERMB8GA1UdIwQYMBaAFAdP8giF4QLaR0HEj9N8
+apTFYnD3MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4ICAQAWzIvIVM32iurqM451Amz0HNDG9j84cORnnaRR5opFTr3P
+EqI3QkgCyP6YOs9t0QSbA4ur9WUzd3c9Ktj3qRRgTE+98JBOPO0rv+Kjj48aANDV
+5tcbI9TZ9ap6g0jYr4XNT+KOO7E8QYlpY/wtokudCUDJE9vrsp1on4Bal2gjvCdh
+SU0C1lnj6q6kBdQSYHrcjiEIGJH21ayVoNaBVP/fxyCHz472w1xN220dxUI/GqB6
+pjcuy9cHjJHJKJbrkdt2eDRAFP5cILXc3mzUoGUDHY2JA1gtOHV0p4ix9R9AfI9x
+snBEFiD8oIpcQay8MJH/z3NLEPLoBW+JaAAs89P+jcppea5N9vbiAkrPi687BFTP
+PWPdstyttw6KrvtPQR1+FsVFcGeTjo32/UrckJixdiOEZgHk+deXpp7JoRdcsgzD
++okrsG79/LgS4icLmzNEp0IV36QckEq0+ALKDu6BXvWTkb5DB/FUrovZKJgkYeWj
+GKogyrPIXrYi725Ff306124kLbxiA+6iBbKUtCutQnvut78puC6iP+a2SrfsbUJ4
+qpvBFOY29Mlww88oWNGTA8QeW84Y1EJbRkHavzSsMFB73sxidQW0cHNC5t9RCKAQ
+uibeZgK1Yk7YQKXdvbZvXwrgTcAjCdbppw2L6e0Uy+OGgNjnIps8K460SdaIiA==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example.com.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example.com.key
new file mode 100644
index 00000000..c8ad8f2e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example.com.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAqCozbPPts2H7CsUf4KlyKwbCOjqUa7ZhBcXfX5KuEHOvAUZf
+JOlm2TCNbO1wMTI1QHwjn6a9HM1njhBG4r9HH8CLckF83b247iJQbqUEjjvbb6DM
+TxjbC7dBunIikv6gUXeWGlRHupy/UEh8K0Y2KM2fm+HEbKI6zvjg/wb7zb0agzNa
+KV6fyEourKL0Xjz8ePm3kH58HaUmqhfkPPf7GnGW1xk/aIm6tsi9wzj2VBI/T3E5
+hVnMGrJTYnXh5DoFQrbuLvWtOB6MdZcMBWN/he8ISvvhKrctjWvUjpWgoZE9bRoM
+xkzxpHF/agM++WlHJrJ7my3yRHN3LspF4ER+/QIDAQABAoIBACHkx5KpI3qpP+ju
+zDsCzAECDrmfvvRqwOlh9WCU9sJYHqi6H0kYReN2lrqirJ8tyG/j1WZDPBCHEd0f
+SLpA5TvwGesAagNjTteoUN/MILvuMo8wMJ2sm9GjsPq8MF3CNlvVJ4rM+9wP5btv
+sJ8kOpxEvWu0uFtQ41t97BNau/u+UtMk3oNCYBhiUWDg0rWPrUeX8cKzFSM8VAt4
+vvsybRHPbBmSLW01xO1Hq5cZdqbN4SxyQC1Ug9gW/afJQNRK7CubpWjOOQAsla0j
+ExyBxMMwDLLZfYCQpRn92ZB4x+LiaXqnbrtyfA+GLLjtlUPY6ClpdXa7KSN/mEuE
+LIIjiV0CgYEA2YsCt1+Yak+GOX6tSx1YUDz6UpZyOo2OKwqJ+G+SKT7JLT84A5nP
+rn6r6UUpNKhK3glpU1A1VJKiFnZ9qJi/gHJcNiSEIcUFHNFflDSmiZc6MyfcIkCZ
+xLUCrYHpETubTnB5P5jOhmsA3/uApaAc/Pv5hfSRzQv524k08TRgCU8CgYEAxeSQ
+MIRV1SKDYsEdEfXJE+WJLz2rlpR19l/9Wfuc5QVgAgDhFCCvHUX1ULU8yGQUSHqz
+DseR3iQF+Jvo3MK7pgC2fH6UePjakWOCXqXey1CpAzUHM/Qhwd451VqAGAT+Pabj
+tzPJ0cSC7sszxhwmAzotIUrjZDbuAzwQyRXdh/MCgYEApP2KVNt69H5V9bs+4W5j
+MY/d5s9V2VTNE5XNqI+uEfwdhmShLhH08on+BlC+/MH67kXDDT4TBI6lwlWh3kHj
+VB7oEuRFFnuf8ghV7ki0WjxJFs1PZucJ+Ke0XTXfN4O2uZoSS4qwcEAtjLLqEjPK
+aJEO4WrpPdOsb7WzYpDvmX8CgYEAwPunXZkAN0xkAl8+4S/mup+Ci+5BMiRvcSek
+4yaLl5AJU4rV9JH3E74QgHdt4iIu4Yu+qHAYoSBSLmKk0PyakEVrsLakReCxDU2U
+aoapYW60k6sX7iNq9CuqDJUoC8R6x1bEBPndG9LeuM6zG8SBkW4farMkU6t5qu/d
+kqvfEN8CgYAQphK9AoATrEomLbGwmcW+8JkjU9Sxnq+zo8io75wFAY2cUnQJQEId
+zGWwYjwHxXQCNCZ3ZwD7+iYgFHfxbPaiTWELkV2nEpBHQI0cLgzlcEBwo+uoFiYK
+33Yxb6EhNFSy7d2GPVZMjIR+KifCIem+i/3BiIlzneuFSRlnKORekQ==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example2.com.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example2.com.crt
new file mode 100644
index 00000000..85756548
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example2.com.crt
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEJzCCAg8CFAQ6oTnLBUHbumx1bxyY9kV0W21BMA0GCSqGSIb3DQEBCwUAMFgx
+CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxLb25n
+IFRlc3RpbmcxHTAbBgNVBAMMFEtvbmcgVGVzdGluZyBSb290IENBMB4XDTIwMDQx
+MzIzNDg0MVoXDTMwMDQxMTIzNDg0MVowSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
+AkNBMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxFTATBgNVBAMMDGV4YW1wbGUyLmNv
+bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+kZhxdN8PA3SW9cXiv
+xgtANq57PIWNnSDsg9Yxn0/+JKR45pSU+SKYtZUJJJuOdkv4IIJLm6uG6LbOPUDO
+g9EaV0Zw7RQtbY6EDFDFzeyq0/Mwl9wLJtXf0fPsXGyFIdeelBjzoSVsGGJKPWbP
+rlUtSHCrpFX53NTPnNVUJz9V6CdzZJgbyoiWP7ggKJeRPq6jCW5pt+cd+sR4+EPh
+daBmEVWeifLEKCbBvsQaOGfU1aVG1AlX0RpLBkTxOOFIIk/3dgWOsrek2ofjku4F
+g0MeWmD8oXOHUX2JxO77/BbLDQt0lzD27y/EkDoqy6mMAH85/LTYrU+P0WsEyexg
+CHcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAwWAxQjQOoGxU5LQu4ZmsCkps9y0B
+kNj8MUpLcFmK+02VIUh3hM4vuB6Gct2Ph+6zqCge3oqTXltU0Bs2MTwAKs/scsxq
+Mtanz4W00UlmG3QdgHaEs196tYQ8cKIaGZsNBv15VgBBduUG478pKqYE8bJKBbw7
+1Ym390hSPo0dNe7jLFXx6AaJvlEYh09P4FgfkXuY5VVTGXfN7XgJI073pLRY6iGH
+Qd+Egymh86AQtnoNpmqSCMNcjRVAyR8Ti3qnyro8ruZCnNYHieGeh/ZsZvhGDeWX
+v4YXjW2NDQ5+Ok6Gtvhf/l6RSrnXLbZtv8NStqwQJ+ydu05BJglZ/7Sb0uQYVNq2
+H8V+MtApFT6fG6ANM6hadNFG+p8Hwz6k4BLrc9ZxeWYKWIIusqExR9JIlGzEjvFJ
+6NmNjm3eZE9Ue4YgURj1KTr53wAso4LbJpz/zuZS+m9PMz7n8fRL25/Z5b/92L3a
+w0vsxUJyTDeMvYf8oT6OkxNVJ0zBRZNtEg5AJKdP6nU53V998jHP9vUisrU2ALhu
+Jw3QiWiDKnRtx8PiiRx7dWo+Xwn9+xVypytqNz3w/XJlOjMwOg73q399w+vMiFTl
+qdr7eYvaQBGOZVc3OdiP8afyVxlhHBowUoi8G+iPbgOsARHv/j4UeMVyIThzxv73
+a2EQ5BzyOzQ81H4=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example2.com.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example2.com.key
new file mode 100644
index 00000000..05c29251
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/forward-proxy/mtls_certs/example2.com.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAr6RmHF03w8DdJb1xeK/GC0A2rns8hY2dIOyD1jGfT/4kpHjm
+lJT5Ipi1lQkkm452S/gggkubq4bots49QM6D0RpXRnDtFC1tjoQMUMXN7KrT8zCX
+3Asm1d/R8+xcbIUh156UGPOhJWwYYko9Zs+uVS1IcKukVfnc1M+c1VQnP1XoJ3Nk
+mBvKiJY/uCAol5E+rqMJbmm35x36xHj4Q+F1oGYRVZ6J8sQoJsG+xBo4Z9TVpUbU
+CVfRGksGRPE44UgiT/d2BY6yt6Tah+OS7gWDQx5aYPyhc4dRfYnE7vv8FssNC3SX
+MPbvL8SQOirLqYwAfzn8tNitT4/RawTJ7GAIdwIDAQABAoIBAHMJzgdN1rRToYSS
+a7uMBL5htG7bMGyYsA1cW4zyu1F9NyqyNPOkDvjl5ChU8LEhwcFIJqKwOqlBlzIE
+KoJDwHo4MmlklSLeDh+FxTsyEwmraV6iuRPaCfmSusR0TqSVHfFHX+Bn0WfdQKs/
+zK+F3rzTB9sj0GKvYD/SKvpeP8Zuu9EBqo4N7PU3VHwDq5t32Ut/+M5XWtulsQcs
+qXr2R3agj/DnODANT6Dn/mJboTrYOSV18S/Yw/+OnWBcLzlT5sj0aLgrtXvIputv
+9caux4HklAQr29+lKB8nBTfjhXnBntMaEgqCVJ3ri83MuEfVDhmjwo6PnX22/J0h
+2XbCyUECgYEA2v8m+CTBTjdAqOuje34+UiWRzT2P9OFONV8nYgzEcQW5JkUoFCun
+KgQQIvjCsX4jY6/8w/IPF1ieTconZYJUWSyMZFtBBDCVif1GZRiiM2C4Zcero1KV
+U0V3wZcnYkzafzIHkqFUYZwamvdKWVI4c6F5MhSEKCgcbgKKI52TEokCgYEAzVHr
+KjQt+dqNkbipYoGH2ywLdcogDwKoyUFbgcvz/q625gu4am025wF25yRKExm7Dyjx
+eCQC+KOsBfJSc78fG5R6KPIDK1JrpUEGSCeqFICiqGv9kUzPf5zeGZVf9cU4tyPT
+5wYUEM7NX8VRoasZ4OUvYyYBw1Cx8vMdvQn/gv8CgYAIhxcFYqkEWrJx4XskO+5B
+VKUw0MziREO/YE0wTD76B7cF/ntpDaocwLvAIN+z+a13HEtDdhGQXysK7GxMT57p
+OgrdfZAykZHBJdOv7B2k0odbr0LHwVd/Pp1DNJecBFId0dzpoM6gXmvKzQZgJAt+
+tTL6+EGNLsKspfyrFl+7wQKBgQDAt2VuJbAJ1xQOdS+4IDCujfbrxp60uCBJVylW
++WK56LAP2WxtqLlhtsQuTKeiqgIkRp/vzo1jZ+0tX7f4oKnIL2NCT3aeESys3g3R
+aDmCKQOD5mkJGvmgpFLr3INHoqiLbfuV2uS2qgWnIQRwJLOTnksOWzxIYdPFYGDH
+cTz9bQKBgQDGv929DUinrKXe/uKJHLAcq+MjmF/+kZU9yn+svq6SSdplqp7xbXX4
+3T5HCWqD4Sy+PVzGaDg5YfXC8yaFPPfY0/35T2FoQEiCAPQO+07Smg6RqJ3yVpIm
+LTsbLleJTc8CX0bI4SukQ7MVQsiHimzyEzx3eyLt1S8aBdJuRFZ2mg==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go-hello.go b/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go-hello.go
new file mode 100644
index 00000000..f55099a1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go-hello.go
@@ -0,0 +1,79 @@
+/*
+A "hello world" plugin in Go,
+which reads a request header and sets a response header.
+*/
+package main
+
+import (
+ "fmt"
+ "github.com/Kong/go-pdk"
+ "github.com/Kong/go-pdk/server"
+)
+
+type Config struct {
+ Message string
+}
+
+func New() interface{} {
+ return &Config{}
+}
+
+func main() {
+ server.StartServer(New, "0.1", 1)
+}
+
+func (conf Config) Access(kong *pdk.PDK) {
+ host, err := kong.Request.GetHeader("host")
+ if err != nil {
+ kong.Log.Err(err.Error())
+ }
+ message := conf.Message
+ if message == "" {
+ message = "hello"
+ }
+ kong.Response.SetHeader("x-hello-from-go", fmt.Sprintf("Go says %s to %s", message, host))
+ kong.Ctx.SetShared("shared_msg", message)
+}
+
+func (conf Config) Log(kong *pdk.PDK) {
+ access_start, err := kong.Nginx.GetCtxFloat("KONG_ACCESS_START")
+ if err != nil {
+ kong.Log.Err(err.Error())
+ }
+ kong.Log.Debug("access_start: ", access_start)
+
+ header_value, err := kong.Request.GetHeader("X-Loose-Data")
+ if err != nil {
+ kong.Log.Err(err.Error())
+ }
+ kong.Log.Debug("request_header: ", header_value)
+
+ header_value, err = kong.Response.GetHeader("X-Powered-By")
+ if err != nil {
+ kong.Log.Err(err.Error())
+ }
+ kong.Log.Debug("response_header: ", header_value)
+
+ shared_msg, err := kong.Ctx.GetSharedString("shared_msg")
+ if err != nil {
+ kong.Log.Err(err.Error())
+ }
+
+ kong.Log.Debug("shared_msg: ", shared_msg)
+
+ serialized, err := kong.Log.Serialize()
+ if err != nil {
+ kong.Log.Err(err.Error())
+ }
+
+ kong.Log.Debug("serialized:", serialized)
+}
+
+func (conf Config) Response(kong *pdk.PDK) {
+ srvr, err := kong.ServiceResponse.GetHeader("Server")
+ if err != nil {
+ kong.Log.Err(err.Error())
+ }
+
+ kong.Response.SetHeader("x-hello-from-go-at-response", fmt.Sprintf("got from server '%s'", srvr))
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go.mod b/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go.mod
new file mode 100644
index 00000000..def3ac47
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go.mod
@@ -0,0 +1,5 @@
+module go-plugins
+
+go 1.13
+
+require github.com/Kong/go-pdk v0.7.1
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go.sum b/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go.sum
new file mode 100644
index 00000000..382f9ec5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/go/go.sum
@@ -0,0 +1,84 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Kong/go-pdk v0.7.1 h1:DWpmvuafH/35xws0VsXPyiGVtQmUuICnok9Hqolgdgg=
+github.com/Kong/go-pdk v0.7.1/go.mod h1:48+yltNveiFYTo6/I1AnmGn3m8goSQbtkfamH1zkwhw=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/ugorji/go v1.2.1 h1:dz+JxTe7GZQdErTo7SREc1jQj/hFP1k7jyIAwODoW+k=
+github.com/ugorji/go v1.2.1/go.mod h1:cSVypSfTLm2o9fKxXvQgn3rMmkPXovcWor6Qn5tbFmI=
+github.com/ugorji/go/codec v1.2.1 h1:/TRfW3XKkvWvmAYyCUaQlhoCDGjcvNR8xVVA/l5p/jQ=
+github.com/ugorji/go/codec v1.2.1/go.mod h1:s/WxCRi46t8rA+fowL40EnmD7ec0XhR7ZypxeBNdzsM=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/direct_imports.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/direct_imports.proto
new file mode 100644
index 00000000..1a4bb350
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/direct_imports.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+
+import "helloworld.proto";
+
+service Own {
+ rpc Open(hello.HelloRequest) returns (hello.HelloResponse);
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/annotations.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/annotations.proto
new file mode 100644
index 00000000..85c361b4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/annotations.proto
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+ // See `HttpRule`.
+ HttpRule http = 72295728;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/http.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/http.proto
new file mode 100644
index 00000000..285d7a3e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/http.proto
@@ -0,0 +1,318 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+ // A list of HTTP configuration rules that apply to individual API methods.
+ //
+ // **NOTE:** All service configuration rules follow "last one wins" order.
+ repeated HttpRule rules = 1;
+
+ // When set to true, URL path parameters will be fully URI-decoded except in
+ // cases of single segment matches in reserved expansion, where "%2F" will be
+ // left encoded.
+ //
+ // The default behavior is to not decode RFC 6570 reserved characters in multi
+ // segment matches.
+ bool fully_decode_reserved_expansion = 2;
+}
+
+// `HttpRule` defines the mapping of an RPC method to one or more HTTP
+// REST API methods. The mapping specifies how different portions of the RPC
+// request message are mapped to URL path, URL query parameters, and
+// HTTP request body. The mapping is typically specified as an
+// `google.api.http` annotation on the RPC method,
+// see "google/api/annotations.proto" for details.
+//
+// The mapping consists of a field specifying the path template and
+// method kind. The path template can refer to fields in the request
+// message, as in the example below which describes a REST GET
+// operation on a resource collection of messages:
+//
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}";
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // mapped to the URL
+// SubMessage sub = 2; // `sub.subfield` is url-mapped
+// }
+// message Message {
+// string text = 1; // content of the resource
+// }
+//
+// The same http annotation can alternatively be expressed inside the
+// `GRPC API Configuration` YAML file.
+//
+// http:
+// rules:
+// - selector: .Messaging.GetMessage
+// get: /v1/messages/{message_id}/{sub.subfield}
+//
+// This definition enables an automatic, bidrectional mapping of HTTP
+// JSON to RPC. Example:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))`
+//
+// In general, not only fields but also field paths can be referenced
+// from a path pattern. Fields mapped to the path pattern cannot be
+// repeated and must have a primitive (non-message) type.
+//
+// Any fields in the request message which are not bound by the path
+// pattern automatically become (optional) HTTP query
+// parameters. Assume the following definition of the request message:
+//
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http).get = "/v1/messages/{message_id}";
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // mapped to the URL
+// int64 revision = 2; // becomes a parameter
+// SubMessage sub = 3; // `sub.subfield` becomes a parameter
+// }
+//
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))`
+//
+// Note that fields which are mapped to HTTP parameters must have a
+// primitive type or a repeated primitive type. Message types are not
+// allowed. In the case of a repeated type, the parameter can be
+// repeated in the URL, as in `...?param=A¶m=B`.
+//
+// For HTTP method kinds which allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+//
+// service Messaging {
+// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// put: "/v1/messages/{message_id}"
+// body: "message"
+// };
+// }
+// }
+// message UpdateMessageRequest {
+// string message_id = 1; // mapped to the URL
+// Message message = 2; // mapped to the body
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | RPC
+// -----|-----
+// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body. This enables the following alternative definition of
+// the update method:
+//
+// service Messaging {
+// rpc UpdateMessage(Message) returns (Message) {
+// option (google.api.http) = {
+// put: "/v1/messages/{message_id}"
+// body: "*"
+// };
+// }
+// }
+// message Message {
+// string message_id = 1;
+// string text = 2;
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | RPC
+// -----|-----
+// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice of
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/messages/{message_id}"
+// additional_bindings {
+// get: "/v1/users/{user_id}/messages/{message_id}"
+// }
+// };
+// }
+// }
+// message GetMessageRequest {
+// string message_id = 1;
+// string user_id = 2;
+// }
+//
+//
+// This enables the following two alternative HTTP JSON to RPC
+// mappings:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")`
+//
+// # Rules for HTTP mapping
+//
+// The rules for mapping HTTP path, query parameters, and body fields
+// to the request message are as follows:
+//
+// 1. The `body` field specifies either `*` or a field path, or is
+// omitted. If omitted, it indicates there is no HTTP request body.
+// 2. Leaf fields (recursive expansion of nested messages in the
+// request) can be classified into three types:
+// (a) Matched in the URL template.
+// (b) Covered by body (if body is `*`, everything except (a) fields;
+// else everything under the body field)
+// (c) All other fields.
+// 3. URL query parameters found in the HTTP request are mapped to (c) fields.
+// 4. Any body sent with an HTTP request can contain only (b) fields.
+//
+// The syntax of the path template is as follows:
+//
+// Template = "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+//
+// The syntax `*` matches a single path segment. The syntax `**` matches zero
+// or more path segments, which must be the last part of the path except the
+// `Verb`. The syntax `LITERAL` matches literal text in the path.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path, all characters
+// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the
+// Discovery Document as `{var}`.
+//
+// If a variable contains one or more path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path, all
+// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables
+// show up in the Discovery Document as `{+var}`.
+//
+// NOTE: While the single segment variable matches the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2
+// Simple String Expansion, the multi segment variable **does not** match
+// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs.
+//
+// NOTE: the field paths in variables and in the `body` must not refer to
+// repeated fields or map fields.
+message HttpRule {
+ // Selects methods to which this rule applies.
+ //
+ // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+ string selector = 1;
+
+ // Determines the URL pattern is matched by this rules. This pattern can be
+ // used with any of the {get|put|post|delete|patch} methods. A custom method
+ // can be defined using the 'custom' field.
+ oneof pattern {
+ // Used for listing and getting information about resources.
+ string get = 2;
+
+ // Used for updating a resource.
+ string put = 3;
+
+ // Used for creating a resource.
+ string post = 4;
+
+ // Used for deleting a resource.
+ string delete = 5;
+
+ // Used for updating a resource.
+ string patch = 6;
+
+ // The custom pattern is used for specifying an HTTP method that is not
+ // included in the `pattern` field, such as HEAD, or "*" to leave the
+ // HTTP method unspecified for this rule. The wild-card rule is useful
+ // for services that provide content to Web (HTML) clients.
+ CustomHttpPattern custom = 8;
+ }
+
+ // The name of the request field whose value is mapped to the HTTP body, or
+ // `*` for mapping all fields not captured by the path pattern to the HTTP
+ // body. NOTE: the referred field must not be a repeated field and must be
+ // present at the top-level of request message type.
+ string body = 7;
+
+ // Optional. The name of the response field whose value is mapped to the HTTP
+ // body of response. Other response fields are ignored. When
+ // not set, the response message will be used as HTTP body of response.
+ string response_body = 12;
+
+ // Additional HTTP bindings for the selector. Nested bindings must
+ // not contain an `additional_bindings` field themselves (that is,
+ // the nesting may only be one level deep).
+ repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+ // The name of this custom HTTP verb.
+ string kind = 1;
+
+ // The path matched by this custom verb.
+ string path = 2;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/httpbody.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/httpbody.proto
new file mode 100644
index 00000000..4428515c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/api/httpbody.proto
@@ -0,0 +1,78 @@
+// Copyright 2018 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/protobuf/any.proto";
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody";
+option java_multiple_files = true;
+option java_outer_classname = "HttpBodyProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+// Message that represents an arbitrary HTTP body. It should only be used for
+// payload formats that can't be represented as JSON, such as raw binary or
+// an HTML page.
+//
+//
+// This message can be used both in streaming and non-streaming API methods in
+// the request as well as the response.
+//
+// It can be used as a top-level request field, which is convenient if one
+// wants to extract parameters from either the URL or HTTP template into the
+// request fields and also want access to the raw HTTP body.
+//
+// Example:
+//
+// message GetResourceRequest {
+// // A unique request id.
+// string request_id = 1;
+//
+// // The raw HTTP body is bound to this field.
+// google.api.HttpBody http_body = 2;
+// }
+//
+// service ResourceService {
+// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody);
+// rpc UpdateResource(google.api.HttpBody) returns
+// (google.protobuf.Empty);
+// }
+//
+// Example with streaming methods:
+//
+// service CaldavService {
+// rpc GetCalendar(stream google.api.HttpBody)
+// returns (stream google.api.HttpBody);
+// rpc UpdateCalendar(stream google.api.HttpBody)
+// returns (stream google.api.HttpBody);
+// }
+//
+// Use of this type only changes how the request and response bodies are
+// handled, all other features will continue to work unchanged.
+message HttpBody {
+ // The HTTP Content-Type header value specifying the content type of the body.
+ string content_type = 1;
+
+ // The HTTP request/response body as raw binary.
+ bytes data = 2;
+
+ // Application specific response metadata. Must be set in the first response
+ // for streaming APIs.
+ repeated google.protobuf.Any extensions = 3;
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/any.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/any.proto
new file mode 100644
index 00000000..49329425
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/any.proto
@@ -0,0 +1,154 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "github.com/golang/protobuf/ptypes/any";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "AnyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// `Any` contains an arbitrary serialized protocol buffer message along with a
+// URL that describes the type of the serialized message.
+//
+// Protobuf library provides support to pack/unpack Any values in the form
+// of utility functions or additional generated methods of the Any type.
+//
+// Example 1: Pack and unpack a message in C++.
+//
+// Foo foo = ...;
+// Any any;
+// any.PackFrom(foo);
+// ...
+// if (any.UnpackTo(&foo)) {
+// ...
+// }
+//
+// Example 2: Pack and unpack a message in Java.
+//
+// Foo foo = ...;
+// Any any = Any.pack(foo);
+// ...
+// if (any.is(Foo.class)) {
+// foo = any.unpack(Foo.class);
+// }
+//
+// Example 3: Pack and unpack a message in Python.
+//
+// foo = Foo(...)
+// any = Any()
+// any.Pack(foo)
+// ...
+// if any.Is(Foo.DESCRIPTOR):
+// any.Unpack(foo)
+// ...
+//
+// Example 4: Pack and unpack a message in Go
+//
+// foo := &pb.Foo{...}
+// any, err := ptypes.MarshalAny(foo)
+// ...
+// foo := &pb.Foo{}
+// if err := ptypes.UnmarshalAny(any, foo); err != nil {
+// ...
+// }
+//
+// The pack methods provided by protobuf library will by default use
+// 'type.googleapis.com/full.type.name' as the type URL and the unpack
+// methods only use the fully qualified type name after the last '/'
+// in the type URL, for example "foo.bar.com/x/y.z" will yield type
+// name "y.z".
+//
+//
+// JSON
+// ====
+// The JSON representation of an `Any` value uses the regular
+// representation of the deserialized, embedded message, with an
+// additional field `@type` which contains the type URL. Example:
+//
+// package google.profile;
+// message Person {
+// string first_name = 1;
+// string last_name = 2;
+// }
+//
+// {
+// "@type": "type.googleapis.com/google.profile.Person",
+// "firstName": ,
+// "lastName":
+// }
+//
+// If the embedded message type is well-known and has a custom JSON
+// representation, that representation will be embedded adding a field
+// `value` which holds the custom JSON in addition to the `@type`
+// field. Example (for message [google.protobuf.Duration][]):
+//
+// {
+// "@type": "type.googleapis.com/google.protobuf.Duration",
+// "value": "1.212s"
+// }
+//
+message Any {
+ // A URL/resource name that uniquely identifies the type of the serialized
+ // protocol buffer message. The last segment of the URL's path must represent
+ // the fully qualified name of the type (as in
+ // `path/google.protobuf.Duration`). The name should be in a canonical form
+ // (e.g., leading "." is not accepted).
+ //
+ // In practice, teams usually precompile into the binary all types that they
+ // expect it to use in the context of Any. However, for URLs which use the
+ // scheme `http`, `https`, or no scheme, one can optionally set up a type
+ // server that maps type URLs to message definitions as follows:
+ //
+ // * If no scheme is provided, `https` is assumed.
+ // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
+ // value in binary format, or produce an error.
+ // * Applications are allowed to cache lookup results based on the
+ // URL, or have them precompiled into a binary to avoid any
+ // lookup. Therefore, binary compatibility needs to be preserved
+ // on changes to types. (Use versioned type names to manage
+ // breaking changes.)
+ //
+ // Note: this functionality is not currently available in the official
+ // protobuf release, and it is not used for type URLs beginning with
+ // type.googleapis.com.
+ //
+ // Schemes other than `http`, `https` (or the empty scheme) might be
+ // used with implementation specific semantics.
+ //
+ string type_url = 1;
+
+ // Must be a valid serialized protocol buffer of the above specified type.
+ bytes value = 2;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/api.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/api.proto
new file mode 100644
index 00000000..f37ee2fa
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/api.proto
@@ -0,0 +1,210 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+import "google/protobuf/source_context.proto";
+import "google/protobuf/type.proto";
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "ApiProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/genproto/protobuf/api;api";
+
+// Api is a light-weight descriptor for an API Interface.
+//
+// Interfaces are also described as "protocol buffer services" in some contexts,
+// such as by the "service" keyword in a .proto file, but they are different
+// from API Services, which represent a concrete implementation of an interface
+// as opposed to simply a description of methods and bindings. They are also
+// sometimes simply referred to as "APIs" in other contexts, such as the name of
+// this message itself. See https://cloud.google.com/apis/design/glossary for
+// detailed terminology.
+message Api {
+
+ // The fully qualified name of this interface, including package name
+ // followed by the interface's simple name.
+ string name = 1;
+
+ // The methods of this interface, in unspecified order.
+ repeated Method methods = 2;
+
+ // Any metadata attached to the interface.
+ repeated Option options = 3;
+
+ // A version string for this interface. If specified, must have the form
+ // `major-version.minor-version`, as in `1.10`. If the minor version is
+ // omitted, it defaults to zero. If the entire version field is empty, the
+ // major version is derived from the package name, as outlined below. If the
+ // field is not empty, the version in the package name will be verified to be
+ // consistent with what is provided here.
+ //
+ // The versioning schema uses [semantic
+ // versioning](http://semver.org) where the major version number
+ // indicates a breaking change and the minor version an additive,
+ // non-breaking change. Both version numbers are signals to users
+ // what to expect from different versions, and should be carefully
+ // chosen based on the product plan.
+ //
+ // The major version is also reflected in the package name of the
+ // interface, which must end in `v`, as in
+ // `google.feature.v1`. For major versions 0 and 1, the suffix can
+ // be omitted. Zero major versions must only be used for
+ // experimental, non-GA interfaces.
+ //
+ //
+ string version = 4;
+
+ // Source context for the protocol buffer service represented by this
+ // message.
+ SourceContext source_context = 5;
+
+ // Included interfaces. See [Mixin][].
+ repeated Mixin mixins = 6;
+
+ // The source syntax of the service.
+ Syntax syntax = 7;
+}
+
+// Method represents a method of an API interface.
+message Method {
+
+ // The simple name of this method.
+ string name = 1;
+
+ // A URL of the input message type.
+ string request_type_url = 2;
+
+ // If true, the request is streamed.
+ bool request_streaming = 3;
+
+ // The URL of the output message type.
+ string response_type_url = 4;
+
+ // If true, the response is streamed.
+ bool response_streaming = 5;
+
+ // Any metadata attached to the method.
+ repeated Option options = 6;
+
+ // The source syntax of this method.
+ Syntax syntax = 7;
+}
+
+// Declares an API Interface to be included in this interface. The including
+// interface must redeclare all the methods from the included interface, but
+// documentation and options are inherited as follows:
+//
+// - If after comment and whitespace stripping, the documentation
+// string of the redeclared method is empty, it will be inherited
+// from the original method.
+//
+// - Each annotation belonging to the service config (http,
+// visibility) which is not set in the redeclared method will be
+// inherited.
+//
+// - If an http annotation is inherited, the path pattern will be
+// modified as follows. Any version prefix will be replaced by the
+// version of the including interface plus the [root][] path if
+// specified.
+//
+// Example of a simple mixin:
+//
+// package google.acl.v1;
+// service AccessControl {
+// // Get the underlying ACL object.
+// rpc GetAcl(GetAclRequest) returns (Acl) {
+// option (google.api.http).get = "/v1/{resource=**}:getAcl";
+// }
+// }
+//
+// package google.storage.v2;
+// service Storage {
+// rpc GetAcl(GetAclRequest) returns (Acl);
+//
+// // Get a data record.
+// rpc GetData(GetDataRequest) returns (Data) {
+// option (google.api.http).get = "/v2/{resource=**}";
+// }
+// }
+//
+// Example of a mixin configuration:
+//
+// apis:
+// - name: google.storage.v2.Storage
+// mixins:
+// - name: google.acl.v1.AccessControl
+//
+// The mixin construct implies that all methods in `AccessControl` are
+// also declared with same name and request/response types in
+// `Storage`. A documentation generator or annotation processor will
+// see the effective `Storage.GetAcl` method after inherting
+// documentation and annotations as follows:
+//
+// service Storage {
+// // Get the underlying ACL object.
+// rpc GetAcl(GetAclRequest) returns (Acl) {
+// option (google.api.http).get = "/v2/{resource=**}:getAcl";
+// }
+// ...
+// }
+//
+// Note how the version in the path pattern changed from `v1` to `v2`.
+//
+// If the `root` field in the mixin is specified, it should be a
+// relative path under which inherited HTTP paths are placed. Example:
+//
+// apis:
+// - name: google.storage.v2.Storage
+// mixins:
+// - name: google.acl.v1.AccessControl
+// root: acls
+//
+// This implies the following inherited HTTP annotation:
+//
+// service Storage {
+// // Get the underlying ACL object.
+// rpc GetAcl(GetAclRequest) returns (Acl) {
+// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl";
+// }
+// ...
+// }
+message Mixin {
+ // The fully qualified name of the interface which is included.
+ string name = 1;
+
+ // If non-empty specifies a path under which inherited HTTP paths
+ // are rooted.
+ string root = 2;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/descriptor.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/descriptor.proto
new file mode 100644
index 00000000..25addc92
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/descriptor.proto
@@ -0,0 +1,883 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+//
+// The messages in this file describe the definitions found in .proto files.
+// A valid .proto file can be translated directly to a FileDescriptorProto
+// without any other information (e.g. without reading its imports).
+
+
+syntax = "proto2";
+
+package google.protobuf;
+option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DescriptorProtos";
+option csharp_namespace = "Google.Protobuf.Reflection";
+option objc_class_prefix = "GPB";
+option cc_enable_arenas = true;
+
+// descriptor.proto must be optimized for speed because reflection-based
+// algorithms don't work during bootstrapping.
+option optimize_for = SPEED;
+
+// The protocol compiler can output a FileDescriptorSet containing the .proto
+// files it parses.
+message FileDescriptorSet {
+ repeated FileDescriptorProto file = 1;
+}
+
+// Describes a complete .proto file.
+message FileDescriptorProto {
+ optional string name = 1; // file name, relative to root of source tree
+ optional string package = 2; // e.g. "foo", "foo.bar", etc.
+
+ // Names of files imported by this file.
+ repeated string dependency = 3;
+ // Indexes of the public imported files in the dependency list above.
+ repeated int32 public_dependency = 10;
+ // Indexes of the weak imported files in the dependency list.
+ // For Google-internal migration only. Do not use.
+ repeated int32 weak_dependency = 11;
+
+ // All top-level definitions in this file.
+ repeated DescriptorProto message_type = 4;
+ repeated EnumDescriptorProto enum_type = 5;
+ repeated ServiceDescriptorProto service = 6;
+ repeated FieldDescriptorProto extension = 7;
+
+ optional FileOptions options = 8;
+
+ // This field contains optional information about the original source code.
+ // You may safely remove this entire field without harming runtime
+ // functionality of the descriptors -- the information is needed only by
+ // development tools.
+ optional SourceCodeInfo source_code_info = 9;
+
+ // The syntax of the proto file.
+ // The supported values are "proto2" and "proto3".
+ optional string syntax = 12;
+}
+
+// Describes a message type.
+message DescriptorProto {
+ optional string name = 1;
+
+ repeated FieldDescriptorProto field = 2;
+ repeated FieldDescriptorProto extension = 6;
+
+ repeated DescriptorProto nested_type = 3;
+ repeated EnumDescriptorProto enum_type = 4;
+
+ message ExtensionRange {
+ optional int32 start = 1;
+ optional int32 end = 2;
+
+ optional ExtensionRangeOptions options = 3;
+ }
+ repeated ExtensionRange extension_range = 5;
+
+ repeated OneofDescriptorProto oneof_decl = 8;
+
+ optional MessageOptions options = 7;
+
+ // Range of reserved tag numbers. Reserved tag numbers may not be used by
+ // fields or extension ranges in the same message. Reserved ranges may
+ // not overlap.
+ message ReservedRange {
+ optional int32 start = 1; // Inclusive.
+ optional int32 end = 2; // Exclusive.
+ }
+ repeated ReservedRange reserved_range = 9;
+ // Reserved field names, which may not be used by fields in the same message.
+ // A given name may only be reserved once.
+ repeated string reserved_name = 10;
+}
+
+message ExtensionRangeOptions {
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+// Describes a field within a message.
+message FieldDescriptorProto {
+ enum Type {
+ // 0 is reserved for errors.
+ // Order is weird for historical reasons.
+ TYPE_DOUBLE = 1;
+ TYPE_FLOAT = 2;
+ // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
+ // negative values are likely.
+ TYPE_INT64 = 3;
+ TYPE_UINT64 = 4;
+ // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
+ // negative values are likely.
+ TYPE_INT32 = 5;
+ TYPE_FIXED64 = 6;
+ TYPE_FIXED32 = 7;
+ TYPE_BOOL = 8;
+ TYPE_STRING = 9;
+ // Tag-delimited aggregate.
+ // Group type is deprecated and not supported in proto3. However, Proto3
+ // implementations should still be able to parse the group wire format and
+ // treat group fields as unknown fields.
+ TYPE_GROUP = 10;
+ TYPE_MESSAGE = 11; // Length-delimited aggregate.
+
+ // New in version 2.
+ TYPE_BYTES = 12;
+ TYPE_UINT32 = 13;
+ TYPE_ENUM = 14;
+ TYPE_SFIXED32 = 15;
+ TYPE_SFIXED64 = 16;
+ TYPE_SINT32 = 17; // Uses ZigZag encoding.
+ TYPE_SINT64 = 18; // Uses ZigZag encoding.
+ };
+
+ enum Label {
+ // 0 is reserved for errors
+ LABEL_OPTIONAL = 1;
+ LABEL_REQUIRED = 2;
+ LABEL_REPEATED = 3;
+ };
+
+ optional string name = 1;
+ optional int32 number = 3;
+ optional Label label = 4;
+
+ // If type_name is set, this need not be set. If both this and type_name
+ // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
+ optional Type type = 5;
+
+ // For message and enum types, this is the name of the type. If the name
+ // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
+ // rules are used to find the type (i.e. first the nested types within this
+ // message are searched, then within the parent, on up to the root
+ // namespace).
+ optional string type_name = 6;
+
+ // For extensions, this is the name of the type being extended. It is
+ // resolved in the same manner as type_name.
+ optional string extendee = 2;
+
+ // For numeric types, contains the original text representation of the value.
+ // For booleans, "true" or "false".
+ // For strings, contains the default text contents (not escaped in any way).
+ // For bytes, contains the C escaped value. All bytes >= 128 are escaped.
+ // TODO(kenton): Base-64 encode?
+ optional string default_value = 7;
+
+ // If set, gives the index of a oneof in the containing type's oneof_decl
+ // list. This field is a member of that oneof.
+ optional int32 oneof_index = 9;
+
+ // JSON name of this field. The value is set by protocol compiler. If the
+ // user has set a "json_name" option on this field, that option's value
+ // will be used. Otherwise, it's deduced from the field's name by converting
+ // it to camelCase.
+ optional string json_name = 10;
+
+ optional FieldOptions options = 8;
+}
+
+// Describes a oneof.
+message OneofDescriptorProto {
+ optional string name = 1;
+ optional OneofOptions options = 2;
+}
+
+// Describes an enum type.
+message EnumDescriptorProto {
+ optional string name = 1;
+
+ repeated EnumValueDescriptorProto value = 2;
+
+ optional EnumOptions options = 3;
+
+ // Range of reserved numeric values. Reserved values may not be used by
+ // entries in the same enum. Reserved ranges may not overlap.
+ //
+ // Note that this is distinct from DescriptorProto.ReservedRange in that it
+ // is inclusive such that it can appropriately represent the entire int32
+ // domain.
+ message EnumReservedRange {
+ optional int32 start = 1; // Inclusive.
+ optional int32 end = 2; // Inclusive.
+ }
+
+ // Range of reserved numeric values. Reserved numeric values may not be used
+ // by enum values in the same enum declaration. Reserved ranges may not
+ // overlap.
+ repeated EnumReservedRange reserved_range = 4;
+
+ // Reserved enum value names, which may not be reused. A given name may only
+ // be reserved once.
+ repeated string reserved_name = 5;
+}
+
+// Describes a value within an enum.
+message EnumValueDescriptorProto {
+ optional string name = 1;
+ optional int32 number = 2;
+
+ optional EnumValueOptions options = 3;
+}
+
+// Describes a service.
+message ServiceDescriptorProto {
+ optional string name = 1;
+ repeated MethodDescriptorProto method = 2;
+
+ optional ServiceOptions options = 3;
+}
+
+// Describes a method of a service.
+message MethodDescriptorProto {
+ optional string name = 1;
+
+ // Input and output type names. These are resolved in the same way as
+ // FieldDescriptorProto.type_name, but must refer to a message type.
+ optional string input_type = 2;
+ optional string output_type = 3;
+
+ optional MethodOptions options = 4;
+
+ // Identifies if client streams multiple client messages
+ optional bool client_streaming = 5 [default=false];
+ // Identifies if server streams multiple server messages
+ optional bool server_streaming = 6 [default=false];
+}
+
+
+// ===================================================================
+// Options
+
+// Each of the definitions above may have "options" attached. These are
+// just annotations which may cause code to be generated slightly differently
+// or may contain hints for code that manipulates protocol messages.
+//
+// Clients may define custom options as extensions of the *Options messages.
+// These extensions may not yet be known at parsing time, so the parser cannot
+// store the values in them. Instead it stores them in a field in the *Options
+// message called uninterpreted_option. This field must have the same name
+// across all *Options messages. We then use this field to populate the
+// extensions when we build a descriptor, at which point all protos have been
+// parsed and so all extensions are known.
+//
+// Extension numbers for custom options may be chosen as follows:
+// * For options which will only be used within a single application or
+// organization, or for experimental options, use field numbers 50000
+// through 99999. It is up to you to ensure that you do not use the
+// same number for multiple options.
+// * For options which will be published and used publicly by multiple
+// independent entities, e-mail protobuf-global-extension-registry@google.com
+// to reserve extension numbers. Simply provide your project name (e.g.
+// Objective-C plugin) and your project website (if available) -- there's no
+// need to explain how you intend to use them. Usually you only need one
+// extension number. You can declare multiple options with only one extension
+// number by putting them in a sub-message. See the Custom Options section of
+// the docs for examples:
+// https://developers.google.com/protocol-buffers/docs/proto#options
+// If this turns out to be popular, a web service will be set up
+// to automatically assign option numbers.
+
+
+message FileOptions {
+
+ // Sets the Java package where classes generated from this .proto will be
+ // placed. By default, the proto package is used, but this is often
+ // inappropriate because proto packages do not normally start with backwards
+ // domain names.
+ optional string java_package = 1;
+
+
+ // If set, all the classes from the .proto file are wrapped in a single
+ // outer class with the given name. This applies to both Proto1
+ // (equivalent to the old "--one_java_file" option) and Proto2 (where
+ // a .proto always translates to a single class, but you may want to
+ // explicitly choose the class name).
+ optional string java_outer_classname = 8;
+
+ // If set true, then the Java code generator will generate a separate .java
+ // file for each top-level message, enum, and service defined in the .proto
+ // file. Thus, these types will *not* be nested inside the outer class
+ // named by java_outer_classname. However, the outer class will still be
+ // generated to contain the file's getDescriptor() method as well as any
+ // top-level extensions defined in the file.
+ optional bool java_multiple_files = 10 [default=false];
+
+ // This option does nothing.
+ optional bool java_generate_equals_and_hash = 20 [deprecated=true];
+
+ // If set true, then the Java2 code generator will generate code that
+ // throws an exception whenever an attempt is made to assign a non-UTF-8
+ // byte sequence to a string field.
+ // Message reflection will do the same.
+ // However, an extension field still accepts non-UTF-8 byte sequences.
+ // This option has no effect on when used with the lite runtime.
+ optional bool java_string_check_utf8 = 27 [default=false];
+
+
+ // Generated classes can be optimized for speed or code size.
+ enum OptimizeMode {
+ SPEED = 1; // Generate complete code for parsing, serialization,
+ // etc.
+ CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
+ LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
+ }
+ optional OptimizeMode optimize_for = 9 [default=SPEED];
+
+ // Sets the Go package where structs generated from this .proto will be
+ // placed. If omitted, the Go package will be derived from the following:
+ // - The basename of the package import path, if provided.
+ // - Otherwise, the package statement in the .proto file, if present.
+ // - Otherwise, the basename of the .proto file, without extension.
+ optional string go_package = 11;
+
+
+
+ // Should generic services be generated in each language? "Generic" services
+ // are not specific to any particular RPC system. They are generated by the
+ // main code generators in each language (without additional plugins).
+ // Generic services were the only kind of service generation supported by
+ // early versions of google.protobuf.
+ //
+ // Generic services are now considered deprecated in favor of using plugins
+ // that generate code specific to your particular RPC system. Therefore,
+ // these default to false. Old code which depends on generic services should
+ // explicitly set them to true.
+ optional bool cc_generic_services = 16 [default=false];
+ optional bool java_generic_services = 17 [default=false];
+ optional bool py_generic_services = 18 [default=false];
+ optional bool php_generic_services = 42 [default=false];
+
+ // Is this file deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for everything in the file, or it will be completely ignored; in the very
+ // least, this is a formalization for deprecating files.
+ optional bool deprecated = 23 [default=false];
+
+ // Enables the use of arenas for the proto messages in this file. This applies
+ // only to generated classes for C++.
+ optional bool cc_enable_arenas = 31 [default=false];
+
+
+ // Sets the objective c class prefix which is prepended to all objective c
+ // generated classes from this .proto. There is no default.
+ optional string objc_class_prefix = 36;
+
+ // Namespace for generated classes; defaults to the package.
+ optional string csharp_namespace = 37;
+
+ // By default Swift generators will take the proto package and CamelCase it
+ // replacing '.' with underscore and use that to prefix the types/symbols
+ // defined. When this options is provided, they will use this value instead
+ // to prefix the types/symbols defined.
+ optional string swift_prefix = 39;
+
+ // Sets the php class prefix which is prepended to all php generated classes
+ // from this .proto. Default is empty.
+ optional string php_class_prefix = 40;
+
+ // Use this option to change the namespace of php generated classes. Default
+ // is empty. When this option is empty, the package name will be used for
+ // determining the namespace.
+ optional string php_namespace = 41;
+
+
+ // Use this option to change the namespace of php generated metadata classes.
+ // Default is empty. When this option is empty, the proto file name will be used
+ // for determining the namespace.
+ optional string php_metadata_namespace = 44;
+
+ // Use this option to change the package of ruby generated classes. Default
+ // is empty. When this option is not set, the package name will be used for
+ // determining the ruby package.
+ optional string ruby_package = 45;
+
+ // The parser stores options it doesn't recognize here.
+ // See the documentation for the "Options" section above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message.
+ // See the documentation for the "Options" section above.
+ extensions 1000 to max;
+
+ reserved 38;
+}
+
+message MessageOptions {
+ // Set true to use the old proto1 MessageSet wire format for extensions.
+ // This is provided for backwards-compatibility with the MessageSet wire
+ // format. You should not use this for any other reason: It's less
+ // efficient, has fewer features, and is more complicated.
+ //
+ // The message must be defined exactly as follows:
+ // message Foo {
+ // option message_set_wire_format = true;
+ // extensions 4 to max;
+ // }
+ // Note that the message cannot have any defined fields; MessageSets only
+ // have extensions.
+ //
+ // All extensions of your type must be singular messages; e.g. they cannot
+ // be int32s, enums, or repeated messages.
+ //
+ // Because this is an option, the above two restrictions are not enforced by
+ // the protocol compiler.
+ optional bool message_set_wire_format = 1 [default=false];
+
+ // Disables the generation of the standard "descriptor()" accessor, which can
+ // conflict with a field of the same name. This is meant to make migration
+ // from proto1 easier; new code should avoid fields named "descriptor".
+ optional bool no_standard_descriptor_accessor = 2 [default=false];
+
+ // Is this message deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the message, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating messages.
+ optional bool deprecated = 3 [default=false];
+
+ // Whether the message is an automatically generated map entry type for the
+ // maps field.
+ //
+ // For maps fields:
+ // map map_field = 1;
+ // The parsed descriptor looks like:
+ // message MapFieldEntry {
+ // option map_entry = true;
+ // optional KeyType key = 1;
+ // optional ValueType value = 2;
+ // }
+ // repeated MapFieldEntry map_field = 1;
+ //
+ // Implementations may choose not to generate the map_entry=true message, but
+ // use a native map in the target language to hold the keys and values.
+ // The reflection APIs in such implementations still need to work as
+ // if the field is a repeated message field.
+ //
+ // NOTE: Do not set the option in .proto files. Always use the maps syntax
+ // instead. The option should only be implicitly set by the proto compiler
+ // parser.
+ optional bool map_entry = 7;
+
+ reserved 8; // javalite_serializable
+ reserved 9; // javanano_as_lite
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message FieldOptions {
+ // The ctype option instructs the C++ code generator to use a different
+ // representation of the field than it normally would. See the specific
+ // options below. This option is not yet implemented in the open source
+ // release -- sorry, we'll try to include it in a future version!
+ optional CType ctype = 1 [default = STRING];
+ enum CType {
+ // Default mode.
+ STRING = 0;
+
+ CORD = 1;
+
+ STRING_PIECE = 2;
+ }
+ // The packed option can be enabled for repeated primitive fields to enable
+ // a more efficient representation on the wire. Rather than repeatedly
+ // writing the tag and type for each element, the entire array is encoded as
+ // a single length-delimited blob. In proto3, only explicit setting it to
+ // false will avoid using packed encoding.
+ optional bool packed = 2;
+
+ // The jstype option determines the JavaScript type used for values of the
+ // field. The option is permitted only for 64 bit integral and fixed types
+ // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
+ // is represented as JavaScript string, which avoids loss of precision that
+ // can happen when a large value is converted to a floating point JavaScript.
+ // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
+ // use the JavaScript "number" type. The behavior of the default option
+ // JS_NORMAL is implementation dependent.
+ //
+ // This option is an enum to permit additional types to be added, e.g.
+ // goog.math.Integer.
+ optional JSType jstype = 6 [default = JS_NORMAL];
+ enum JSType {
+ // Use the default type.
+ JS_NORMAL = 0;
+
+ // Use JavaScript strings.
+ JS_STRING = 1;
+
+ // Use JavaScript numbers.
+ JS_NUMBER = 2;
+ }
+
+ // Should this field be parsed lazily? Lazy applies only to message-type
+ // fields. It means that when the outer message is initially parsed, the
+ // inner message's contents will not be parsed but instead stored in encoded
+ // form. The inner message will actually be parsed when it is first accessed.
+ //
+ // This is only a hint. Implementations are free to choose whether to use
+ // eager or lazy parsing regardless of the value of this option. However,
+ // setting this option true suggests that the protocol author believes that
+ // using lazy parsing on this field is worth the additional bookkeeping
+ // overhead typically needed to implement it.
+ //
+ // This option does not affect the public interface of any generated code;
+ // all method signatures remain the same. Furthermore, thread-safety of the
+ // interface is not affected by this option; const methods remain safe to
+ // call from multiple threads concurrently, while non-const methods continue
+ // to require exclusive access.
+ //
+ //
+ // Note that implementations may choose not to check required fields within
+ // a lazy sub-message. That is, calling IsInitialized() on the outer message
+ // may return true even if the inner message has missing required fields.
+ // This is necessary because otherwise the inner message would have to be
+ // parsed in order to perform the check, defeating the purpose of lazy
+ // parsing. An implementation which chooses not to check required fields
+ // must be consistent about it. That is, for any particular sub-message, the
+ // implementation must either *always* check its required fields, or *never*
+ // check its required fields, regardless of whether or not the message has
+ // been parsed.
+ optional bool lazy = 5 [default=false];
+
+ // Is this field deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for accessors, or it will be completely ignored; in the very least, this
+ // is a formalization for deprecating fields.
+ optional bool deprecated = 3 [default=false];
+
+ // For Google-internal migration only. Do not use.
+ optional bool weak = 10 [default=false];
+
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+
+ reserved 4; // removed jtype
+}
+
+message OneofOptions {
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message EnumOptions {
+
+ // Set this option to true to allow mapping different tag names to the same
+ // value.
+ optional bool allow_alias = 2;
+
+ // Is this enum deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the enum, or it will be completely ignored; in the very least, this
+ // is a formalization for deprecating enums.
+ optional bool deprecated = 3 [default=false];
+
+ reserved 5; // javanano_as_lite
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message EnumValueOptions {
+ // Is this enum value deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the enum value, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating enum values.
+ optional bool deprecated = 1 [default=false];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message ServiceOptions {
+
+ // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
+ // framework. We apologize for hoarding these numbers to ourselves, but
+ // we were already using them long before we decided to release Protocol
+ // Buffers.
+
+ // Is this service deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the service, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating services.
+ optional bool deprecated = 33 [default=false];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message MethodOptions {
+
+ // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
+ // framework. We apologize for hoarding these numbers to ourselves, but
+ // we were already using them long before we decided to release Protocol
+ // Buffers.
+
+ // Is this method deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the method, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating methods.
+ optional bool deprecated = 33 [default=false];
+
+ // Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
+ // or neither? HTTP based RPC implementation may choose GET verb for safe
+ // methods, and PUT verb for idempotent methods instead of the default POST.
+ enum IdempotencyLevel {
+ IDEMPOTENCY_UNKNOWN = 0;
+ NO_SIDE_EFFECTS = 1; // implies idempotent
+ IDEMPOTENT = 2; // idempotent, but may have side effects
+ }
+ optional IdempotencyLevel idempotency_level =
+ 34 [default=IDEMPOTENCY_UNKNOWN];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+
+// A message representing a option the parser does not recognize. This only
+// appears in options protos created by the compiler::Parser class.
+// DescriptorPool resolves these when building Descriptor objects. Therefore,
+// options protos in descriptor objects (e.g. returned by Descriptor::options(),
+// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
+// in them.
+message UninterpretedOption {
+ // The name of the uninterpreted option. Each string represents a segment in
+ // a dot-separated name. is_extension is true iff a segment represents an
+ // extension (denoted with parentheses in options specs in .proto files).
+ // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
+ // "foo.(bar.baz).qux".
+ message NamePart {
+ required string name_part = 1;
+ required bool is_extension = 2;
+ }
+ repeated NamePart name = 2;
+
+ // The value of the uninterpreted option, in whatever type the tokenizer
+ // identified it as during parsing. Exactly one of these should be set.
+ optional string identifier_value = 3;
+ optional uint64 positive_int_value = 4;
+ optional int64 negative_int_value = 5;
+ optional double double_value = 6;
+ optional bytes string_value = 7;
+ optional string aggregate_value = 8;
+}
+
+// ===================================================================
+// Optional source code info
+
+// Encapsulates information about the original source file from which a
+// FileDescriptorProto was generated.
+message SourceCodeInfo {
+ // A Location identifies a piece of source code in a .proto file which
+ // corresponds to a particular definition. This information is intended
+ // to be useful to IDEs, code indexers, documentation generators, and similar
+ // tools.
+ //
+ // For example, say we have a file like:
+ // message Foo {
+ // optional string foo = 1;
+ // }
+ // Let's look at just the field definition:
+ // optional string foo = 1;
+ // ^ ^^ ^^ ^ ^^^
+ // a bc de f ghi
+ // We have the following locations:
+ // span path represents
+ // [a,i) [ 4, 0, 2, 0 ] The whole field definition.
+ // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
+ // [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
+ // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
+ // [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
+ //
+ // Notes:
+ // - A location may refer to a repeated field itself (i.e. not to any
+ // particular index within it). This is used whenever a set of elements are
+ // logically enclosed in a single code segment. For example, an entire
+ // extend block (possibly containing multiple extension definitions) will
+ // have an outer location whose path refers to the "extensions" repeated
+ // field without an index.
+ // - Multiple locations may have the same path. This happens when a single
+ // logical declaration is spread out across multiple places. The most
+ // obvious example is the "extend" block again -- there may be multiple
+ // extend blocks in the same scope, each of which will have the same path.
+ // - A location's span is not always a subset of its parent's span. For
+ // example, the "extendee" of an extension declaration appears at the
+ // beginning of the "extend" block and is shared by all extensions within
+ // the block.
+ // - Just because a location's span is a subset of some other location's span
+ // does not mean that it is a descendent. For example, a "group" defines
+ // both a type and a field in a single declaration. Thus, the locations
+ // corresponding to the type and field and their components will overlap.
+ // - Code which tries to interpret locations should probably be designed to
+ // ignore those that it doesn't understand, as more types of locations could
+ // be recorded in the future.
+ repeated Location location = 1;
+ message Location {
+ // Identifies which part of the FileDescriptorProto was defined at this
+ // location.
+ //
+ // Each element is a field number or an index. They form a path from
+ // the root FileDescriptorProto to the place where the definition. For
+ // example, this path:
+ // [ 4, 3, 2, 7, 1 ]
+ // refers to:
+ // file.message_type(3) // 4, 3
+ // .field(7) // 2, 7
+ // .name() // 1
+ // This is because FileDescriptorProto.message_type has field number 4:
+ // repeated DescriptorProto message_type = 4;
+ // and DescriptorProto.field has field number 2:
+ // repeated FieldDescriptorProto field = 2;
+ // and FieldDescriptorProto.name has field number 1:
+ // optional string name = 1;
+ //
+ // Thus, the above path gives the location of a field name. If we removed
+ // the last element:
+ // [ 4, 3, 2, 7 ]
+ // this path refers to the whole field declaration (from the beginning
+ // of the label to the terminating semicolon).
+ repeated int32 path = 1 [packed=true];
+
+ // Always has exactly three or four elements: start line, start column,
+ // end line (optional, otherwise assumed same as start line), end column.
+ // These are packed into a single field for efficiency. Note that line
+ // and column numbers are zero-based -- typically you will want to add
+ // 1 to each before displaying to a user.
+ repeated int32 span = 2 [packed=true];
+
+ // If this SourceCodeInfo represents a complete declaration, these are any
+ // comments appearing before and after the declaration which appear to be
+ // attached to the declaration.
+ //
+ // A series of line comments appearing on consecutive lines, with no other
+ // tokens appearing on those lines, will be treated as a single comment.
+ //
+ // leading_detached_comments will keep paragraphs of comments that appear
+ // before (but not connected to) the current element. Each paragraph,
+ // separated by empty lines, will be one comment element in the repeated
+ // field.
+ //
+ // Only the comment content is provided; comment markers (e.g. //) are
+ // stripped out. For block comments, leading whitespace and an asterisk
+ // will be stripped from the beginning of each line other than the first.
+ // Newlines are included in the output.
+ //
+ // Examples:
+ //
+ // optional int32 foo = 1; // Comment attached to foo.
+ // // Comment attached to bar.
+ // optional int32 bar = 2;
+ //
+ // optional string baz = 3;
+ // // Comment attached to baz.
+ // // Another line attached to baz.
+ //
+ // // Comment attached to qux.
+ // //
+ // // Another line attached to qux.
+ // optional double qux = 4;
+ //
+ // // Detached comment for corge. This is not leading or trailing comments
+ // // to qux or corge because there are blank lines separating it from
+ // // both.
+ //
+ // // Detached comment for corge paragraph 2.
+ //
+ // optional string corge = 5;
+ // /* Block comment attached
+ // * to corge. Leading asterisks
+ // * will be removed. */
+ // /* Block comment attached to
+ // * grault. */
+ // optional int32 grault = 6;
+ //
+ // // ignored detached comments.
+ optional string leading_comments = 3;
+ optional string trailing_comments = 4;
+ repeated string leading_detached_comments = 6;
+ }
+}
+
+// Describes the relationship between generated code and its original source
+// file. A GeneratedCodeInfo message is associated with only one generated
+// source file, but may contain references to different source .proto files.
+message GeneratedCodeInfo {
+ // An Annotation connects some span of text in generated code to an element
+ // of its generating .proto file.
+ repeated Annotation annotation = 1;
+ message Annotation {
+ // Identifies the element in the original source .proto file. This field
+ // is formatted the same as SourceCodeInfo.Location.path.
+ repeated int32 path = 1 [packed=true];
+
+ // Identifies the filesystem path to the original source .proto.
+ optional string source_file = 2;
+
+ // Identifies the starting offset in bytes in the generated code
+ // that relates to the identified object.
+ optional int32 begin = 3;
+
+ // Identifies the ending offset in bytes in the generated code that
+ // relates to the identified offset. The end offset should be one past
+ // the last relevant byte (so the length of the text = end - begin).
+ optional int32 end = 4;
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/duration.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/duration.proto
new file mode 100644
index 00000000..975fce41
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/duration.proto
@@ -0,0 +1,117 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "github.com/golang/protobuf/ptypes/duration";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DurationProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// A Duration represents a signed, fixed-length span of time represented
+// as a count of seconds and fractions of seconds at nanosecond
+// resolution. It is independent of any calendar and concepts like "day"
+// or "month". It is related to Timestamp in that the difference between
+// two Timestamp values is a Duration and it can be added or subtracted
+// from a Timestamp. Range is approximately +-10,000 years.
+//
+// # Examples
+//
+// Example 1: Compute Duration from two Timestamps in pseudo code.
+//
+// Timestamp start = ...;
+// Timestamp end = ...;
+// Duration duration = ...;
+//
+// duration.seconds = end.seconds - start.seconds;
+// duration.nanos = end.nanos - start.nanos;
+//
+// if (duration.seconds < 0 && duration.nanos > 0) {
+// duration.seconds += 1;
+// duration.nanos -= 1000000000;
+// } else if (durations.seconds > 0 && duration.nanos < 0) {
+// duration.seconds -= 1;
+// duration.nanos += 1000000000;
+// }
+//
+// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
+//
+// Timestamp start = ...;
+// Duration duration = ...;
+// Timestamp end = ...;
+//
+// end.seconds = start.seconds + duration.seconds;
+// end.nanos = start.nanos + duration.nanos;
+//
+// if (end.nanos < 0) {
+// end.seconds -= 1;
+// end.nanos += 1000000000;
+// } else if (end.nanos >= 1000000000) {
+// end.seconds += 1;
+// end.nanos -= 1000000000;
+// }
+//
+// Example 3: Compute Duration from datetime.timedelta in Python.
+//
+// td = datetime.timedelta(days=3, minutes=10)
+// duration = Duration()
+// duration.FromTimedelta(td)
+//
+// # JSON Mapping
+//
+// In JSON format, the Duration type is encoded as a string rather than an
+// object, where the string ends in the suffix "s" (indicating seconds) and
+// is preceded by the number of seconds, with nanoseconds expressed as
+// fractional seconds. For example, 3 seconds with 0 nanoseconds should be
+// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
+// be expressed in JSON format as "3.000000001s", and 3 seconds and 1
+// microsecond should be expressed in JSON format as "3.000001s".
+//
+//
+message Duration {
+
+ // Signed seconds of the span of time. Must be from -315,576,000,000
+ // to +315,576,000,000 inclusive. Note: these bounds are computed from:
+ // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
+ int64 seconds = 1;
+
+ // Signed fractions of a second at nanosecond resolution of the span
+ // of time. Durations less than one second are represented with a 0
+ // `seconds` field and a positive or negative `nanos` field. For durations
+ // of one second or more, a non-zero value for the `nanos` field must be
+ // of the same sign as the `seconds` field. Must be from -999,999,999
+ // to +999,999,999 inclusive.
+ int32 nanos = 2;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/empty.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/empty.proto
new file mode 100644
index 00000000..03cacd23
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/empty.proto
@@ -0,0 +1,52 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "github.com/golang/protobuf/ptypes/empty";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "EmptyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option cc_enable_arenas = true;
+
+// A generic empty message that you can re-use to avoid defining duplicated
+// empty messages in your APIs. A typical example is to use it as the request
+// or the response type of an API method. For instance:
+//
+// service Foo {
+// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
+// }
+//
+// The JSON representation for `Empty` is empty JSON object `{}`.
+message Empty {}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/field_mask.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/field_mask.proto
new file mode 100644
index 00000000..76e09f39
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/field_mask.proto
@@ -0,0 +1,252 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "FieldMaskProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/genproto/protobuf/field_mask;field_mask";
+
+// `FieldMask` represents a set of symbolic field paths, for example:
+//
+// paths: "f.a"
+// paths: "f.b.d"
+//
+// Here `f` represents a field in some root message, `a` and `b`
+// fields in the message found in `f`, and `d` a field found in the
+// message in `f.b`.
+//
+// Field masks are used to specify a subset of fields that should be
+// returned by a get operation or modified by an update operation.
+// Field masks also have a custom JSON encoding (see below).
+//
+// # Field Masks in Projections
+//
+// When used in the context of a projection, a response message or
+// sub-message is filtered by the API to only contain those fields as
+// specified in the mask. For example, if the mask in the previous
+// example is applied to a response message as follows:
+//
+// f {
+// a : 22
+// b {
+// d : 1
+// x : 2
+// }
+// y : 13
+// }
+// z: 8
+//
+// The result will not contain specific values for fields x,y and z
+// (their value will be set to the default, and omitted in proto text
+// output):
+//
+//
+// f {
+// a : 22
+// b {
+// d : 1
+// }
+// }
+//
+// A repeated field is not allowed except at the last position of a
+// paths string.
+//
+// If a FieldMask object is not present in a get operation, the
+// operation applies to all fields (as if a FieldMask of all fields
+// had been specified).
+//
+// Note that a field mask does not necessarily apply to the
+// top-level response message. In case of a REST get operation, the
+// field mask applies directly to the response, but in case of a REST
+// list operation, the mask instead applies to each individual message
+// in the returned resource list. In case of a REST custom method,
+// other definitions may be used. Where the mask applies will be
+// clearly documented together with its declaration in the API. In
+// any case, the effect on the returned resource/resources is required
+// behavior for APIs.
+//
+// # Field Masks in Update Operations
+//
+// A field mask in update operations specifies which fields of the
+// targeted resource are going to be updated. The API is required
+// to only change the values of the fields as specified in the mask
+// and leave the others untouched. If a resource is passed in to
+// describe the updated values, the API ignores the values of all
+// fields not covered by the mask.
+//
+// If a repeated field is specified for an update operation, the existing
+// repeated values in the target resource will be overwritten by the new values.
+// Note that a repeated field is only allowed in the last position of a `paths`
+// string.
+//
+// If a sub-message is specified in the last position of the field mask for an
+// update operation, then the existing sub-message in the target resource is
+// overwritten. Given the target message:
+//
+// f {
+// b {
+// d : 1
+// x : 2
+// }
+// c : 1
+// }
+//
+// And an update message:
+//
+// f {
+// b {
+// d : 10
+// }
+// }
+//
+// then if the field mask is:
+//
+// paths: "f.b"
+//
+// then the result will be:
+//
+// f {
+// b {
+// d : 10
+// }
+// c : 1
+// }
+//
+// However, if the update mask was:
+//
+// paths: "f.b.d"
+//
+// then the result would be:
+//
+// f {
+// b {
+// d : 10
+// x : 2
+// }
+// c : 1
+// }
+//
+// In order to reset a field's value to the default, the field must
+// be in the mask and set to the default value in the provided resource.
+// Hence, in order to reset all fields of a resource, provide a default
+// instance of the resource and set all fields in the mask, or do
+// not provide a mask as described below.
+//
+// If a field mask is not present on update, the operation applies to
+// all fields (as if a field mask of all fields has been specified).
+// Note that in the presence of schema evolution, this may mean that
+// fields the client does not know and has therefore not filled into
+// the request will be reset to their default. If this is unwanted
+// behavior, a specific service may require a client to always specify
+// a field mask, producing an error if not.
+//
+// As with get operations, the location of the resource which
+// describes the updated values in the request message depends on the
+// operation kind. In any case, the effect of the field mask is
+// required to be honored by the API.
+//
+// ## Considerations for HTTP REST
+//
+// The HTTP kind of an update operation which uses a field mask must
+// be set to PATCH instead of PUT in order to satisfy HTTP semantics
+// (PUT must only be used for full updates).
+//
+// # JSON Encoding of Field Masks
+//
+// In JSON, a field mask is encoded as a single string where paths are
+// separated by a comma. Fields name in each path are converted
+// to/from lower-camel naming conventions.
+//
+// As an example, consider the following message declarations:
+//
+// message Profile {
+// User user = 1;
+// Photo photo = 2;
+// }
+// message User {
+// string display_name = 1;
+// string address = 2;
+// }
+//
+// In proto a field mask for `Profile` may look as such:
+//
+// mask {
+// paths: "user.display_name"
+// paths: "photo"
+// }
+//
+// In JSON, the same mask is represented as below:
+//
+// {
+// mask: "user.displayName,photo"
+// }
+//
+// # Field Masks and Oneof Fields
+//
+// Field masks treat fields in oneofs just as regular fields. Consider the
+// following message:
+//
+// message SampleMessage {
+// oneof test_oneof {
+// string name = 4;
+// SubMessage sub_message = 9;
+// }
+// }
+//
+// The field mask can be:
+//
+// mask {
+// paths: "name"
+// }
+//
+// Or:
+//
+// mask {
+// paths: "sub_message"
+// }
+//
+// Note that oneof type names ("test_oneof" in this case) cannot be used in
+// paths.
+//
+// ## Field Mask Verification
+//
+// The implementation of any API method which has a FieldMask type field in the
+// request should verify the included field paths, and return an
+// `INVALID_ARGUMENT` error if any path is duplicated or unmappable.
+message FieldMask {
+ // The set of field mask paths.
+ repeated string paths = 1;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/source_context.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/source_context.proto
new file mode 100644
index 00000000..f3b2c966
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/source_context.proto
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "SourceContextProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/genproto/protobuf/source_context;source_context";
+
+// `SourceContext` represents information about the source of a
+// protobuf element, like the file in which it is defined.
+message SourceContext {
+ // The path-qualified name of the .proto file that contained the associated
+ // protobuf element. For example: `"google/protobuf/source_context.proto"`.
+ string file_name = 1;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/struct.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/struct.proto
new file mode 100644
index 00000000..7d7808e7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/struct.proto
@@ -0,0 +1,96 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "github.com/golang/protobuf/ptypes/struct;structpb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "StructProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+
+// `Struct` represents a structured data value, consisting of fields
+// which map to dynamically typed values. In some languages, `Struct`
+// might be supported by a native representation. For example, in
+// scripting languages like JS a struct is represented as an
+// object. The details of that representation are described together
+// with the proto support for the language.
+//
+// The JSON representation for `Struct` is JSON object.
+message Struct {
+ // Unordered map of dynamically typed values.
+ map fields = 1;
+}
+
+// `Value` represents a dynamically typed value which can be either
+// null, a number, a string, a boolean, a recursive struct value, or a
+// list of values. A producer of value is expected to set one of that
+// variants, absence of any variant indicates an error.
+//
+// The JSON representation for `Value` is JSON value.
+message Value {
+ // The kind of value.
+ oneof kind {
+ // Represents a null value.
+ NullValue null_value = 1;
+ // Represents a double value.
+ double number_value = 2;
+ // Represents a string value.
+ string string_value = 3;
+ // Represents a boolean value.
+ bool bool_value = 4;
+ // Represents a structured value.
+ Struct struct_value = 5;
+ // Represents a repeated `Value`.
+ ListValue list_value = 6;
+ }
+}
+
+// `NullValue` is a singleton enumeration to represent the null value for the
+// `Value` type union.
+//
+// The JSON representation for `NullValue` is JSON `null`.
+enum NullValue {
+ // Null value.
+ NULL_VALUE = 0;
+}
+
+// `ListValue` is a wrapper around a repeated field of values.
+//
+// The JSON representation for `ListValue` is JSON array.
+message ListValue {
+ // Repeated field of dynamically typed values.
+ repeated Value values = 1;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/timestamp.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/timestamp.proto
new file mode 100644
index 00000000..eafb3fa0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/timestamp.proto
@@ -0,0 +1,135 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "github.com/golang/protobuf/ptypes/timestamp";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "TimestampProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// A Timestamp represents a point in time independent of any time zone
+// or calendar, represented as seconds and fractions of seconds at
+// nanosecond resolution in UTC Epoch time. It is encoded using the
+// Proleptic Gregorian Calendar which extends the Gregorian calendar
+// backwards to year one. It is encoded assuming all minutes are 60
+// seconds long, i.e. leap seconds are "smeared" so that no leap second
+// table is needed for interpretation. Range is from
+// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
+// By restricting to that range, we ensure that we can convert to
+// and from RFC 3339 date strings.
+// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
+//
+// # Examples
+//
+// Example 1: Compute Timestamp from POSIX `time()`.
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(time(NULL));
+// timestamp.set_nanos(0);
+//
+// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+//
+// struct timeval tv;
+// gettimeofday(&tv, NULL);
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(tv.tv_sec);
+// timestamp.set_nanos(tv.tv_usec * 1000);
+//
+// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+//
+// FILETIME ft;
+// GetSystemTimeAsFileTime(&ft);
+// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+//
+// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+// Timestamp timestamp;
+// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+//
+// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+//
+// long millis = System.currentTimeMillis();
+//
+// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+// .setNanos((int) ((millis % 1000) * 1000000)).build();
+//
+//
+// Example 5: Compute Timestamp from current time in Python.
+//
+// timestamp = Timestamp()
+// timestamp.GetCurrentTime()
+//
+// # JSON Mapping
+//
+// In JSON format, the Timestamp type is encoded as a string in the
+// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
+// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
+// where {year} is always expressed using four digits while {month}, {day},
+// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
+// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
+// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
+// is required. A proto3 JSON serializer should always use UTC (as indicated by
+// "Z") when printing the Timestamp type and a proto3 JSON parser should be
+// able to accept both UTC and other timezones (as indicated by an offset).
+//
+// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
+// 01:30 UTC on January 15, 2017.
+//
+// In JavaScript, one can convert a Date object to this format using the
+// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
+// method. In Python, a standard `datetime.datetime` object can be converted
+// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
+// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
+// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
+// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
+// ) to obtain a formatter capable of generating timestamps in this format.
+//
+//
+message Timestamp {
+
+ // Represents seconds of UTC time since Unix epoch
+ // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+ // 9999-12-31T23:59:59Z inclusive.
+ int64 seconds = 1;
+
+ // Non-negative fractions of a second at nanosecond resolution. Negative
+ // second values with fractions must still have non-negative nanos values
+ // that count forward in time. Must be from 0 to 999,999,999
+ // inclusive.
+ int32 nanos = 2;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/type.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/type.proto
new file mode 100644
index 00000000..624c15ee
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/type.proto
@@ -0,0 +1,187 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+import "google/protobuf/any.proto";
+import "google/protobuf/source_context.proto";
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option java_package = "com.google.protobuf";
+option java_outer_classname = "TypeProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/genproto/protobuf/ptype;ptype";
+
+// A protocol buffer message type.
+message Type {
+ // The fully qualified message name.
+ string name = 1;
+ // The list of fields.
+ repeated Field fields = 2;
+ // The list of types appearing in `oneof` definitions in this type.
+ repeated string oneofs = 3;
+ // The protocol buffer options.
+ repeated Option options = 4;
+ // The source context.
+ SourceContext source_context = 5;
+ // The source syntax.
+ Syntax syntax = 6;
+}
+
+// A single field of a message type.
+message Field {
+ // Basic field types.
+ enum Kind {
+ // Field type unknown.
+ TYPE_UNKNOWN = 0;
+ // Field type double.
+ TYPE_DOUBLE = 1;
+ // Field type float.
+ TYPE_FLOAT = 2;
+ // Field type int64.
+ TYPE_INT64 = 3;
+ // Field type uint64.
+ TYPE_UINT64 = 4;
+ // Field type int32.
+ TYPE_INT32 = 5;
+ // Field type fixed64.
+ TYPE_FIXED64 = 6;
+ // Field type fixed32.
+ TYPE_FIXED32 = 7;
+ // Field type bool.
+ TYPE_BOOL = 8;
+ // Field type string.
+ TYPE_STRING = 9;
+ // Field type group. Proto2 syntax only, and deprecated.
+ TYPE_GROUP = 10;
+ // Field type message.
+ TYPE_MESSAGE = 11;
+ // Field type bytes.
+ TYPE_BYTES = 12;
+ // Field type uint32.
+ TYPE_UINT32 = 13;
+ // Field type enum.
+ TYPE_ENUM = 14;
+ // Field type sfixed32.
+ TYPE_SFIXED32 = 15;
+ // Field type sfixed64.
+ TYPE_SFIXED64 = 16;
+ // Field type sint32.
+ TYPE_SINT32 = 17;
+ // Field type sint64.
+ TYPE_SINT64 = 18;
+ };
+
+ // Whether a field is optional, required, or repeated.
+ enum Cardinality {
+ // For fields with unknown cardinality.
+ CARDINALITY_UNKNOWN = 0;
+ // For optional fields.
+ CARDINALITY_OPTIONAL = 1;
+ // For required fields. Proto2 syntax only.
+ CARDINALITY_REQUIRED = 2;
+ // For repeated fields.
+ CARDINALITY_REPEATED = 3;
+ };
+
+ // The field type.
+ Kind kind = 1;
+ // The field cardinality.
+ Cardinality cardinality = 2;
+ // The field number.
+ int32 number = 3;
+ // The field name.
+ string name = 4;
+ // The field type URL, without the scheme, for message or enumeration
+ // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
+ string type_url = 6;
+ // The index of the field type in `Type.oneofs`, for message or enumeration
+ // types. The first type has index 1; zero means the type is not in the list.
+ int32 oneof_index = 7;
+ // Whether to use alternative packed wire representation.
+ bool packed = 8;
+ // The protocol buffer options.
+ repeated Option options = 9;
+ // The field JSON name.
+ string json_name = 10;
+ // The string value of the default value of this field. Proto2 syntax only.
+ string default_value = 11;
+}
+
+// Enum type definition.
+message Enum {
+ // Enum type name.
+ string name = 1;
+ // Enum value definitions.
+ repeated EnumValue enumvalue = 2;
+ // Protocol buffer options.
+ repeated Option options = 3;
+ // The source context.
+ SourceContext source_context = 4;
+ // The source syntax.
+ Syntax syntax = 5;
+}
+
+// Enum value definition.
+message EnumValue {
+ // Enum value name.
+ string name = 1;
+ // Enum value number.
+ int32 number = 2;
+ // Protocol buffer options.
+ repeated Option options = 3;
+}
+
+// A protocol buffer option, which can be attached to a message, field,
+// enumeration, etc.
+message Option {
+ // The option's name. For protobuf built-in options (options defined in
+ // descriptor.proto), this is the short name. For example, `"map_entry"`.
+ // For custom options, it should be the fully-qualified name. For example,
+ // `"google.api.http"`.
+ string name = 1;
+ // The option's value packed in an Any message. If the value is a primitive,
+ // the corresponding wrapper type defined in google/protobuf/wrappers.proto
+ // should be used. If the value is an enum, it should be stored as an int32
+ // value using the google.protobuf.Int32Value type.
+ Any value = 2;
+}
+
+// The syntax in which a protocol buffer element is defined.
+enum Syntax {
+ // Syntax `proto2`.
+ SYNTAX_PROTO2 = 0;
+ // Syntax `proto3`.
+ SYNTAX_PROTO3 = 1;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/wrappers.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/wrappers.proto
new file mode 100644
index 00000000..01947639
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/google/protobuf/wrappers.proto
@@ -0,0 +1,118 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Wrappers for primitive (non-message) types. These types are useful
+// for embedding primitives in the `google.protobuf.Any` type and for places
+// where we need to distinguish between the absence of a primitive
+// typed field and its default value.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "github.com/golang/protobuf/ptypes/wrappers";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "WrappersProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// Wrapper message for `double`.
+//
+// The JSON representation for `DoubleValue` is JSON number.
+message DoubleValue {
+ // The double value.
+ double value = 1;
+}
+
+// Wrapper message for `float`.
+//
+// The JSON representation for `FloatValue` is JSON number.
+message FloatValue {
+ // The float value.
+ float value = 1;
+}
+
+// Wrapper message for `int64`.
+//
+// The JSON representation for `Int64Value` is JSON string.
+message Int64Value {
+ // The int64 value.
+ int64 value = 1;
+}
+
+// Wrapper message for `uint64`.
+//
+// The JSON representation for `UInt64Value` is JSON string.
+message UInt64Value {
+ // The uint64 value.
+ uint64 value = 1;
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+message Int32Value {
+ // The int32 value.
+ int32 value = 1;
+}
+
+// Wrapper message for `uint32`.
+//
+// The JSON representation for `UInt32Value` is JSON number.
+message UInt32Value {
+ // The uint32 value.
+ uint32 value = 1;
+}
+
+// Wrapper message for `bool`.
+//
+// The JSON representation for `BoolValue` is JSON `true` and `false`.
+message BoolValue {
+ // The bool value.
+ bool value = 1;
+}
+
+// Wrapper message for `string`.
+//
+// The JSON representation for `StringValue` is JSON string.
+message StringValue {
+ // The string value.
+ string value = 1;
+}
+
+// Wrapper message for `bytes`.
+//
+// The JSON representation for `BytesValue` is JSON string.
+message BytesValue {
+ // The bytes value.
+ bytes value = 1;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/hello.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/hello.proto
new file mode 100644
index 00000000..273a41fb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/hello.proto
@@ -0,0 +1,21 @@
+// from https://github.com/moul/pb
+// based on https://grpc.io/docs/guides/concepts.html
+
+syntax = "proto2";
+
+package hello;
+
+service HelloService {
+ rpc SayHello(HelloRequest) returns (HelloResponse);
+ rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
+ rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
+ rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
+}
+
+message HelloRequest {
+ optional string greeting = 1;
+}
+
+message HelloResponse {
+ required string reply = 1;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/helloworld.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/helloworld.proto
new file mode 100644
index 00000000..6967b3ab
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/helloworld.proto
@@ -0,0 +1,35 @@
+syntax = "proto3";
+
+package hello;
+
+service HelloService {
+ rpc SayHello(HelloRequest) returns (HelloResponse) {
+ option (google.api.http) = {
+ // https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
+ // HTTP | gRPC
+ // -----|-----
+ // `GET /v1/messages/123456` | `HelloRequest(greeting: "123456")`
+ get: "/v1/messages/{greeting}"
+ additional_bindings {
+ get: "/v1/messages/legacy/{greeting=**}"
+ }
+ post: "/v1/messages/"
+ body: "*"
+ }
+ };
+
+ // define a gRPC method that's not implemented in grpcbin
+ rpc UnknownMethod(HelloRequest) returns (HelloResponse) {
+ option (google.api.http) = {
+ get: "/v1/unknown/{greeting}"
+ }
+ };
+}
+
+message HelloRequest {
+ required string greeting = 1;
+}
+
+message HelloResponse {
+ required string reply = 1;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/second_level_imports.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/second_level_imports.proto
new file mode 100644
index 00000000..7edce70a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/second_level_imports.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+
+import "direct_imports.proto";
+
+service Added {
+ rpc Final(hello.HelloRequest) returns (hello.HelloResponse);
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/go.mod b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/go.mod
new file mode 100644
index 00000000..24a43b89
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/go.mod
@@ -0,0 +1,10 @@
+module target
+
+go 1.15
+
+require (
+ github.com/golang/protobuf v1.5.2
+ google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea
+ google.golang.org/grpc v1.39.0
+ google.golang.org/protobuf v1.27.1
+)
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/go.sum b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/go.sum
new file mode 100644
index 00000000..780a5f3f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/go.sum
@@ -0,0 +1,170 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs=
+golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea h1:8ZyCcgugUqamxp/vZSEJw9CMy7VZlSWYJLLJPi/dSDA=
+google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/grpc-target.go b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/grpc-target.go
new file mode 100644
index 00000000..de15cd4c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/grpc-target.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "net"
+ "time"
+
+ pb "target/targetservice"
+
+ "google.golang.org/grpc"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+const (
+ port = ":15010"
+)
+
+type server struct {
+ pb.UnimplementedBouncerServer
+}
+
+func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
+ return &pb.HelloResponse{
+ Reply: fmt.Sprintf("hello %s", in.GetGreeting()),
+ BooleanTest: in.GetBooleanTest(),
+ }, nil
+}
+
+func (s *server) BounceIt(ctx context.Context, in *pb.BallIn) (*pb.BallOut, error) {
+ w := in.GetWhen().AsTime()
+ now := in.GetNow().AsTime()
+ ago := now.Sub(w)
+
+ reply := fmt.Sprintf("hello %s", in.GetMessage())
+ time_message := fmt.Sprintf("%s was %v ago", w.Format(time.RFC3339), ago.Truncate(time.Second))
+
+ return &pb.BallOut{
+ Reply: reply,
+ TimeMessage: time_message,
+ Now: timestamppb.New(now),
+ }, nil
+}
+
+func (s *server) GrowTail(ctx context.Context, in *pb.Body) (*pb.Body, error) {
+ in.Tail.Count += 1
+
+ return in, nil
+}
+
+func (s *server) Echo(ctx context.Context, in *pb.EchoMsg) (*pb.EchoMsg, error) {
+ return in, nil
+}
+
+func main() {
+ lis, err := net.Listen("tcp", port)
+ if err != nil {
+ log.Fatalf("failed to listen: %v", err)
+ }
+ s := grpc.NewServer()
+ pb.RegisterBouncerServer(s, &server{})
+ log.Printf("server listening at %v", lis.Addr())
+ if err := s.Serve(lis); err != nil {
+ log.Fatalf("failed to serve: %v", err)
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/targetservice/targetservice.pb.go b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/targetservice/targetservice.pb.go
new file mode 100644
index 00000000..43cf2421
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/targetservice/targetservice.pb.go
@@ -0,0 +1,685 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.28.1
+// protoc v3.21.7
+// source: targetservice.proto
+
+package targetservice
+
+import (
+ timestamp "github.com/golang/protobuf/ptypes/timestamp"
+ _ "google.golang.org/genproto/googleapis/api/annotations"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type HelloRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Greeting string `protobuf:"bytes,1,opt,name=greeting,proto3" json:"greeting,omitempty"`
+ BooleanTest bool `protobuf:"varint,2,opt,name=boolean_test,json=booleanTest,proto3" json:"boolean_test,omitempty"`
+}
+
+func (x *HelloRequest) Reset() {
+ *x = HelloRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_targetservice_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *HelloRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HelloRequest) ProtoMessage() {}
+
+func (x *HelloRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_targetservice_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
+func (*HelloRequest) Descriptor() ([]byte, []int) {
+ return file_targetservice_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *HelloRequest) GetGreeting() string {
+ if x != nil {
+ return x.Greeting
+ }
+ return ""
+}
+
+func (x *HelloRequest) GetBooleanTest() bool {
+ if x != nil {
+ return x.BooleanTest
+ }
+ return false
+}
+
+type HelloResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Reply string `protobuf:"bytes,1,opt,name=reply,proto3" json:"reply,omitempty"`
+ BooleanTest bool `protobuf:"varint,2,opt,name=boolean_test,json=booleanTest,proto3" json:"boolean_test,omitempty"`
+}
+
+func (x *HelloResponse) Reset() {
+ *x = HelloResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_targetservice_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *HelloResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HelloResponse) ProtoMessage() {}
+
+func (x *HelloResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_targetservice_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead.
+func (*HelloResponse) Descriptor() ([]byte, []int) {
+ return file_targetservice_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *HelloResponse) GetReply() string {
+ if x != nil {
+ return x.Reply
+ }
+ return ""
+}
+
+func (x *HelloResponse) GetBooleanTest() bool {
+ if x != nil {
+ return x.BooleanTest
+ }
+ return false
+}
+
+type BallIn struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+ When *timestamp.Timestamp `protobuf:"bytes,2,opt,name=when,proto3" json:"when,omitempty"`
+ Now *timestamp.Timestamp `protobuf:"bytes,3,opt,name=now,proto3" json:"now,omitempty"`
+}
+
+func (x *BallIn) Reset() {
+ *x = BallIn{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_targetservice_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BallIn) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BallIn) ProtoMessage() {}
+
+func (x *BallIn) ProtoReflect() protoreflect.Message {
+ mi := &file_targetservice_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BallIn.ProtoReflect.Descriptor instead.
+func (*BallIn) Descriptor() ([]byte, []int) {
+ return file_targetservice_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *BallIn) GetMessage() string {
+ if x != nil {
+ return x.Message
+ }
+ return ""
+}
+
+func (x *BallIn) GetWhen() *timestamp.Timestamp {
+ if x != nil {
+ return x.When
+ }
+ return nil
+}
+
+func (x *BallIn) GetNow() *timestamp.Timestamp {
+ if x != nil {
+ return x.Now
+ }
+ return nil
+}
+
+type BallOut struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Reply string `protobuf:"bytes,1,opt,name=reply,proto3" json:"reply,omitempty"`
+ TimeMessage string `protobuf:"bytes,2,opt,name=time_message,json=timeMessage,proto3" json:"time_message,omitempty"`
+ Now *timestamp.Timestamp `protobuf:"bytes,3,opt,name=now,proto3" json:"now,omitempty"`
+}
+
+func (x *BallOut) Reset() {
+ *x = BallOut{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_targetservice_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BallOut) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BallOut) ProtoMessage() {}
+
+func (x *BallOut) ProtoReflect() protoreflect.Message {
+ mi := &file_targetservice_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BallOut.ProtoReflect.Descriptor instead.
+func (*BallOut) Descriptor() ([]byte, []int) {
+ return file_targetservice_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *BallOut) GetReply() string {
+ if x != nil {
+ return x.Reply
+ }
+ return ""
+}
+
+func (x *BallOut) GetTimeMessage() string {
+ if x != nil {
+ return x.TimeMessage
+ }
+ return ""
+}
+
+func (x *BallOut) GetNow() *timestamp.Timestamp {
+ if x != nil {
+ return x.Now
+ }
+ return nil
+}
+
+type Limb struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Count int32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
+ Endings string `protobuf:"bytes,2,opt,name=endings,proto3" json:"endings,omitempty"`
+}
+
+func (x *Limb) Reset() {
+ *x = Limb{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_targetservice_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Limb) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Limb) ProtoMessage() {}
+
+func (x *Limb) ProtoReflect() protoreflect.Message {
+ mi := &file_targetservice_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Limb.ProtoReflect.Descriptor instead.
+func (*Limb) Descriptor() ([]byte, []int) {
+ return file_targetservice_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *Limb) GetCount() int32 {
+ if x != nil {
+ return x.Count
+ }
+ return 0
+}
+
+func (x *Limb) GetEndings() string {
+ if x != nil {
+ return x.Endings
+ }
+ return ""
+}
+
+type Body struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Hands *Limb `protobuf:"bytes,2,opt,name=hands,proto3" json:"hands,omitempty"`
+ Legs *Limb `protobuf:"bytes,3,opt,name=legs,proto3" json:"legs,omitempty"`
+ Tail *Limb `protobuf:"bytes,4,opt,name=tail,proto3" json:"tail,omitempty"`
+}
+
+func (x *Body) Reset() {
+ *x = Body{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_targetservice_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Body) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Body) ProtoMessage() {}
+
+func (x *Body) ProtoReflect() protoreflect.Message {
+ mi := &file_targetservice_proto_msgTypes[5]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Body.ProtoReflect.Descriptor instead.
+func (*Body) Descriptor() ([]byte, []int) {
+ return file_targetservice_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *Body) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Body) GetHands() *Limb {
+ if x != nil {
+ return x.Hands
+ }
+ return nil
+}
+
+func (x *Body) GetLegs() *Limb {
+ if x != nil {
+ return x.Legs
+ }
+ return nil
+}
+
+func (x *Body) GetTail() *Limb {
+ if x != nil {
+ return x.Tail
+ }
+ return nil
+}
+
+type EchoMsg struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Array []string `protobuf:"bytes,1,rep,name=array,proto3" json:"array,omitempty"`
+ Nullable string `protobuf:"bytes,2,opt,name=nullable,proto3" json:"nullable,omitempty"`
+}
+
+func (x *EchoMsg) Reset() {
+ *x = EchoMsg{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_targetservice_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *EchoMsg) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*EchoMsg) ProtoMessage() {}
+
+func (x *EchoMsg) ProtoReflect() protoreflect.Message {
+ mi := &file_targetservice_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use EchoMsg.ProtoReflect.Descriptor instead.
+func (*EchoMsg) Descriptor() ([]byte, []int) {
+ return file_targetservice_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *EchoMsg) GetArray() []string {
+ if x != nil {
+ return x.Array
+ }
+ return nil
+}
+
+func (x *EchoMsg) GetNullable() string {
+ if x != nil {
+ return x.Nullable
+ }
+ return ""
+}
+
+var File_targetservice_proto protoreflect.FileDescriptor
+
+var file_targetservice_proto_rawDesc = []byte{
+ 0x0a, 0x13, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65, 0x72,
+ 0x76, 0x69, 0x63, 0x65, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69,
+ 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x22, 0x4d, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12,
+ 0x21, 0x0a, 0x0c, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x54, 0x65,
+ 0x73, 0x74, 0x22, 0x48, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6f, 0x6f,
+ 0x6c, 0x65, 0x61, 0x6e, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x0b, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x54, 0x65, 0x73, 0x74, 0x22, 0x80, 0x01, 0x0a,
+ 0x06, 0x42, 0x61, 0x6c, 0x6c, 0x49, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+ 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x77, 0x68, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+ 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x77, 0x68, 0x65,
+ 0x6e, 0x12, 0x2c, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x6e, 0x6f, 0x77, 0x22,
+ 0x70, 0x0a, 0x07, 0x42, 0x61, 0x6c, 0x6c, 0x4f, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65,
+ 0x70, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79,
+ 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x6e, 0x6f,
+ 0x77, 0x22, 0x36, 0x0a, 0x04, 0x4c, 0x69, 0x6d, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12,
+ 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x07, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x04, 0x42, 0x6f,
+ 0x64, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65,
+ 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x6d, 0x62, 0x52, 0x05, 0x68, 0x61, 0x6e, 0x64,
+ 0x73, 0x12, 0x27, 0x0a, 0x04, 0x6c, 0x65, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x13, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+ 0x4c, 0x69, 0x6d, 0x62, 0x52, 0x04, 0x6c, 0x65, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x04, 0x74, 0x61,
+ 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65,
+ 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x6d, 0x62, 0x52, 0x04, 0x74,
+ 0x61, 0x69, 0x6c, 0x22, 0x3b, 0x0a, 0x07, 0x45, 0x63, 0x68, 0x6f, 0x4d, 0x73, 0x67, 0x12, 0x14,
+ 0x0a, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61,
+ 0x72, 0x72, 0x61, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65,
+ 0x32, 0x80, 0x04, 0x0a, 0x07, 0x42, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x72, 0x12, 0x9f, 0x01, 0x0a,
+ 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x1b, 0x2e, 0x74, 0x61, 0x72, 0x67,
+ 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x52, 0x12, 0x17, 0x2f, 0x76,
+ 0x31, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x65, 0x65,
+ 0x74, 0x69, 0x6e, 0x67, 0x7d, 0x3a, 0x01, 0x2a, 0x5a, 0x34, 0x12, 0x21, 0x2f, 0x76, 0x31, 0x2f,
+ 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x2f,
+ 0x7b, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x3d, 0x2a, 0x2a, 0x7d, 0x5a, 0x0f, 0x22,
+ 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x12, 0x6a,
+ 0x0a, 0x0d, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12,
+ 0x1b, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74,
+ 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x6c,
+ 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93,
+ 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x2f,
+ 0x7b, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x7d, 0x12, 0x4d, 0x0a, 0x08, 0x42, 0x6f,
+ 0x75, 0x6e, 0x63, 0x65, 0x49, 0x74, 0x12, 0x15, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x6c, 0x6c, 0x49, 0x6e, 0x1a, 0x16, 0x2e,
+ 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61,
+ 0x6c, 0x6c, 0x4f, 0x75, 0x74, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x22, 0x07, 0x2f,
+ 0x62, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x72, 0x6f,
+ 0x77, 0x54, 0x61, 0x69, 0x6c, 0x12, 0x13, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65,
+ 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x1a, 0x13, 0x2e, 0x74, 0x61, 0x72,
+ 0x67, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x22,
+ 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f,
+ 0x77, 0x2f, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x4b, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x16,
+ 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x45,
+ 0x63, 0x68, 0x6f, 0x4d, 0x73, 0x67, 0x1a, 0x16, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x4d, 0x73, 0x67, 0x22, 0x13,
+ 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x22, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x63, 0x68, 0x6f,
+ 0x3a, 0x01, 0x2a, 0x42, 0x11, 0x5a, 0x0f, 0x2e, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_targetservice_proto_rawDescOnce sync.Once
+ file_targetservice_proto_rawDescData = file_targetservice_proto_rawDesc
+)
+
+func file_targetservice_proto_rawDescGZIP() []byte {
+ file_targetservice_proto_rawDescOnce.Do(func() {
+ file_targetservice_proto_rawDescData = protoimpl.X.CompressGZIP(file_targetservice_proto_rawDescData)
+ })
+ return file_targetservice_proto_rawDescData
+}
+
+var file_targetservice_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_targetservice_proto_goTypes = []interface{}{
+ (*HelloRequest)(nil), // 0: targetservice.HelloRequest
+ (*HelloResponse)(nil), // 1: targetservice.HelloResponse
+ (*BallIn)(nil), // 2: targetservice.BallIn
+ (*BallOut)(nil), // 3: targetservice.BallOut
+ (*Limb)(nil), // 4: targetservice.Limb
+ (*Body)(nil), // 5: targetservice.Body
+ (*EchoMsg)(nil), // 6: targetservice.EchoMsg
+ (*timestamp.Timestamp)(nil), // 7: google.protobuf.Timestamp
+}
+var file_targetservice_proto_depIdxs = []int32{
+ 7, // 0: targetservice.BallIn.when:type_name -> google.protobuf.Timestamp
+ 7, // 1: targetservice.BallIn.now:type_name -> google.protobuf.Timestamp
+ 7, // 2: targetservice.BallOut.now:type_name -> google.protobuf.Timestamp
+ 4, // 3: targetservice.Body.hands:type_name -> targetservice.Limb
+ 4, // 4: targetservice.Body.legs:type_name -> targetservice.Limb
+ 4, // 5: targetservice.Body.tail:type_name -> targetservice.Limb
+ 0, // 6: targetservice.Bouncer.SayHello:input_type -> targetservice.HelloRequest
+ 0, // 7: targetservice.Bouncer.UnknownMethod:input_type -> targetservice.HelloRequest
+ 2, // 8: targetservice.Bouncer.BounceIt:input_type -> targetservice.BallIn
+ 5, // 9: targetservice.Bouncer.GrowTail:input_type -> targetservice.Body
+ 6, // 10: targetservice.Bouncer.Echo:input_type -> targetservice.EchoMsg
+ 1, // 11: targetservice.Bouncer.SayHello:output_type -> targetservice.HelloResponse
+ 1, // 12: targetservice.Bouncer.UnknownMethod:output_type -> targetservice.HelloResponse
+ 3, // 13: targetservice.Bouncer.BounceIt:output_type -> targetservice.BallOut
+ 5, // 14: targetservice.Bouncer.GrowTail:output_type -> targetservice.Body
+ 6, // 15: targetservice.Bouncer.Echo:output_type -> targetservice.EchoMsg
+ 11, // [11:16] is the sub-list for method output_type
+ 6, // [6:11] is the sub-list for method input_type
+ 6, // [6:6] is the sub-list for extension type_name
+ 6, // [6:6] is the sub-list for extension extendee
+ 0, // [0:6] is the sub-list for field type_name
+}
+
+func init() { file_targetservice_proto_init() }
+func file_targetservice_proto_init() {
+ if File_targetservice_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_targetservice_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*HelloRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_targetservice_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*HelloResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_targetservice_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BallIn); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_targetservice_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BallOut); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_targetservice_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Limb); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_targetservice_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Body); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_targetservice_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*EchoMsg); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_targetservice_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 7,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_targetservice_proto_goTypes,
+ DependencyIndexes: file_targetservice_proto_depIdxs,
+ MessageInfos: file_targetservice_proto_msgTypes,
+ }.Build()
+ File_targetservice_proto = out.File
+ file_targetservice_proto_rawDesc = nil
+ file_targetservice_proto_goTypes = nil
+ file_targetservice_proto_depIdxs = nil
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/targetservice/targetservice_grpc.pb.go b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/targetservice/targetservice_grpc.pb.go
new file mode 100644
index 00000000..e59433d7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/target/targetservice/targetservice_grpc.pb.go
@@ -0,0 +1,251 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc v3.21.7
+// source: targetservice.proto
+
+package targetservice
+
+import (
+ context "context"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// BouncerClient is the client API for Bouncer service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type BouncerClient interface {
+ SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error)
+ // define a gRPC method that's not implemented in the target
+ UnknownMethod(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error)
+ BounceIt(ctx context.Context, in *BallIn, opts ...grpc.CallOption) (*BallOut, error)
+ GrowTail(ctx context.Context, in *Body, opts ...grpc.CallOption) (*Body, error)
+ Echo(ctx context.Context, in *EchoMsg, opts ...grpc.CallOption) (*EchoMsg, error)
+}
+
+type bouncerClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewBouncerClient(cc grpc.ClientConnInterface) BouncerClient {
+ return &bouncerClient{cc}
+}
+
+func (c *bouncerClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) {
+ out := new(HelloResponse)
+ err := c.cc.Invoke(ctx, "/targetservice.Bouncer/SayHello", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *bouncerClient) UnknownMethod(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) {
+ out := new(HelloResponse)
+ err := c.cc.Invoke(ctx, "/targetservice.Bouncer/UnknownMethod", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *bouncerClient) BounceIt(ctx context.Context, in *BallIn, opts ...grpc.CallOption) (*BallOut, error) {
+ out := new(BallOut)
+ err := c.cc.Invoke(ctx, "/targetservice.Bouncer/BounceIt", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *bouncerClient) GrowTail(ctx context.Context, in *Body, opts ...grpc.CallOption) (*Body, error) {
+ out := new(Body)
+ err := c.cc.Invoke(ctx, "/targetservice.Bouncer/GrowTail", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *bouncerClient) Echo(ctx context.Context, in *EchoMsg, opts ...grpc.CallOption) (*EchoMsg, error) {
+ out := new(EchoMsg)
+ err := c.cc.Invoke(ctx, "/targetservice.Bouncer/Echo", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// BouncerServer is the server API for Bouncer service.
+// All implementations must embed UnimplementedBouncerServer
+// for forward compatibility
+type BouncerServer interface {
+ SayHello(context.Context, *HelloRequest) (*HelloResponse, error)
+ // define a gRPC method that's not implemented in the target
+ UnknownMethod(context.Context, *HelloRequest) (*HelloResponse, error)
+ BounceIt(context.Context, *BallIn) (*BallOut, error)
+ GrowTail(context.Context, *Body) (*Body, error)
+ Echo(context.Context, *EchoMsg) (*EchoMsg, error)
+ mustEmbedUnimplementedBouncerServer()
+}
+
+// UnimplementedBouncerServer must be embedded to have forward compatible implementations.
+type UnimplementedBouncerServer struct {
+}
+
+func (UnimplementedBouncerServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
+}
+func (UnimplementedBouncerServer) UnknownMethod(context.Context, *HelloRequest) (*HelloResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method UnknownMethod not implemented")
+}
+func (UnimplementedBouncerServer) BounceIt(context.Context, *BallIn) (*BallOut, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method BounceIt not implemented")
+}
+func (UnimplementedBouncerServer) GrowTail(context.Context, *Body) (*Body, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GrowTail not implemented")
+}
+func (UnimplementedBouncerServer) Echo(context.Context, *EchoMsg) (*EchoMsg, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented")
+}
+func (UnimplementedBouncerServer) mustEmbedUnimplementedBouncerServer() {}
+
+// UnsafeBouncerServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to BouncerServer will
+// result in compilation errors.
+type UnsafeBouncerServer interface {
+ mustEmbedUnimplementedBouncerServer()
+}
+
+func RegisterBouncerServer(s grpc.ServiceRegistrar, srv BouncerServer) {
+ s.RegisterService(&Bouncer_ServiceDesc, srv)
+}
+
+func _Bouncer_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(HelloRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BouncerServer).SayHello(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/targetservice.Bouncer/SayHello",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BouncerServer).SayHello(ctx, req.(*HelloRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Bouncer_UnknownMethod_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(HelloRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BouncerServer).UnknownMethod(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/targetservice.Bouncer/UnknownMethod",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BouncerServer).UnknownMethod(ctx, req.(*HelloRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Bouncer_BounceIt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(BallIn)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BouncerServer).BounceIt(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/targetservice.Bouncer/BounceIt",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BouncerServer).BounceIt(ctx, req.(*BallIn))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Bouncer_GrowTail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Body)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BouncerServer).GrowTail(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/targetservice.Bouncer/GrowTail",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BouncerServer).GrowTail(ctx, req.(*Body))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Bouncer_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(EchoMsg)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BouncerServer).Echo(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/targetservice.Bouncer/Echo",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BouncerServer).Echo(ctx, req.(*EchoMsg))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// Bouncer_ServiceDesc is the grpc.ServiceDesc for Bouncer service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Bouncer_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "targetservice.Bouncer",
+ HandlerType: (*BouncerServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "SayHello",
+ Handler: _Bouncer_SayHello_Handler,
+ },
+ {
+ MethodName: "UnknownMethod",
+ Handler: _Bouncer_UnknownMethod_Handler,
+ },
+ {
+ MethodName: "BounceIt",
+ Handler: _Bouncer_BounceIt_Handler,
+ },
+ {
+ MethodName: "GrowTail",
+ Handler: _Bouncer_GrowTail_Handler,
+ },
+ {
+ MethodName: "Echo",
+ Handler: _Bouncer_Echo_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "targetservice.proto",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/targetservice.proto b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/targetservice.proto
new file mode 100644
index 00000000..e2f887fe
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/grpc/targetservice.proto
@@ -0,0 +1,95 @@
+syntax = "proto3";
+
+package targetservice;
+
+import "google/api/annotations.proto";
+import "google/protobuf/timestamp.proto";
+
+option go_package = "./targetservice";
+
+service Bouncer {
+ rpc SayHello(HelloRequest) returns (HelloResponse) {
+ option (google.api.http) = {
+ // https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
+ // HTTP | gRPC
+ // -----|-----
+ // `GET /v1/messages/123456` | `HelloRequest(greeting: "123456")`
+ get: "/v1/messages/{greeting}"
+ additional_bindings {
+ get: "/v1/messages/legacy/{greeting=**}"
+ additional_bindings {
+ post: "/v1/messages/"
+ }
+ }
+ body: "*"
+ };
+ };
+
+ // define a gRPC method that's not implemented in the target
+ rpc UnknownMethod(HelloRequest) returns (HelloResponse) {
+ option (google.api.http) = {
+ get: "/v1/unknown/{greeting}"
+ };
+ };
+
+ rpc BounceIt (BallIn) returns (BallOut) {
+ option (google.api.http) = {
+ post: "/bounce"
+ body: "*"
+ };
+ }
+
+ rpc GrowTail(Body) returns (Body) {
+ option (google.api.http) = {
+ get: "/v1/grow/tail"
+ };
+ }
+
+ rpc Echo(EchoMsg) returns (EchoMsg) {
+ option (google.api.http) = {
+ post: "/v1/echo"
+ body: "*"
+ };
+ }
+}
+
+
+message HelloRequest {
+ string greeting = 1;
+ bool boolean_test = 2;
+}
+
+message HelloResponse {
+ string reply = 1;
+ bool boolean_test = 2;
+}
+
+
+message BallIn {
+ string message = 1;
+ google.protobuf.Timestamp when = 2;
+ google.protobuf.Timestamp now = 3;
+}
+
+message BallOut {
+ string reply = 1;
+ string time_message = 2;
+ google.protobuf.Timestamp now = 3;
+}
+
+message Limb {
+ int32 count = 1;
+ string endings = 2;
+}
+
+message Body {
+ string name = 1;
+ Limb hands = 2;
+ Limb legs = 3;
+ Limb tail = 4;
+}
+
+message EchoMsg {
+ repeated string array = 1;
+ string nullable = 2;
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/headers.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/headers.conf
new file mode 100644
index 00000000..36df085d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/headers.conf
@@ -0,0 +1,28 @@
+# 1st digit is 9 for our test instances
+admin_listen = 127.0.0.1:9001
+proxy_listen = 0.0.0.0:9000, 0.0.0.0:9443 ssl
+
+ssl_cert = spec/fixtures/kong_spec.crt
+ssl_cert_key = spec/fixtures/kong_spec.key
+
+admin_ssl_cert = spec/fixtures/kong_spec.crt
+admin_ssl_cert_key = spec/fixtures/kong_spec.key
+
+database = postgres
+pg_host = 127.0.0.1
+pg_port = 5432
+pg_timeout = 10000
+pg_database = kong_tests
+anonymous_reports = off
+
+dns_hostsfile = spec/fixtures/hosts
+
+nginx_main_worker_processes = 1
+nginx_main_worker_rlimit_nofile = 4096
+nginx_events_worker_connections = 4096
+nginx_events_multi_accept = off
+
+prefix = servroot
+log_level = debug
+
+headers = server_tokens, X-Kong-Proxy-Latency
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/hosts b/kong-versions/test9.9.9.3/kong/spec/fixtures/hosts
new file mode 100644
index 00000000..75721cd5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/hosts
@@ -0,0 +1 @@
+127.0.0.1 localhost
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/https_server.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/https_server.lua
new file mode 100644
index 00000000..9ad2c61b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/https_server.lua
@@ -0,0 +1,289 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+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
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid-module.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid-module.lua
new file mode 100644
index 00000000..e40e5929
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid-module.lua
@@ -0,0 +1,12 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- Invalid module (syntax error) for utils.load_module_if_exists unit tests.
+-- Assert that load_module_if_exists throws an error helps for development, where one could
+-- be confused as to the reason why his or her plugin doesn't load. (not implemented or has an error)
+
+local a = "hello",
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid.conf
new file mode 100644
index 00000000..fbbfedcc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid.conf
@@ -0,0 +1,3 @@
+pg_ssl = on
+database = postgres
+untrusted_lua = foobar
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid_nginx_directives.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid_nginx_directives.conf
new file mode 100644
index 00000000..46738cc2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/invalid_nginx_directives.conf
@@ -0,0 +1 @@
+nginx_http_random_directive = value
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/jwks.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/jwks.lua
new file mode 100644
index 00000000..24712e93
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/jwks.lua
@@ -0,0 +1,69 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local client_jwk = {
+ p =
+ "2OZf9JxDwXpXLokSpjFq81XZfrG1_cSqzi-ZMzbiqpVKdBYhZ77SfV2xsgK7kAxo_4Jh0Bh3MFt9NvYll8pE6VBDVPRLmAIcv7-UMkSVbUnRdltT5ElROViTy57iJO3BlzOiFHENhA_DnQrDRVtLuPFyMZVYHXuWWTfJveXh2xU",
+ kty = "RSA",
+ q =
+ "sC8S3S-rKb2GFzfmSNDw5thy-2sLWMKPPWloN5jIWqHB2OumYzzUR_2TLkrf4SBQ-69qbfamoce8El_ipRnBbJRzd-qO8kjgRgK9_v_trxmcDiqb__Mivyu2J0a2-13VyS12tFAM9DVfewcR10V_bNsxV15WaMNmqslpHuHyPkk",
+ d =
+ "QRes9v86K0SMfBLLalyDMR5BUdI8uF_e6vmYCU9i7kYkGMGdwEVy9ZJ5Zz8EptbUuUpH3lCnstEyCZJ92CFieGAiw95S9BQNqKYaQJi2vnWopPHExDmNy6p2opSzaskBh0UlgVfQ_9bCHaXC-L-63lg1vqm3Lb2vGhqfKCJ_n1O8Y078l2Bp1zf8t3kAT7yix7qvO7HO_8JcWXecMpnZp2BYvUTUC5x58UGz30JzIuhsZqEGjiYInSJ5NyUuiC5sYrSW87ClsmrsM3QtieEekceqdCPimIcNyL6hetQ_sEeAVNsX7Pr1Ptf68cLmFf5SygLsqPZn4QUzOhmX5IHPQQ",
+ e = "AQAB",
+ use = "sig",
+ kid = "test",
+ qi =
+ "jZMzMywer5cauHZSdSa0bUBh1pm3Xs3-hGYz87He9at8rgwJOrbExn3gnt7j0YOqgbto3hRa5c15dW5zijElhLxlOfYm3Mx8iqjx6wZwCzgh0_gcGg0K2z9NyFo5jr9cx4ADFPQbeBg1nJuSDCSM4dKZDRIoea67ZOlD0nVX31U",
+ dp =
+ "1WUidujfCJWy_IfDn1rzXGDUUP_Ki9dYLO9LjMWtECM9UyUKzKVDNjBeSULDAiATlX7TeQb5DMyWTuKxbbuFPP7In-aSEQXS_xwfU62CTjnYKNgFm44-qWrqTeUNnUOFuZsFFZN8trdDv1f9clk2EMkLjpjeTnNvImvuwjRPl1k",
+ alg = "RS512",
+ dq =
+ "V2pz3KUbMvGjR3_n1bc93_wDSPW6woZ-VcUn_XXj6vwZV0zM2K5E0ZdiYHSsEmJbbBWEJB1eXiThDujeZnaZ_wrFI_mQhWxdQr93j79iKh_1yx2fG2aClgHVNjWi8qODQHc7T4nsc3rL7hn64ynLaLJ1D9lx3DcfiJu4eac372k",
+ n =
+ "lUZEP2Nan3nFork1tElu5V8-pwsvMibfAau3ADpM6OqBF4n08eSeHeWC2uZltQkLKaU_n-dm0INUv72QDJsaZ-ZNCc9LST_HNoQQ5qPH86RDPmMEus_OdKJ7V57gxJzWonwzJ3sDdGW1QjK1Hb8kHnwM46uPoHHF_Iottc6oUXIiXQ18hTFFsKtpeSUl6PsA76j3pvxOLPFhJPtgZ2p9WhwDmiav6oK4XXgyhxolkF7kBVNSIqZrGI4_KAuxVpZiX3t-4pzHV4Rh6_YBmA9OgdVJ3WfKjGiPpz8YA4PmhXPND2nFXm4_GUWm3_QIhD1Cyk6jO8RRUUEZI59Xz1CO_Q"
+}
+
+local client_jwk_public = {
+ kty = "RSA",
+ e = "AQAB",
+ use = "sig",
+ kid = "test",
+ alg = "RS512",
+ n =
+ "lUZEP2Nan3nFork1tElu5V8-pwsvMibfAau3ADpM6OqBF4n08eSeHeWC2uZltQkLKaU_n-dm0INUv72QDJsaZ-ZNCc9LST_HNoQQ5qPH86RDPmMEus_OdKJ7V57gxJzWonwzJ3sDdGW1QjK1Hb8kHnwM46uPoHHF_Iottc6oUXIiXQ18hTFFsKtpeSUl6PsA76j3pvxOLPFhJPtgZ2p9WhwDmiav6oK4XXgyhxolkF7kBVNSIqZrGI4_KAuxVpZiX3t-4pzHV4Rh6_YBmA9OgdVJ3WfKjGiPpz8YA4PmhXPND2nFXm4_GUWm3_QIhD1Cyk6jO8RRUUEZI59Xz1CO_Q"
+}
+
+local client2_jwk = {
+ p = "0uCA8VaL5xxR8G2eFCo1ZfigXEDWS8IjWhB0jrIFsruRSEOo5ZvPq6AK23pI3-QwG05IZTyPegsJL_VXKiPzBXiPxCn1C_nlJY0EjYAdzPhQVub3Jv820cPIXWhgzCVmX1eU_gX1uU7BksHHViIPqQ180huyes4eDP0LVz2sisk",
+ kty = "RSA",
+ q = "ymSY6f7peJTh3i_IfQdoymrhuFT3PB6EOjDCEtaiKUfzC4iy4Sj4zlCAm4hvoo1O9Bl_qeGRVsFFz47JD66Kd1S0fJV-5ZmyroKy0h1vcwNudUWdNZlplQfg22I3GgNeWzte_xFF7kyv49BFkPPZ9VyAfDSdzN4ngv-JLkaFz1c",
+ d = "nR5APnZezsNHk9kbHeq1Who_YQPikMlqNI39bEbTWq_eSsAkpgSIMQv8r_w76_9jL2ilYu26arWxi4ReEa8HvbIc9VxLRO392zw6JdPrNBVfWRw5YFkBf5V3_5B8VDjzmuUNpZHR6H92MhLJ7P3kqd6kIb9fJLFZlqkKQs-XPtfI4eTNfsuSkDLqaxGs6dRSc7za6wqnajJUuX0ThwEGRUjBEdx5fzCJH5_8-Y4DMb7qmgM24nNR9pHqhmSDJFDUSuqRNZEyZD-9wnl-A2rXMakNknll-4WkjlcccqzDSMoHpCyXkn5fduMrHuBSBqsIIWJCIVnwuSaOjCh8nlwBkQ",
+ e = "AQAB",
+ use = "sig",
+ kid = "test2",
+ qi = "RN8D_k4J2bx247FKfLsabNBczUDKE2YlJSru2viQJzy9doEPAd0WCMQ2da5xc3JubHmnnfpUWMcl4bcOOVlMSEIu57NSqy1N3xY52m_OztfceRc4R1Ri1i79mX17w2cFDGiE7HIQtWEmCw8ijN1-7JzJq1StkhtHgTFPGTXI2FE",
+ dp = "C8HkrpAKwNn9X5BdDfbEf2j7V-ltiU_LtMvSE9qtIqf-k67iDdtAGuuTb2VEBuesHvmgY7Sas01GN9xP_dN_S0DLkz5boU1Pj2ZraJBKGRGHOTEreoskPVHTLBITw44aRRqW_grLBofzlwEi4hSIFv7fhL9ylhJD7ql1JmoT4rE",
+ alg = "RS512",
+ dq = "CKTZPw0rDYJWTzX9OxHI8PQ1pbjDQmcPQKj6cPGHxXmUMMbq1OD6F12q0Hm4QtoEKDq6kBfZLDpe7-lqPug2c7hDaRq9a7LvxbJBTuYA74mS-yE5AKQHtVy7xsLgFZVVP1I-0Wf8c5wE2xb3EaTIh0knF6bromdOirK9OiO67Us",
+ n = "prgDZtlv66YmFnnyPkWbJFYi_Ozz2qmyAod4loa_lzsbGP3Db4wziSAs8mmrSbjYfX1U-OvIYKiykPiIim1F7EUQjF4GZX8luCUPmO8MLoqC2PMIXvGyBU84I2pz0a6ipxDs_5IkXDRNjHhEieyVfxVKz_8CSLphMohlJqRelGFtGMp89zu5ZQix1CA3UpMnAlQg_peDMob81VeYYus8S92moL110V8JPsiv7Ao4eQ6aUyS0dJZGW9dMckDaNz5kf9XppeKFP3X_d6mdUeDODRSH-O4bxA8mGR1tPWDXrelXn86r1969JSJtvwpsc3qwSueORH4hgu2A2B85ikixTw"
+}
+
+local client2_jwk_public = {
+ kty = "RSA",
+ e = "AQAB",
+ use = "sig",
+ kid = "test2",
+ alg = "RS512",
+ n = "prgDZtlv66YmFnnyPkWbJFYi_Ozz2qmyAod4loa_lzsbGP3Db4wziSAs8mmrSbjYfX1U-OvIYKiykPiIim1F7EUQjF4GZX8luCUPmO8MLoqC2PMIXvGyBU84I2pz0a6ipxDs_5IkXDRNjHhEieyVfxVKz_8CSLphMohlJqRelGFtGMp89zu5ZQix1CA3UpMnAlQg_peDMob81VeYYus8S92moL110V8JPsiv7Ao4eQ6aUyS0dJZGW9dMckDaNz5kf9XppeKFP3X_d6mdUeDODRSH-O4bxA8mGR1tPWDXrelXn86r1969JSJtvwpsc3qwSueORH4hgu2A2B85ikixTw"
+}
+
+return {
+ client_jwk = client_jwk,
+ client_jwk_public = client_jwk_public,
+ client2_jwk = client2_jwk,
+ client2_jwk_public = client2_jwk_public,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/key-auth-enc/FTI-4592.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/key-auth-enc/FTI-4592.yaml
new file mode 100644
index 00000000..c6ac85ec
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/key-auth-enc/FTI-4592.yaml
@@ -0,0 +1,64 @@
+# workspace default
+_format_version: "3.0"
+_transform: false
+
+workspaces:
+ - name: default
+ id: 213e4358-0611-4a0f-8b6a-e1a0df608a04
+ - name: test
+ id: 84c4e4b7-a307-4478-8875-27a484599159
+
+consumers:
+ - id: 1bd69f90-741c-4e75-834e-94155d6e3f5d
+ username: bob
+ ws_id: 213e4358-0611-4a0f-8b6a-e1a0df608a04
+ - id: 8df2d29b-fb8f-45b2-b90d-4b7fdbaa92e7
+ username: bob
+ ws_id: 84c4e4b7-a307-4478-8875-27a484599159
+
+keyauth_enc_credentials:
+ - key: wcKDwkL3I5nOCDVd8qlMhWKSV69NE7uf
+ id: 05f41be3-0002-4fcf-b361-3f479bf2e437
+ consumer: 1bd69f90-741c-4e75-834e-94155d6e3f5d
+ ws_id: 213e4358-0611-4a0f-8b6a-e1a0df608a04
+ - key: wcKDwkL3I5nOCDVd8qlMhWKSV69NE7uf
+ id: 535cb5a1-d8b5-4698-a75a-6fcda03ed08f
+ consumer: 8df2d29b-fb8f-45b2-b90d-4b7fdbaa92e7
+ ws_id: 84c4e4b7-a307-4478-8875-27a484599159
+
+
+services:
+ - host: localhost
+ name: example-service-1
+ port: 9036
+ protocol: http
+ ws_id: 213e4358-0611-4a0f-8b6a-e1a0df608a04
+ id: 6e40e2ba-3a9a-4201-87ef-fd6029005da7
+ - host: localhost
+ name: example-service-2
+ port: 9036
+ protocol: http
+ ws_id: 84c4e4b7-a307-4478-8875-27a484599159
+ id: 9c70e94e-341a-4bd4-b62a-7d1e42778746
+
+routes:
+ - https_redirect_status_code: 426
+ name: example-route-1
+ ws_id: 213e4358-0611-4a0f-8b6a-e1a0df608a04
+ service: 6e40e2ba-3a9a-4201-87ef-fd6029005da7
+ paths:
+ - /test1
+ - https_redirect_status_code: 426
+ name: example-route-2
+ ws_id: 84c4e4b7-a307-4478-8875-27a484599159
+ service: 9c70e94e-341a-4bd4-b62a-7d1e42778746
+ paths:
+ - /test2
+
+plugins:
+ - name: key-auth-enc
+ ws_id: 213e4358-0611-4a0f-8b6a-e1a0df608a04
+ service: 6e40e2ba-3a9a-4201-87ef-fd6029005da7
+ - name: key-auth-enc
+ ws_id: 84c4e4b7-a307-4478-8875-27a484599159
+ service: 9c70e94e-341a-4bd4-b62a-7d1e42778746
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering.crt
new file mode 100644
index 00000000..a92d31ca
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering.crt
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDzTCCArWgAwIBAgIUMmq4W4is+P02LXKinUdLoPjFuDYwDQYJKoZIhvcNAQEL
+BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
+DVNhbiBGcmFuY2lzY28xIDAeBgNVBAoMF0tvbmcgQ2x1c3RlcmluZyBUZXN0aW5n
+MRgwFgYDVQQDDA9rb25nX2NsdXN0ZXJpbmcwHhcNMTkxMTEzMDU0NTA1WhcNMjkx
+MTEwMDU0NTA1WjB2MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW
+MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEgMB4GA1UECgwXS29uZyBDbHVzdGVyaW5n
+IFRlc3RpbmcxGDAWBgNVBAMMD2tvbmdfY2x1c3RlcmluZzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALr7evXK3nLxW98lXDWUcyNRCKDzUVX5Rlm7ny0a
+qVIh+qRUT7XGHFnDznl7s1gEkcxLtuMnKBV7Ic2jVTzKluZZFJD5H2plP7flpVu/
+byvpBNguERFDC2mbnlX7TSRhhWjlYTgFS2KiFP1OjYjim6vemszobDsCg2gRs0Mh
+A7XwsVvPSFNfnAOPTpyLRGtN3ShEA0LKjBkjg2u67MPAfg1y8/8Tm3h/kqfOciMT
+5ax2J1Ll/9/oCWX9qW6gNmnnUGNlBpcAZk3pzh6n1coRnVaysoCPYPgd9u1KoBkt
+uTQJOn1Qi3OWPZzyiLGRa/X0tGx/5QQDnLr6GyDjwPcC09sCAwEAAaNTMFEwHQYD
+VR0OBBYEFNNvhlhHAsJtBZejHystlPa/CoP2MB8GA1UdIwQYMBaAFNNvhlhHAsJt
+BZejHystlPa/CoP2MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
+AHQpVBYGfFPFTRY/HvtWdXROgW358m9rUC5E4SfTJ8JLWpCB4J+hfjQ+mASTFb1V
+5FS8in8S/u1MgeU65RC1/xt6Rof7Tu/Cx2SusPWo0YGyN0E9mwr2c91JsIgDO03Y
+gtDiavyw3tAPVo5n2U3y5Hf46bfT5TLZ2yFnUJcKRZ0CeX6YAJA5dwG182xOn02r
+kkh9T1bO72pQHi15QxnQ9Gc4Mi5gjuxX4/Xyag5KyEXnniTb7XquW+JKP36RfhnU
+DGoEEUNU5UYwIzh910NM0UZubu5Umya1JVumoDqAi1lf2DHhKwDNAhmozYqE1vJJ
++e1C9/9oqok3CRyLDe+VJ7M=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering.key
new file mode 100644
index 00000000..67e3d9f3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6+3r1yt5y8Vvf
+JVw1lHMjUQig81FV+UZZu58tGqlSIfqkVE+1xhxZw855e7NYBJHMS7bjJygVeyHN
+o1U8ypbmWRSQ+R9qZT+35aVbv28r6QTYLhERQwtpm55V+00kYYVo5WE4BUtiohT9
+To2I4pur3prM6Gw7AoNoEbNDIQO18LFbz0hTX5wDj06ci0RrTd0oRANCyowZI4Nr
+uuzDwH4NcvP/E5t4f5KnznIjE+WsdidS5f/f6All/aluoDZp51BjZQaXAGZN6c4e
+p9XKEZ1WsrKAj2D4HfbtSqAZLbk0CTp9UItzlj2c8oixkWv19LRsf+UEA5y6+hsg
+48D3AtPbAgMBAAECggEBALoFVt8RZR2VYYEu+f2UIrgP9jWp3FFcHdFIB6Qn0iwU
+AfdaqbJ91da4JsJVfqciZKqK6Pg0DHzSc17SEArawiWImh1guxBuimW54jjUyxU0
+Tc2EhxZVTRVT7MI9sRFws/kXuxCws7784UTg0Y5NY/IpFHinAoXyiikO8vjl73sg
+trN5mQGNTE/c8lEs7pUAFWX9zuNbmV0m1q25lHDgbkAD76/9X26lLCK1A5e2iCj3
+MME6/2GlSy3hrtSY7mCiR1GktvnK+yidXXJSkGMNCSopQARfcAlMvcCDav5ODxTz
+mB+A47oxGKBTdc9gGF44dR15y5E1kRAvTtaAIzpc14ECgYEA4u9uZkZS0gEiiA5K
+pOm/lnBp6bloGg9RlsOO5waE8DiGZgkwWuDwsncxUB1SvLd28MgxZzNQClncS98J
+viJzdAVzauMpn3Iqrdtk9drGzEeuxibic1FKMf1URGwKnlcsDHaeKAGyRQgO2Q7l
+Oy7EwtRmUKBUA3RCIqLSoiEi6NcCgYEA0u4a2abgYdyR1QMavgevqCGhuqu1Aa2Y
+rbD3TmIfGVubI2YZeFSyhC/7Jx+5HofQj5cpMRgASxzKXqrCXuyb+Q+u23kHogfQ
+cO1yO2GzjlA3FVHTK28t9EDPTOgHWQt3q7iS1s44VHwXDOpEQJ2onKKohvcP5WTf
+LO0T2K9NOJ0CgYEAtX9nHXc6/+iWdJhxjKnCaBBqNNrrboQ37ctj/FOTeQjMPMk2
+mkhzWVjI4NlC9doJz5NdJ7u7VTv/W9L7WMz256EAaUlbXcGSbtAcVCFwg6sFFke9
+Lxuhqo+AmOSMLY1sll88KKUKrfk+3szx+z5xcZ0sY2mHJ+gQiOEOc0rrP6sCgYBi
+Ksi6RU0mnoYMki5PBLq+0DA59ZH/XvCw3ayrgUUiAx1XwzvVYe3XUZFc6wm36NOr
+EFnubFIuow6YMnbVwN7yclcZ8+EWivZ6qDfC5Tyw3ipUtMlH7K2BgOw5yb8ptQmU
+FQnaCQ30W/BKZXkwbW+8voMalT+DroejnA7hiOyyjQKBgFLi6x6w76fTgQ7Ts8x0
+eATLOrvdvfotuLyMSsQLbljXyJznCTNrOGfYTua/Ifgkn4LpnoOkkxvVbj/Eugc7
+WeXBG+gbEi25GZUktrZWP1uc6s8aXH6rjYJP8iXnUpFHmQAPGuGiFnfB5MxlSns9
+9SKBXe7AvKGknGf7zg8WLKJZ
+-----END PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_ca.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_ca.crt
new file mode 100644
index 00000000..c16572e0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDLTCCAhWgAwIBAgIUBEI9LAuIcrg6fFLxC+Qo8SaEvfswDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTa29uZ19jbHVzdGVyaW5nX3BraTAeFw0yMDAyMTkxODIw
+MzlaFw0zMDAyMTYxODIxMDlaMB4xHDAaBgNVBAMME2tvbmdfY2x1c3RlcmluZ19w
+a2kwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqJdBsqWhTxA0k83N1
+9KvV6Z9YBee4BnYhQiOe8HgL8+d/SS/Ri35Ue+rZ5YbGu0im2KwzpsIEc5dw2/Tm
+eL5QL7rGnPeEQlRNufP27lI07M5XVsQx3VeZ41lhVFBwr03XQCOdozkeimriZryd
+WElslevdH4htxALDobK+HaSWl8FR8kJWlflaXuoOH0A4SQf2djo3/kmRkok5OXZz
+oeActuO5tE8/EveykABNyC4HOOfHSFCrm4dzVTskEZsJKEgQGqXNG30S5SmP6keS
+4npcgVTYV8DQinwY3lpwGb5f62nCFYbiu7M41e3ly+J9UP+qxmI9Qm1QG3rlVCtF
+wCJbAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G
+A1UdDgQWBBRdgggWza66a1dWqUDM05ANj+BUJDAfBgNVHSMEGDAWgBRdgggWza66
+a1dWqUDM05ANj+BUJDANBgkqhkiG9w0BAQsFAAOCAQEAt9nD9jNxF0d08ioqY92R
+MR4WXa7Lp0jZUavLevXl+xhSXUo/ovUARHXs9qJ8SA/8u6eADtUQNGs0D03OSpD+
+klY4FmbcfJSBSXjOgwUz4hUVnUk/MESmLOKRMfqAW9oToBqWa/sdh9B8UzXLeM7c
+DPazf0K9AcaITUuwltDIFkQH80DiRZnoOk6nag/eBkK4nI42UZCvrg0ffRq4HY43
+jvb4EpIOf3i6PfkWznxwPq4UJLoj3NGhPwBzxZEpyfu5bvXFB4sSww9RMwu+w/fT
++wjt04CbQf286zWgOcGShJYBzNZ2EKQji6dC5PBpBO/iidddnxxCKnF/04ivSBME
+6g==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_client.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_client.crt
new file mode 100644
index 00000000..b5853359
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_client.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSTCCAjGgAwIBAgIUYr7mGItDJ+hOGVdVQSYBgacnKqIwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTa29uZ19jbHVzdGVyaW5nX3BraTAeFw0yMDAyMTkxODI1
+NDhaFw0zMDAyMTIxNDI2MThaMCwxKjAoBgNVBAMMIWNsaWVudC5rb25nX2NsdXN0
+ZXJpbmdfcGtpLmRvbWFpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AMv1wKHhIy7RrHr/MFNYzZXyCEcdc0/b+BxFzhgdy8n3KI2rR60WtVulCGItbr35
+Dj3NxfRHTTAF1BvlbCg+A4bEOz3lfuuD1NQHRbLK+j2mH+GuHmUMV9fP9jf7SfbL
+zGR1zdj+d/xQHe3oSsEW3LQ/vjnWpFmVA8LFE6xYKQQtKqPHxJ+/hnohqcW2RiOJ
+EW7oAgjB/HWgwg1kgshEaAhouWWSzI2uEilxHEPtsTcSJCd1bxO0oU61jYa1dkwX
++CWhW/CDoyiCWzxr97lavyW02xRwvcDvTbpXD3sFQScp36quZ1Igr3iWGniKCaQa
+hZ4/OA7TGlhbOf5Bu4RihTECAwEAAaNxMG8wDgYDVR0PAQH/BAQDAgOoMB0GA1Ud
+JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUaeNNIcyO/C7B4L0F
+BQC/2ZILhiowHwYDVR0jBBgwFoAUXYIIFs2uumtXVqlAzNOQDY/gVCQwDQYJKoZI
+hvcNAQELBQADggEBAKUBSjBESwRwgyhSLmZU6LWbVmezmGmkTdTmaArwN4SZL0Vm
+gBJSatBN+MHuqzgnUxvgLIGmZLCMfL1rW6ERv/sOXWV6QTN+91RoGFf9zGBJ+tmH
+oXs+qps9Ge8zV+W63DoNqRKOkU9TrEWYt9kAQE/y6zTCMtmwRBaSAWurlwO6FZcF
+D9apy74B7RNjM3L0NF4Nj9z5O44drWPIcBpd+26sVQuUkyda2MCyLItla7OSHAWl
+FgDQt1Bmx1ByL1PeogKBQ/2elwsw0hn8jqNLDu0YuNEdUz4h6oGxdPsh1zJeGT7l
+u/Jqwu+kkQgZk8UIiKV7A9cGXD88W3+ABpf5IX8=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_client.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_client.key
new file mode 100644
index 00000000..f4abb659
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_client.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAy/XAoeEjLtGsev8wU1jNlfIIRx1zT9v4HEXOGB3LyfcojatH
+rRa1W6UIYi1uvfkOPc3F9EdNMAXUG+VsKD4DhsQ7PeV+64PU1AdFssr6PaYf4a4e
+ZQxX18/2N/tJ9svMZHXN2P53/FAd7ehKwRbctD++OdakWZUDwsUTrFgpBC0qo8fE
+n7+GeiGpxbZGI4kRbugCCMH8daDCDWSCyERoCGi5ZZLMja4SKXEcQ+2xNxIkJ3Vv
+E7ShTrWNhrV2TBf4JaFb8IOjKIJbPGv3uVq/JbTbFHC9wO9NulcPewVBJynfqq5n
+UiCveJYaeIoJpBqFnj84DtMaWFs5/kG7hGKFMQIDAQABAoIBAECRRPE6P5QTxoGN
+hbPs+fBNFbfFp/qRDWON6aeSzy/ZsZnfrioqdTFU9ml2Loevb4W7Pb6OT+4y+in2
+oB3BPAaO2p5Gp75RFrAkuTjXrw6TJ6afI3ojxS9J441nXMvnSoZHEOpv7HYQutjU
+381uumHJiCPxQkglt3ISR58YratnPFKKO21p9JRXBVDGefLhR9cFjhvRixsxF+DU
+y9l0X6hBq/VXtozzLMYpsUwLtzc6+FWIz+pLv5Y+JGtONx5IdaTW5z/VFLMNZHdP
+9cwwYKdR9dm5c37a1HKuHOZvBVAWRwjaC+uaF8IoyrGteAA9HMjCvR+jz9VST+nG
+CVGaN1ECgYEA3+OCeOcdWA2dolWlHPcSkR0BEpHmKlxSYjShJVa1JvW9C91rXw/E
+5jeCtTBmMOT/6OT/SEtaSHtMfuWSknig8OfHO0zO1N5t4+xgmp47vWUFR2fHK5PF
+GoqAYYPmYOZdSAsqE+9Rm7gzjDhriCRl6vvr0gV1bbn3BtyrCJo3XR0CgYEA6TaF
+qDhAoNQlC/GuC0JcrcxZzH2WecdSjncEPFv5xsvzc7K6Y1ptRx+9ntC8x0VyLHJJ
+RL2fPOC3HmGIVko/JS9b8kjciymwRzsQBsq3mDHzloqBAnJI3w1nzibAKfc9Azsp
+ZsZsLK+PWTkx5QR/cl+Avw8K7z+Wil4pfxUdUCUCgYEAnyXgRwUybLcxCIYM2qwe
+vJx7eLHRhOcfgMOckSgzmUAxY8+/VVGmS2fGN/nximweJXUyqjc2NDPBSqryJ8Ar
+11NK4jJVDCgYwV3zxbUZDiZTPFDe2XEvORCe9bKG/vaB9IZLIOSLhD/KFLC+b4ZA
+FGuDVEWhFaCNookV4wWMuVkCgYAQ+VjtD3sUuZwgrGyAgUoBlFgM3C8xmeJWYC88
+vW0GnR2RFDcguBowDQ1eG89BqbpPpdnTWQHYCnsJiQvFTA4ghLvqfIcg0d30sUXP
+WL3YI/qYwGf3Z54KLg2aIZPm+gnEzG+M/XAuKHEEi3coNhWYm37haRTgqP4qAASB
+LWY+yQKBgEN/zl6yTDg1iCGt/QlZWTdeQEWvCSrdaGjqvsFXlWoNsnf20zXiS/rU
+NATB1+T74kKIohYiagcgYt4H4P8wXX7rEFhCi+aYtz5ZRmjrD0hrbJkqmR+VPBJP
+SIHDqpgwRx0gXFoCV3JWl9dvHWUFl/FgRD55oCe1C8lxqZNpt2Fu
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_server.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_server.crt
new file mode 100644
index 00000000..7abc701e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_server.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFOTCCAyGgAwIBAgIUGIGEUNv1gzJpSkoCZbKcsjmuQxcwDQYJKoZIhvcNAQEL
+BQAwLDEqMCgGA1UEAwwhc2VydmVyLmtvbmdfY2x1c3RlcmluZ19wa2kuZG9tYWlu
+MB4XDTIwMTIwODA5NDU1M1oXDTMwMTIwNjA5NDU1M1owLDEqMCgGA1UEAwwhc2Vy
+dmVyLmtvbmdfY2x1c3RlcmluZ19wa2kuZG9tYWluMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA3SvUdI94oqmGXkv3YBws8wxITBJ+f/ri+dTBlKSaw0/b
+2MRaP8zkhGywuQd9owVv0q9XfTUJChbmzAKwS4oC/Kk0oJ6/xD5Xv/6KtRHCydtG
+Yn3o/INzE6gJJ8f+pAotvTZfLRs7O4nfJHYpKlnpJsUG31RR66yiVOd09c5dQyQg
+CjcLH35Jv4vIIyQN6QXVZAf3QVYgF193bqoFEaL5OZH4i4Y5PMevaCOLl7f4NXy4
+XEHbvRtBUuN4EISai7Riyx00M60a/ocmWzJr3PtBFYu4t5hIiReNuoQAJxAmqvLb
+RXh5ti3LOgNel1kjlcKXaDWGMeIAj4T5IgRikMPosEhdUU7CIlTcGJFj03e9uUNY
+VW7SfTopDLdbFrblYqrqPO7djQ7XI5iHGyP3EJHkKWpbT6YGCRd5+bWp4qwRHHjZ
+xyxK5lx3tVbhd6ZXqlj/+7fiEn5W6fVGUyRjb+SPavpihgal2G/yeJrY4JYeHTSj
+nWQ0P5dIGHVBgDWzcUpSJmTmuayQ/+lU2+zc2UWfhFqmgyArR4QlzzNMDU0362qb
+amxN+VOsHMJ2XJWETB44IfgW3qv5oVoOuZdl6883TmE4oSzWzyG/HKBvujBMOSIQ
+u5PPDXQrweuoWx7mHhIaSNy4/v4BtpZ7Y3IUxbWXA5W7Voe16E9v+Aos3lx6Si8C
+AwEAAaNTMFEwHQYDVR0OBBYEFA2pU/tVp1+iXxS1pAQJSVY807rfMB8GA1UdIwQY
+MBaAFA2pU/tVp1+iXxS1pAQJSVY807rfMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAE9wnFNn/D6qB9CYfT6AU7+Xza6i+a1pKOkPheU9LSRWUKNh
+Qz0+N2qdV3mozBC1kjT3XW5LRxE8NpQjGG6oZ+Caa6VR9RQZkLihYxqGtgociVT1
+N34tWzy6ck1UAsMpy9QwvFUE0udgpR1NSbYjFElMQmOM5Pr5phr4r0+mDljX2ro3
+I5m5NZ/W9jaICrxqS3GnP91a5V2TcLmwSZGiC2TOqJLhi0zc+Jfkscr6hik3P2Bc
+kzSbM4A+6lo5nE7221/CRFg/hQXx8dxfySCl3eHQYjuBSQAsYyVSO5DFmaX+KJE6
+4Wg5sMRcmMDRa/sQhDJiBH1+RQMtJRUPDwds0y6WIK4pfONJjiCeE0/3PLpem5qE
+/h3AJjoYWnJ0hTkjnR26OW7bWrwuo/RVMQq3An78TsQWyr1SDazPpjfFYwxZk722
+sgYkIuCZ3I+rW+cAw9VvrT3gh7QFpEb3Yuh5ehr6srl/jD2p/WeVBYQwHJKpgxGF
+13t3DXbzAVdNDy/M1gJxfHyDTe3kxs03BckMz+fd19EQhlVd4ZI+14xm2kr+ULKg
++pDTdg5TpcgqcfpG55sHWm5G2059fkEk/z/8ABtB4v4fcKA7ZmrUcDlpSQd/ejjt
+obN+e+Dq0Rm5Rb5oI/wHHd6JLW1TqLEH6w6ZyNsOjofiJWSSJTC75eH4HJgl
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_server.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_server.key
new file mode 100644
index 00000000..2add6a0e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_clustering_server.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDdK9R0j3iiqYZe
+S/dgHCzzDEhMEn5/+uL51MGUpJrDT9vYxFo/zOSEbLC5B32jBW/Sr1d9NQkKFubM
+ArBLigL8qTSgnr/EPle//oq1EcLJ20Zifej8g3MTqAknx/6kCi29Nl8tGzs7id8k
+dikqWekmxQbfVFHrrKJU53T1zl1DJCAKNwsffkm/i8gjJA3pBdVkB/dBViAXX3du
+qgURovk5kfiLhjk8x69oI4uXt/g1fLhcQdu9G0FS43gQhJqLtGLLHTQzrRr+hyZb
+Mmvc+0EVi7i3mEiJF426hAAnECaq8ttFeHm2Lcs6A16XWSOVwpdoNYYx4gCPhPki
+BGKQw+iwSF1RTsIiVNwYkWPTd725Q1hVbtJ9OikMt1sWtuViquo87t2NDtcjmIcb
+I/cQkeQpaltPpgYJF3n5tanirBEceNnHLErmXHe1VuF3pleqWP/7t+ISflbp9UZT
+JGNv5I9q+mKGBqXYb/J4mtjglh4dNKOdZDQ/l0gYdUGANbNxSlImZOa5rJD/6VTb
+7NzZRZ+EWqaDICtHhCXPM0wNTTfraptqbE35U6wcwnZclYRMHjgh+Bbeq/mhWg65
+l2XrzzdOYTihLNbPIb8coG+6MEw5IhC7k88NdCvB66hbHuYeEhpI3Lj+/gG2lntj
+chTFtZcDlbtWh7XoT2/4CizeXHpKLwIDAQABAoICAQDAYZWl5f47D1avw0hkM13t
+8+sXMKcUT3kLc0MHNr7f0SEcRn9yK5lCjwCRkbB5EwuXqaliYbWBLzaGkngi3OOI
+Pu5mIHASFWBiwtiIc/CJgNCSb48vxm9c07dTASXALpJmmVrkc2Fsgpq0rotUKW0i
+qhM2Q7QWWc3CKKqV/XEmK9Aq0oI8XK9ti/sEhiILZWWeY0dxb6bubb4HZqCOHMLI
+ju4w/wnekptC7FhKGl4uuFPs++RVc9W4ceBIDfAYQUoF/1se8B4g9BIrFYq5Z1vP
+j/+Ww0Gk7fX8HUG8uNHJtQGCRi8ohmDWD4RCBelsJR1qKdSWo011iWbjbgkGf0Jw
+JiiU9yJBMjXhedEnsrQnqRzqL/+GSX8MciHMmzaHlrIYiinEaVRsZ+m2Tq3KiE8v
+VVD39gZCbEkN4swu+FdHEpdL4FQBslz29SOixKmBadK+S3ZNK3Q2SodIVP9DYcod
+Oga1LoZeVkNSFMalcal2PmgT6IfIZHC/i5yAF40xV/SESg5QZNVWUCFnnjR5UxEN
+Gn9LxXBHoJlh1SSI7Mf7OSSBC/n3JY8XOoY364v1OkqsMYvgnz1CbruRNDI76Bc+
+Rl9jRImmqO89jgNMzQpcCOAeb/fNnjYqCb19J/Bke1c/c0cw1pvJvPxGgWJ/MqLu
+/pIQjMY2tqO3wAzBrmAsYQKCAQEA8CSpxsSyIE2xGMkfasAQ9axMohd76xqskpUR
+Uw+XR7yAwh1qrGKGa9rXai3tjTvMVRjIk23z+yJf1NhwTp77ssFyOBDIJAm8+bkJ
+zRBwIWKFQytZKLM8BhxJvrMAn81YbgXmhoz0A5felqzAfILZP0Wghm0NWdIz81E4
+hcspKuwcxMh5HH7xKhBfX9hko3zo6qzgCl+drRZBSBkPRguKZE0/bCZWY5rb1c1w
+PgDWgjknkBFamo8UWFYrkwVf9X1ARtMOGRaYzoEHt9ME0It3p7wisP7BYzLbQBLk
+ca5rQztccmgUrSII2BqeBXkYx0OSLow/pgQcVD59Do5ck7CNJwKCAQEA68Z3qTmf
+NmI3cHkkP5flMT4xkloaMSaUu+wZbqKBpxbahazSTO6cayXUDEDkDGIO1XjJxhtF
+/FNQ7lgQCsUXxmF78caNhtE1vjo4zXdgf9yVkSM3wBmZTOWssFYbG2sgelz3yHjm
+mAztj9cvrnrenJeqZTYx0SNsqBwccy12WGek6cgk+bQLARXWctOwJ+d0RWRaqfab
+tU2/2Dg4H53w8sNG7AXQ6ELq9tIHkykHs5T3mmYFft2NMgO2Wqfy5yPWckhJlO/P
+D+ZLUCETCB153yf9N9KUjlZRDoIhn9G2JoEuDw1UMyj79c2H1KChdNwCLaJ4RfqI
+CEJq87H4mskPuQKCAQB9wqWKq52hHy68jS8/x1/aWKY1SNkjcW+owBo3hnWyIrzH
+a4JezqYYu52ORIkgKo3Cjt0ToERiY0kOfV6rPcUQgxAe3mMBxHvLx6SjWJVwVlj1
+qTDjCKJJjOztym5sQayqybpFAnITo6f0/nk7e4bsBLmhTGDuhg+gwAs6vf9MNCSI
+xXfvfWcsoTBhUEC8AYE0zN3zcMlxSLNdX4pJI2gRSQcKyzT4qrOvzwuV3Ph5cBcQ
+8pWhVYbRECd3shvjwvkzXfoHF3dkxdvV39OopzE9jWAZkwCRw6NgdcRe6HnR1rd+
+TpVtaeJ50c6pjRZ13CQk7luyxHpVf/gb9F/1FEOHAoIBABcajaNBiD52BWS6Gjk5
+EzZuF8F1ntduJmj7y8oxlm7fKG6s7ranMyC0xkClIzOrcDSu72bASW1eZLHWY0wK
+4DeKWfDpOfkaCyAm6GYJ6qTc4rMkNlRWrdUBNJja9zEKoFYdQhKc9ku0CJttxLhe
+Re4mVoYwd1U9GWqnxonqzIwBLAHUoPrvD5z9nNQHtl1SquwZ12CMT9nGUqRHbgh7
+G59OT2bVcxEvbYLeNevPu3d3g4PXt3YlE67w+i+MAZtwwZTVTJCRp0KxmHOzqf/D
+k6uwaXnM4s3H242GZvEYRbckNXU4IyYYQAhoKYVGcEvZO0VphEunLylQCNb0e53/
+UdECggEBALgTFFed9iTiZxvFeT8p/Xq7WIOyf82A/stvPDlm4Hyl+7nQ3oxqp05D
+B+tPtjPE33rjfwhevd0OZVF6DAB792E9x6Ln1khlsbgdSuASJNHkrj12Ib8WVib5
+U+O5Ps9s3+VHe53C0e2nP+SNv8UdCu41liudQtmrZnCfKEYaXs/Yysd/LYsp2hbe
+0NYhG84WeLajumVI0CbENkkPioOOJUbo8ytIPgtWgbEFuy9DZUcSCwrg7UoWqoqL
+qGe7eYuHDetoxuA2men4mgBLLEeo1DqlW9BIO1qz2LOU4EW/0AlLgGMVwBI7Z/fj
+VzlV/SRWxLEaQCujgP/IFVniA+r/yw0=
+-----END PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_spec.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_spec.crt
new file mode 100644
index 00000000..75cd9602
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_spec.crt
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBmzCCAUGgAwIBAgIUDTiAG3jByZjjmUwJZJDtuLItbWwwCgYIKoZIzj0EAwIw
+IzENMAsGA1UECgwES29uZzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMDQyNDA5
+MzcxM1oXDTQzMDQxOTA5MzcxM1owIzENMAsGA1UECgwES29uZzESMBAGA1UEAwwJ
+bG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEus1/MmWimrKiQngJ
+7Ia+SvsU5FCIpttKfEvcKQZRzImlAVLpWEGnf7IbqxgTUP/03JmVkg9SdExN/gP8
+6Usgq6NTMFEwHQYDVR0OBBYEFIXbvRnB4mXOnZ5Ps6AnXz66FPPgMB8GA1UdIwQY
+MBaAFIXbvRnB4mXOnZ5Ps6AnXz66FPPgMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZI
+zj0EAwIDSAAwRQIgXiyT9+6AS1oSR6A06eTVaiM1/XO80r1h24OrTXvc/pYCIQDA
+yHqB8IlSpNGJXcsEUuuhvbGT6ZcpLeEWNfNFtVp11w==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_spec.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_spec.key
new file mode 100644
index 00000000..1da05db1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/kong_spec.key
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIEIaZr9tJn4gjvOisL8syGCKs8pC9eYGqpHdp012CPmqoAoGCCqGSM49
+AwEHoUQDQgAEus1/MmWimrKiQngJ7Ia+SvsU5FCIpttKfEvcKQZRzImlAVLpWEGn
+f7IbqxgTUP/03JmVkg9SdExN/gP86Usgqw==
+-----END EC PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/000_base.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/000_base.lua
new file mode 100644
index 00000000..3a4b4f82
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/000_base.lua
@@ -0,0 +1,107 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ CREATE TABLE IF NOT EXISTS "cluster_events" (
+ "id" UUID PRIMARY KEY,
+ "node_id" UUID NOT NULL,
+ "at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "nbf" TIMESTAMP WITH TIME ZONE,
+ "expire_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "channel" TEXT,
+ "data" TEXT
+ );
+
+ CREATE TABLE IF NOT EXISTS "services" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "routes" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "certificates" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "consumers" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "snis" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "plugins" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "upstreams" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "targets" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "filter_chains" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "key_sets" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "keys" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "sm_vaults" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ INSERT INTO sm_vaults ("id", "ws_id")
+ VALUES ('23111c66-8c80-4f8a-8f18-7d6c495bc36e', '23111c66-8c80-4f8a-8f18-7d6c495bc36e')
+ ON CONFLICT DO NOTHING; -- Hack, mock data
+
+ CREATE OR REPLACE FUNCTION batch_delete_expired_rows() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+ BEGIN
+ EXECUTE FORMAT('WITH rows AS (SELECT ctid FROM %s WHERE %s < CURRENT_TIMESTAMP AT TIME ZONE ''UTC'' ORDER BY %s LIMIT 2 FOR UPDATE SKIP LOCKED) DELETE FROM %s WHERE ctid IN (TABLE rows)', TG_TABLE_NAME, TG_ARGV[0], TG_ARGV[0], TG_TABLE_NAME);
+ RETURN NULL;
+ END;
+ $$;
+
+ DROP TRIGGER IF EXISTS "cluster_events_ttl_trigger" ON "cluster_events";
+
+ DO $$
+ BEGIN
+ CREATE TRIGGER "cluster_events_ttl_trigger"
+ AFTER INSERT ON "cluster_events"
+ FOR EACH STATEMENT
+ EXECUTE PROCEDURE batch_delete_expired_rows("expire_at");
+ EXCEPTION WHEN UNDEFINED_COLUMN OR UNDEFINED_TABLE THEN
+ -- Do nothing, accept existing state
+ END$$;
+ ]]
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/001_14_to_15.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/001_14_to_15.lua
new file mode 100644
index 00000000..a2f18917
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/001_14_to_15.lua
@@ -0,0 +1,13 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[
+ ]],
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/init.lua
new file mode 100644
index 00000000..5c4163d3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/db/migrations/core/init.lua
@@ -0,0 +1,10 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ "000_base",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/000_base.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/000_base.lua
new file mode 100644
index 00000000..a4bbe3b5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/000_base.lua
@@ -0,0 +1,97 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local utils = require "kong.tools.utils"
+local enums = require "kong.enterprise_edition.dao.enums"
+
+local fmt = string.format
+
+-- This query is designed to ensure that the workspace entity counter does not count non-proxy
+-- consumers. The created consumer may not function as expected.
+-- See also: `seed()` in kong/enterprise_edition/db/migrations/enterprise/000_base.lua
+local mock_admin_consumer = [[
+ DO $$
+ BEGIN
+ -- Mainly taken from enterprise base migration
+ IF not EXISTS (SELECT column_name
+ FROM information_schema.columns
+ WHERE table_schema=current_schema()
+ AND table_name='consumers'
+ AND column_name='type') THEN
+ ALTER TABLE consumers ADD COLUMN type int NOT NULL DEFAULT 0;
+ END IF;
+ END$$;
+]] .. fmt([[
+ INSERT INTO consumers (id, ws_id, type) VALUES ('%s', '%s', %d);
+]], utils.uuid(), utils.uuid(), enums.CONSUMERS.TYPE.ADMIN)
+
+return {
+ postgres = {
+ up = [[
+ CREATE TABLE IF NOT EXISTS "workspaces" (
+ id UUID PRIMARY KEY,
+ name TEXT UNIQUE,
+ comment TEXT,
+ created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT timezone('utc'::text, ('now'::text)::timestamp(0) with time zone),
+ meta JSON DEFAULT '{}'::json,
+ config JSON DEFAULT '{"portal":false}'::json
+ );
+
+ CREATE TABLE IF NOT EXISTS "workspace_entity_counters" (
+ workspace_id uuid,
+ entity_type text,
+ count int,
+ PRIMARY KEY(workspace_id, entity_type)
+ );
+
+ CREATE TABLE IF NOT EXISTS "rbac_users" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "rbac_roles" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "consumer_groups" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "consumer_group_plugins" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "files" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "developers" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "document_objects" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "applications" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS "application_instances" (
+ "id" UUID PRIMARY KEY,
+ "ws_id" UUID NULL
+ );
+ ]] .. mock_admin_consumer
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/006_1301_to_1500.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/006_1301_to_1500.lua
new file mode 100644
index 00000000..bf7d7952
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/006_1301_to_1500.lua
@@ -0,0 +1,12 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ postgres = {
+ up = [[ ]],
+ },
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/init.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/init.lua
new file mode 100644
index 00000000..3edb511d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/migrations/kong/enterprise_edition/db/migrations/enterprise/init.lua
@@ -0,0 +1,11 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ "000_base",
+ "006_1301_to_1500",
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_cp.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_cp.lua
new file mode 100644
index 00000000..219317b1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_cp.lua
@@ -0,0 +1,411 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local _M = {}
+
+local ws_server = require "resty.websocket.server"
+local pl_file = require "pl.file"
+local cjson = require "cjson.safe"
+local semaphore = require "ngx.semaphore"
+local gzip = require "kong.tools.gzip"
+local buffer = require "string.buffer"
+
+local shm = assert(ngx.shared.kong_test_cp_mock)
+
+local WRITER = "writer"
+local READER = "reader"
+
+---@type resty.websocket.new.opts
+local WS_OPTS = {
+ timeout = 500,
+ max_payload_len = 1024 * 1024 * 20,
+}
+
+
+---@class spec.fixtures.cluster-mock.ctx
+---
+---@field basic_info table
+---@field cancel boolean
+---@field dp table
+---@field need_pong boolean
+---@field writer_sema ngx.semaphore
+---@field ws resty.websocket.server
+---@field sent_version integer
+
+
+local function send(status, json)
+ ngx.status = status
+ ngx.print(cjson.encode(json))
+ return ngx.exit(status)
+end
+
+
+local function bad_request(err)
+ send(ngx.HTTP_BAD_REQUEST, { error = err })
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+---@param entry table
+local function emit_log_entry(ctx, entry)
+ entry.dp = ctx.dp
+ assert(shm:rpush("log", buffer.encode(entry)))
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+---@param name string
+---@param data table?
+local function log_event(ctx, name, data)
+ local evt = data or {}
+ evt.event = name
+ emit_log_entry(ctx, evt)
+end
+
+
+---@return integer
+local function get_version()
+ return shm:get("payload-version") or 0
+end
+
+
+---@return integer
+local function increment_version()
+ return assert(shm:incr("payload-version", 1, 0))
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+local function wake_writer(ctx)
+ ctx.writer_sema:post(1)
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+---@return boolean
+local function canceled(ctx)
+ return ctx.cancel or ngx.worker.exiting()
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+local function wait_writer(ctx)
+ return canceled(ctx) or ctx.writer_sema:wait(0.1)
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+---@return boolean continue
+local function get_basic_info(ctx)
+ local data, typ, err = ctx.ws:recv_frame()
+
+ if err and err:find("timeout") then
+ return true
+
+ elseif not data then
+ log_event(ctx, "client-read-error", { error = err })
+ return false
+ end
+
+ if typ == "binary" then
+ local info = cjson.decode(data)
+
+ if type(info) == "table" and info.type == "basic_info" then
+ log_event(ctx, "client-basic-info-received")
+ wake_writer(ctx)
+ ctx.basic_info = info
+ return true
+
+ else
+ log_event(ctx, "client-error",
+ { error = "client did not send proper basic info frame" })
+
+ return false
+ end
+
+ else
+ log_event(ctx, "client-error", {
+ error = "invalid pre-basic-info frame type: " .. typ,
+ })
+ return false
+ end
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+---@return boolean continue
+local function reader_recv(ctx)
+ local data, typ, err = ctx.ws:recv_frame()
+
+ if err then
+ if err:find("timeout") then
+ return true
+ end
+
+ log_event(ctx, "client-read-error", { error = err })
+ return false
+ end
+
+ log_event(ctx, "client-recv", {
+ type = typ,
+ data = data,
+ json = cjson.decode(data),
+ })
+
+ if typ == "ping" then
+ ctx.need_pong = true
+ wake_writer(ctx)
+
+ elseif typ == "close" then
+ log_event(ctx, "close", { initiator = "dp" })
+ return false
+ end
+
+ return true
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+local function read_handler(ctx)
+ while not canceled(ctx) and not ctx.basic_info do
+ if not get_basic_info(ctx) then
+ return READER
+ end
+ end
+
+ while not canceled(ctx) do
+ if not reader_recv(ctx) then
+ break
+ end
+ end
+
+ return READER
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+---@return boolean continue
+local function handle_ping(ctx)
+ if ctx.need_pong then
+ ctx.need_pong = false
+ ctx.ws:send_pong()
+ end
+
+ return true
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+---@return boolean continue
+local function send_config(ctx)
+ local version = get_version()
+
+ if version <= ctx.sent_version then
+ return true
+ end
+
+ local data = assert(shm:get("payload"))
+ local payload = gzip.deflate_gzip(data)
+
+ local ok, err = ctx.ws:send_binary(payload)
+
+ if ok then
+ log_event(ctx, "sent-config", {
+ version = version,
+ size = #data,
+ deflated_size = #payload,
+ })
+ ctx.sent_version = version
+ return true
+
+ else
+ log_event(ctx, "send-error", { error = err })
+ return false
+ end
+end
+
+
+---@param ctx spec.fixtures.cluster-mock.ctx
+local function write_handler(ctx)
+ while not ctx.basic_info and not canceled(ctx) do
+ wait_writer(ctx)
+ end
+
+ -- wait until the test driver has sent us at least one config payload
+ while get_version() < 1 and not canceled(ctx) do
+ wait_writer(ctx)
+ end
+
+ ctx.sent_version = 0
+
+ while not canceled(ctx)
+ and handle_ping(ctx)
+ and send_config(ctx)
+ do
+ wait_writer(ctx)
+ end
+
+ return WRITER
+end
+
+
+function _M.outlet()
+ local dp = {
+ id = ngx.var.arg_node_id,
+ hostname = ngx.var.arg_node_hostname,
+ ip = ngx.var.remote_addr,
+ version = ngx.var.arg_node_version,
+ }
+
+ local ctx = ngx.ctx
+ ctx.dp = dp
+
+ log_event(ctx, "connect")
+
+ local ws, err = ws_server:new(WS_OPTS)
+
+ if ws then
+ log_event(ctx, "handshake", { ok = true, err = nil })
+ else
+ log_event(ctx, "handshake", { ok = false, err = err })
+ log_event(ctx, "close", { initiator = "cp" })
+ return ngx.exit(ngx.HTTP_CLOSE)
+ end
+
+ ws:set_timeout(500)
+
+ ctx.ws = ws
+ ctx.cancel = false
+ ctx.writer_sema = semaphore.new()
+
+ local reader = ngx.thread.spawn(read_handler, ctx)
+ local writer = ngx.thread.spawn(write_handler, ctx)
+
+ local ok, err_or_result = ngx.thread.wait(reader, writer)
+
+ ctx.cancel = true
+ wake_writer(ctx)
+
+ ws:send_close()
+
+ if ok then
+ local res = err_or_result
+ local thread
+ if res == READER then
+ thread = writer
+
+ elseif res == WRITER then
+ thread = reader
+
+ else
+ error("unreachable!")
+ end
+
+ ngx.thread.wait(thread)
+ ngx.thread.kill(thread)
+
+ else
+ ngx.log(ngx.ERR, "abnormal ngx.thread.wait() status: ", err_or_result)
+ ngx.thread.kill(reader)
+ ngx.thread.kill(writer)
+ end
+
+ log_event(ctx, "exit")
+end
+
+
+function _M.set_payload()
+ ngx.req.read_body()
+
+ local body = ngx.req.get_body_data()
+ if not body then
+ local body_file = ngx.req.get_body_file()
+ if body_file then
+ body = pl_file.read(body_file)
+ end
+ end
+
+ if not body then
+ return bad_request("expected request body")
+ end
+
+ local json, err = cjson.decode(body)
+ if err then
+ return bad_request("invalid JSON: " .. tostring(err))
+ end
+
+ assert(shm:set("payload", cjson.encode(json)))
+ local version = increment_version()
+
+ return send(201, {
+ status = "created",
+ message = "updated payload",
+ version = version,
+ })
+end
+
+function _M.get_log()
+ local entries = {}
+
+ repeat
+ local data = shm:lpop("log")
+ if data then
+ table.insert(entries, buffer.decode(data))
+ end
+ until not data
+
+ send(200, { data = entries })
+end
+
+
+function _M.fixture(listen, listen_ssl)
+ return ([[
+lua_shared_dict kong_test_cp_mock 10m;
+
+server {
+ charset UTF-8;
+ server_name kong_cluster_listener;
+ listen %s;
+ listen %s ssl;
+
+ access_log ${{ADMIN_ACCESS_LOG}};
+ error_log ${{ADMIN_ERROR_LOG}} ${{LOG_LEVEL}};
+
+> if cluster_mtls == "shared" then
+ ssl_verify_client optional_no_ca;
+> else
+ ssl_verify_client on;
+ ssl_client_certificate ${{CLUSTER_CA_CERT}};
+ ssl_verify_depth 4;
+> end
+ ssl_certificate ${{CLUSTER_CERT}};
+ ssl_certificate_key ${{CLUSTER_CERT_KEY}};
+ ssl_session_cache shared:ClusterSSL:10m;
+
+ location = /v1/outlet {
+ content_by_lua_block {
+ require("spec.fixtures.mock_cp").outlet()
+ }
+ }
+
+ location = /payload {
+ content_by_lua_block {
+ require("spec.fixtures.mock_cp").set_payload()
+ }
+ }
+
+ location = /log {
+ content_by_lua_block {
+ require("spec.fixtures.mock_cp").get_log()
+ }
+ }
+}
+]]):format(listen, listen_ssl)
+end
+
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_upstream.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_upstream.lua
new file mode 100644
index 00000000..25d25b5b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_upstream.lua
@@ -0,0 +1,426 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cjson_safe = require "cjson.safe"
+local cjson = require "cjson"
+local ws_server = require "resty.websocket.server"
+local pl_file = require "pl.file"
+local strip = require("kong.tools.string").strip
+local split = require("kong.tools.string").split
+
+
+local kong = {
+ table = require("kong.pdk.table").new()
+}
+
+local ocsp_status = "good"
+
+local function parse_multipart_form_params(body, content_type)
+ if not content_type then
+ return nil, 'missing content-type'
+ end
+
+ local m, err = ngx.re.match(content_type, "boundary=(.+)", "oj")
+ if not m or not m[1] or err then
+ return nil, "could not find boundary in content type " .. content_type ..
+ "error: " .. tostring(err)
+ end
+
+ local boundary = m[1]
+ local parts_split = split(body, '--' .. boundary)
+ local params = {}
+ local part, from, to, part_value, part_name, part_headers, first_header
+ for i = 1, #parts_split do
+ part = strip(parts_split[i])
+
+ if part ~= '' and part ~= '--' then
+ from, to, err = ngx.re.find(part, '^\\r$', 'ojm')
+ if err or (not from and not to) then
+ return nil, nil, "could not find part body. Error: " .. tostring(err)
+ end
+
+ part_value = part:sub(to + 2, #part) -- +2: trim leading line jump
+ part_headers = part:sub(1, from - 1)
+ first_header = split(part_headers, '\\n')[1]
+ if first_header:lower():sub(1, 19) == "content-disposition" then
+ local m, err = ngx.re.match(first_header, 'name="(.*?)"', "oj")
+
+ if err or not m or not m[1] then
+ return nil, "could not parse part name. Error: " .. tostring(err)
+ end
+
+ part_name = m[1]
+ else
+ return nil, "could not find part name in: " .. part_headers
+ end
+
+ params[part_name] = part_value
+ end
+ end
+
+ return params
+end
+
+
+local function send_text_response(text, content_type, headers)
+ headers = headers or {}
+ content_type = content_type or "text/plain"
+
+ text = ngx.req.get_method() == "HEAD" and "" or tostring(text)
+
+ ngx.header["X-Powered-By"] = "mock_upstream"
+ ngx.header["Server"] = "mock-upstream/1.0.0"
+ ngx.header["Content-Length"] = #text + 1
+ ngx.header["Content-Type"] = content_type
+
+ for header,value in pairs(headers) do
+ if type(value) == "table" then
+ ngx.header[header] = table.concat(value, ", ")
+ else
+ ngx.header[header] = value
+ end
+ end
+
+ return ngx.say(text)
+end
+
+
+local function filter_access_by_method(method)
+ if ngx.req.get_method() ~= method then
+ ngx.status = ngx.HTTP_NOT_ALLOWED
+ send_text_response("Method not allowed for the requested URL")
+ return ngx.exit(ngx.OK)
+ end
+end
+
+
+local function find_http_credentials(authorization_header)
+ if not authorization_header then
+ return
+ end
+
+ local iterator, iter_err = ngx.re.gmatch(authorization_header,
+ "\\s*[Bb]asic\\s*(.+)")
+ if not iterator then
+ ngx.log(ngx.ERR, iter_err)
+ return
+ end
+
+ local m, err = iterator()
+
+ if err then
+ ngx.log(ngx.ERR, err)
+ return
+ end
+
+ if m and m[1] then
+ local decoded_basic = ngx.decode_base64(m[1])
+
+ if decoded_basic then
+ local user_pass = split(decoded_basic, ":")
+ return user_pass[1], user_pass[2]
+ end
+ end
+end
+
+
+local function filter_access_by_basic_auth(expected_username,
+ expected_password)
+ local headers = ngx.req.get_headers(0)
+
+ local username, password =
+ find_http_credentials(headers["proxy-authorization"])
+
+ if not username then
+ username, password =
+ find_http_credentials(headers["authorization"])
+ end
+
+ if username ~= expected_username or password ~= expected_password then
+ ngx.header["WWW-Authenticate"] = "mock_upstream"
+ ngx.header["X-Powered-By"] = "mock_upstream"
+ return ngx.exit(ngx.HTTP_UNAUTHORIZED)
+ end
+end
+
+
+local function get_ngx_vars()
+ local var = ngx.var
+ return {
+ uri = var.uri,
+ host = var.host,
+ hostname = var.hostname,
+ https = var.https,
+ scheme = var.scheme,
+ is_args = var.is_args,
+ server_addr = var.server_addr,
+ server_port = var.server_port,
+ server_name = var.server_name,
+ server_protocol = var.server_protocol,
+ remote_addr = var.remote_addr,
+ remote_port = var.remote_port,
+ realip_remote_addr = var.realip_remote_addr,
+ realip_remote_port = var.realip_remote_port,
+ binary_remote_addr = var.binary_remote_addr,
+ request = var.request,
+ request_uri = var.request_uri,
+ request_time = var.request_time,
+ request_length = var.request_length,
+ request_method = var.request_method,
+ bytes_received = var.bytes_received,
+ ssl_server_name = var.ssl_server_name or "no SNI",
+ }
+end
+
+
+local function get_body_data()
+ local req = ngx.req
+
+ req.read_body()
+ local data = req.get_body_data()
+ if data then
+ return data
+ end
+
+ local file_path = req.get_body_file()
+ if file_path then
+ local file = io.open(file_path, "r")
+ data = file:read("*all")
+ file:close()
+ return data
+ end
+
+ return ""
+end
+
+local function get_post_data(content_type)
+ local text = get_body_data()
+ local kind = "unknown"
+ local params = cjson_safe.null
+ local err
+
+ if type(content_type) == "string" then
+ if content_type:find("application/x-www-form-urlencoded", nil, true) then
+
+ kind = "form"
+ params, err = ngx.req.get_post_args(0)
+
+ elseif content_type:find("multipart/form-data", nil, true) then
+ kind = "multipart-form"
+ params, err = parse_multipart_form_params(text, content_type)
+
+ elseif content_type:find("application/json", nil, true) then
+ kind = "json"
+ params, err = cjson_safe.decode(text)
+ end
+
+ params = params or cjson_safe.null
+
+ if err then
+ kind = kind .. " (error)"
+ err = tostring(err)
+ end
+ end
+
+ return { text = text, kind = kind, params = params, error = err }
+end
+
+
+local function get_default_json_response()
+ local headers = ngx.req.get_headers(0)
+ local vars = get_ngx_vars()
+
+ return {
+ headers = headers,
+ post_data = get_post_data(headers["Content-Type"]),
+ url = ("%s://%s:%s%s"):format(vars.scheme, vars.host,
+ vars.server_port, vars.request_uri),
+ uri_args = ngx.req.get_uri_args(0),
+ vars = vars,
+ }
+end
+
+
+local function send_default_json_response(extra_fields, response_headers)
+ local tbl = kong.table.merge(get_default_json_response(), extra_fields)
+ return send_text_response(cjson.encode(tbl),
+ "application/json", response_headers)
+end
+
+
+local function serve_web_sockets()
+ local wb, err = ws_server:new({
+ timeout = 5000,
+ max_payload_len = 65535,
+ })
+
+ if not wb then
+ ngx.log(ngx.ERR, "failed to open websocket: ", err)
+ return ngx.exit(444)
+ end
+
+ while true do
+ local data, typ, err = wb:recv_frame()
+ if wb.fatal then
+ ngx.log(ngx.ERR, "failed to receive frame: ", err)
+ return ngx.exit(444)
+ end
+
+ if data then
+ if typ == "close" then
+ break
+ end
+
+ if typ == "ping" then
+ local bytes, err = wb:send_pong(data)
+ if not bytes then
+ ngx.log(ngx.ERR, "failed to send pong: ", err)
+ return ngx.exit(444)
+ end
+
+ elseif typ == "pong" then
+ ngx.log(ngx.INFO, "client ponged")
+
+ elseif typ == "text" then
+ local bytes, err = wb:send_text(data)
+ if not bytes then
+ ngx.log(ngx.ERR, "failed to send text: ", err)
+ return ngx.exit(444)
+ end
+ end
+
+ else
+ local bytes, err = wb:send_ping()
+ if not bytes then
+ ngx.log(ngx.ERR, "failed to send ping: ", err)
+ return ngx.exit(444)
+ end
+ end
+ end
+
+ wb:send_close()
+end
+
+
+local function get_logger()
+ local logger = ngx.shared.kong_mock_upstream_loggers
+ if not logger then
+ error("missing 'kong_mock_upstream_loggers' shm declaration")
+ end
+
+ return logger
+end
+
+
+local function store_log(logname)
+ ngx.req.read_body()
+
+ local raw_entries = ngx.req.get_body_data()
+ local logger = get_logger()
+
+ local entries = cjson.decode(raw_entries)
+ if #entries == 0 then
+ -- backwards-compatibility for `conf.queue_size == 1`
+ entries = { entries }
+ end
+
+ local log_req_params = ngx.req.get_uri_args()
+ local log_req_headers = ngx.req.get_headers(0)
+
+ for i = 1, #entries do
+ local store = {
+ entry = entries[i],
+ log_req_headers = log_req_headers,
+ log_req_params = log_req_params,
+ }
+
+ assert(logger:rpush(logname, cjson.encode(store)))
+ assert(logger:incr(logname .. "|count", 1, 0))
+ end
+
+ ngx.status = 200
+end
+
+
+local function retrieve_log(logname)
+ local logger = get_logger()
+ local len = logger:llen(logname)
+ local entries = {}
+
+ for i = 1, len do
+ local encoded_stored = assert(logger:lpop(logname))
+ local stored = cjson.decode(encoded_stored)
+ entries[i] = stored.entry
+ entries[i].log_req_headers = stored.log_req_headers
+ entries[i].log_req_params = stored.log_req_params
+ assert(logger:rpush(logname, encoded_stored))
+ end
+
+ local count, err = logger:get(logname .. "|count")
+ if err then
+ error(err)
+ end
+
+ ngx.status = 200
+ ngx.say(cjson.encode({
+ entries = entries,
+ count = count,
+ }))
+end
+
+
+local function count_log(logname)
+ local logger = get_logger()
+ local count = assert(logger:get(logname .. "|count"))
+
+ ngx.status = 200
+ ngx.say(count)
+end
+
+
+local function reset_log(logname)
+ local logger = get_logger()
+ logger:delete(logname)
+ logger:delete(logname .. "|count")
+end
+
+
+local function handle_ocsp()
+ if ocsp_status == "good" then
+ ngx.print(pl_file.read(ngx.config.prefix() .. "/../spec/fixtures/ocsp_certs/resp-good.dat"))
+
+ elseif ocsp_status == "revoked" then
+ ngx.print(pl_file.read(ngx.config.prefix() .. "/../spec/fixtures/ocsp_certs/resp-revoked.dat"))
+
+ elseif ocsp_status == "error" then
+ ngx.exit(500)
+
+ else
+ assert("unknown ocsp_status:" ..ocsp_status)
+ end
+end
+
+
+local function set_ocsp(status)
+ ocsp_status = status
+end
+
+
+return {
+ get_default_json_response = get_default_json_response,
+ filter_access_by_method = filter_access_by_method,
+ filter_access_by_basic_auth = filter_access_by_basic_auth,
+ send_text_response = send_text_response,
+ send_default_json_response = send_default_json_response,
+ serve_web_sockets = serve_web_sockets,
+ store_log = store_log,
+ retrieve_log = retrieve_log,
+ count_log = count_log,
+ reset_log = reset_log,
+ handle_ocsp = handle_ocsp,
+ set_ocsp = set_ocsp,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_webserver_tpl.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_webserver_tpl.lua
new file mode 100644
index 00000000..d0f1d207
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mock_webserver_tpl.lua
@@ -0,0 +1,267 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return [[daemon on;
+worker_processes ${worker_num};
+error_log ${base_path}/${logs_dir}/error.log info;
+pid ${base_path}/${logs_dir}/nginx.pid;
+worker_rlimit_nofile 8192;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ lua_shared_dict server_values 512k;
+ lua_shared_dict logs 512k;
+ lua_shared_dict log_locks 512k;
+
+ init_worker_by_lua_block {
+ local resty_lock = require "resty.lock"
+ _G.log_locks = resty_lock:new("log_locks")
+
+ _G.log_record = function(ngx_req)
+ local cjson = require("cjson")
+ local args, err = ngx_req.get_uri_args(0)
+ local key = args['key'] or "default"
+ local log_locks = _G.log_locks
+
+ if err then
+ return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
+ end
+
+ log_locks:lock("lock")
+
+ local logs = ngx.shared.logs:get(key) or "[]"
+
+ if not args['do_not_log'] then
+ local log = {
+ time = ngx.now(),
+ -- path = "/log",
+ method = ngx_req.get_method(),
+ headers = ngx_req.get_headers(0),
+ }
+
+ logs = cjson.decode(logs)
+ table.insert(logs, log)
+ logs = cjson.encode(logs)
+ ngx.shared.logs:set(key, logs)
+ end
+
+ log_locks:unlock()
+
+ return logs
+ end
+
+ local server_values = ngx.shared.server_values
+# for _, prefix in ipairs(hosts) do
+ if server_values:get("$(prefix)_healthy") == nil then
+ server_values:set("$(prefix)_healthy", true)
+ ngx.log(ngx.INFO, "Creating entries for $(prefix)_healthy")
+ end
+
+ if server_values:get("$(prefix)_timeout") == nil then
+ server_values:set("$(prefix)_timeout", false)
+ ngx.log(ngx.INFO, "Creating entries for $(prefix)_timeout")
+ end
+# end
+ }
+
+ default_type application/json;
+ access_log ${base_path}/${logs_dir}/access.log;
+ sendfile on;
+ tcp_nopush on;
+ server_names_hash_bucket_size 128;
+
+ server {
+# if protocol ~= 'https' then
+ listen 127.0.0.1:${http_port};
+# if not disable_ipv6 then
+ listen [::1]:${http_port};
+#end
+# else
+ listen 127.0.0.1:${http_port} ssl;
+# if not disable_ipv6 then
+ listen [::1]:${http_port} ssl;
+#end
+ http2 on;
+
+ ssl_certificate ${cert_path}/kong_spec.crt;
+ ssl_certificate_key ${cert_path}/kong_spec.key;
+ ssl_protocols TLSv1.2;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+#end
+# if check_hostname then
+ server_name ${host};
+#end
+
+ location = /clear_log {
+ content_by_lua_block {
+ local log_locks = _G.log_locks
+ log_locks:lock("lock")
+ ngx.shared.logs:flush_all()
+ log_locks:unlock()
+ ngx.say("cleared")
+ }
+ }
+
+ location = /log {
+ content_by_lua_block {
+ ngx.say(_G.log_record(ngx.req))
+ }
+ }
+
+ location = /always_200 {
+ content_by_lua_block {
+ ngx.say("ok")
+ return ngx.exit(ngx.HTTP_OK)
+ }
+ }
+
+ location = /healthy {
+ access_by_lua_block {
+ local host = ngx.req.get_headers(0)["host"] or "localhost"
+ local host_no_port = ngx.re.match(host, [=[([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\])(:?[0-9]+)*]=])
+ if host_no_port == nil then
+ return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
+ else
+ host = host_no_port[2]
+ if host == "[0000:0000:0000:0000:0000:0000:0000:0001]" then
+ host = "[::1]"
+ end
+ end
+ ngx.shared.server_values:set(host .. "_healthy", true)
+ ngx.shared.server_values:set(host .. "_timeout", false)
+ ngx.log(ngx.INFO, "Host ", host, " is now healthy")
+ }
+
+ content_by_lua_block {
+ ngx.say("server ", ngx.var.server_name, " is now healthy")
+ return ngx.exit(ngx.HTTP_OK)
+ }
+ }
+
+ location = /unhealthy {
+ access_by_lua_block {
+ local host = ngx.req.get_headers(0)["host"] or "localhost"
+ local host_no_port = ngx.re.match(host, [=[([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\])(:?[0-9]+)*]=])
+ if host_no_port == nil then
+ return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
+ else
+ host = host_no_port[2]
+ if host == "[0000:0000:0000:0000:0000:0000:0000:0001]" then
+ host = "[::1]"
+ end
+ end
+ ngx.shared.server_values:set(host .. "_healthy", false)
+ ngx.log(ngx.INFO, "Host ", host, " is now unhealthy")
+ }
+
+ content_by_lua_block {
+ ngx.say("server ", ngx.var.server_name, " is now unhealthy")
+ return ngx.exit(ngx.HTTP_OK)
+ }
+ }
+
+ location = /timeout {
+ access_by_lua_block {
+ local host = ngx.req.get_headers()["host"] or "localhost"
+ local host_no_port = ngx.re.match(host, [=[([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\])(:?[0-9]+)*]=])
+ if host_no_port == nil then
+ return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
+ else
+ host = host_no_port[2]
+ if host == "[0000:0000:0000:0000:0000:0000:0000:0001]" then
+ host = "[::1]"
+ end
+ end
+ ngx.shared.server_values:set(host .. "_timeout", true)
+ ngx.log(ngx.INFO, "Host ", host, " is timeouting now")
+ }
+
+ content_by_lua_block {
+ ngx.say("server ", ngx.var.server_name, " is timeouting now")
+ return ngx.exit(ngx.HTTP_OK)
+ }
+ }
+
+ location = /status {
+ access_by_lua_block {
+ _G.log_record(ngx.req)
+ local i = require 'inspect'
+ ngx.log(ngx.ERR, "INSPECT status (headers): ", i(ngx.req.get_headers(0)))
+ local host = ngx.req.get_headers(0)["host"] or "localhost"
+ local host_no_port = ngx.re.match(host, [=[([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\])(:?[0-9]+)*]=])
+ if host_no_port == nil then
+ return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
+ else
+ host = host_no_port[2]
+ if host == "[0000:0000:0000:0000:0000:0000:0000:0001]" then
+ host = "[::1]"
+ end
+ end
+ local server_values = ngx.shared.server_values
+
+ local status = server_values:get(host .. "_healthy") and
+ ngx.HTTP_OK or ngx.HTTP_INTERNAL_SERVER_ERROR
+
+ if server_values:get(host .. "_timeout") == true then
+ ngx.log(ngx.INFO, "Host ", host, " timeouting...")
+ ngx.log(ngx.INFO, "[COUNT] status 599")
+ ngx.sleep(4)
+ else
+ ngx.log(ngx.INFO, "[COUNT] status ", status)
+ end
+
+ ngx.exit(status)
+ }
+ }
+
+ location / {
+ access_by_lua_block {
+ _G.log_record(ngx.req)
+ local cjson = require("cjson")
+ local server_values = ngx.shared.server_values
+ local host = ngx.req.get_headers(0)["host"] or "localhost"
+ local host_no_port = ngx.re.match(host, [=[([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\])(:?[0-9]+)*]=])
+ if host_no_port == nil then
+ return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
+ else
+ host = host_no_port[2]
+ if host == "[0000:0000:0000:0000:0000:0000:0000:0001]" then
+ host = "[::1]"
+ end
+ end
+ local status
+
+ local status = server_values:get(host .. "_healthy") and
+ ngx.HTTP_OK or ngx.HTTP_INTERNAL_SERVER_ERROR
+
+ if server_values:get(host .. "_timeout") == true then
+ -- not this status actually, but it is used to count failures
+ ngx.log(ngx.INFO, "[COUNT] slash 599")
+ ngx.sleep(4)
+ else
+ ngx.log(ngx.INFO, "[COUNT] slash ", status)
+ end
+
+ ngx.sleep(${delay}/1000)
+ ngx.exit(status)
+ }
+ }
+ }
+# if check_hostname then
+ server {
+ listen 127.0.0.1:${http_port} default_server;
+ listen [::1]:${http_port} default_server;
+ server_name _;
+ return 400;
+ }
+# end
+
+}
+]]
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocker.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocker.lua
new file mode 100644
index 00000000..2080c57d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocker.lua
@@ -0,0 +1,68 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local mocker = {}
+
+-- Setup mocks, which are undone in a finally() block
+-- @param finally The `finally` function, that needs to be passed in because
+-- Busted generates it dynamically.
+-- @param args A table containing three optional fields:
+-- * modules: an array of pairs (module name, module content).
+-- This allows modules to be declared in order.
+-- * kong: a mock of the kong global (which will fallback to the default one
+-- via metatable)
+-- * ngx: a mock of the ngx global (which will fallback to the default one
+-- via metatable)
+function mocker.setup(finally, args)
+
+ local mocked_modules = {}
+ local _ngx = _G.ngx
+ local _kong = _G.kong
+
+ local function mock_module(name, tbl)
+ local old_module = require(name)
+ mocked_modules[name] = true
+ package.loaded[name] = setmetatable(tbl or {}, {
+ __index = old_module,
+ })
+ end
+
+ if args.ngx then
+ _G.ngx = setmetatable(args.ngx, { __index = _ngx })
+ end
+
+ if args.kong then
+ _G.kong = setmetatable(args.kong, { __index = _kong })
+ end
+
+ if args.modules then
+ for _, pair in ipairs(args.modules) do
+ mock_module(pair[1], pair[2])
+ end
+ end
+
+ finally(function()
+ _G.ngx = _ngx
+ _G.kong = _kong
+
+ for k in pairs(mocked_modules) do
+ package.loaded[k] = nil
+ end
+ end)
+end
+
+
+function mocker.table_where_every_key_returns(value)
+ return setmetatable({}, {
+ __index = function()
+ return value
+ end
+ })
+end
+
+
+return mocker
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/T1-1748.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/T1-1748.json
new file mode 100644
index 00000000..fdaff9b7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/T1-1748.json
@@ -0,0 +1,357 @@
+{
+ "openapi": "3.0.2",
+ "info": {
+ "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "termsOfService": "http://swagger.io/terms/",
+ "contact": {
+ "email": "apiteam@swagger.io"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "servers": [{
+ "url": "http://petstore.swagger.io/v2"
+ }],
+ "tags": [{
+ "name": "pet",
+ "description": "Everything about your Pets",
+ "externalDocs": {
+ "description": "Find out more",
+ "url": "http://swagger.io"
+ }
+ }],
+ "paths": {
+ "/pet/findByStatus/MultipleExamples": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings",
+ "operationId": "findPetsByStatus",
+ "produces": ["application/xml", "application/json"],
+ "parameters": [{
+ "name": "status",
+ "in": "query",
+ "description": "Status values that need to be considered for filter",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["available", "pending", "sold"],
+ "default": "available"
+ }
+ },
+ "style": "form",
+ "explode": true,
+ "examples": {
+ "Available": {
+ "summary": "Available",
+ "description": "Showing status of `available`, using `value` property",
+ "value": "available"
+ },
+ "Sold": {
+ "summary": "Sold",
+ "description": "Showing status of `sold`, using `externalValue` property",
+ "externalValue": "http://example.com/examples/dog.json"
+ }
+ }
+ }],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "examples": {
+ "Issue1": {
+ "value": {
+ "issueCode": "example issue code",
+ "reference": "example reference"
+ }
+ },
+ "Issue2": {
+ "value": {
+ "issueCode": "example issue 2",
+ "reference": "example reference 2"
+ }
+ }
+ }
+ }
+ }
+
+
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [{
+ "petstore_auth": ["write:pets", "read:pets"]
+ }]
+ }
+ },
+ "/pet/findByStatus/singleExample": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings",
+ "operationId": "findPetsByStatus",
+ "produces": ["application/xml", "application/json"],
+ "parameters": [{
+ "name": "status",
+ "in": "query",
+ "description": "Status values that need to be considered for filter",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["available", "pending", "sold"],
+ "default": "available"
+ }
+ },
+ "style": "form",
+ "explode": true,
+ "example": {
+ "summary": "Available",
+ "description": "Showing status of `available`, using `value` property",
+ "value": "available"
+ }
+ }],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "example": {
+ "summary": "Example response showing a regular response",
+ "description": "Two pets are returned in this example.",
+ "value": [{
+ "id": 1,
+ "category": {
+ "id": 1,
+ "name": "cat"
+ },
+ "name": "fluffy",
+ "photoUrls": [
+ "http://example.com/path/to/cat/1.jpg",
+ "http://example.com/path/to/cat/2.jpg"
+ ],
+ "tags": [{
+ "id": 1,
+ "name": "cat"
+ }],
+ "status": "available"
+ }, {
+ "id": 2,
+ "category": {
+ "id": 2,
+ "name": "dog"
+ },
+ "name": "puppy",
+ "photoUrls": [
+ "http://example.com/path/to/dog/1.jpg"
+ ],
+ "tags": [{
+ "id": 2,
+ "name": "dog"
+ }],
+ "status": "available"
+ }]
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [{
+ "petstore_auth": ["write:pets", "read:pets"]
+ }]
+ }
+ },
+ "/pet": {
+ "post": {
+ "tags": ["pet"],
+ "summary": "Add a new pet to the store",
+ "description": "",
+ "operationId": "addPet",
+ "requestBody": {
+ "description": "Pet object that needs to be added to the store",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ },
+ "examples": {
+ "Cat": {
+ "summary": "An example of cat",
+ "description": "An example of cat, using `value` property",
+ "value": {
+ "id": 1,
+ "category": {
+ "id": 1,
+ "name": "cat"
+ },
+ "name": "fluffy",
+ "photoUrls": [
+ "http://example.com/path/to/cat/1.jpg",
+ "http://example.com/path/to/cat/2.jpg"
+ ],
+ "tags": [{
+ "id": 1,
+ "name": "cat"
+ }],
+ "status": "available"
+ }
+ },
+ "Cat2": {
+ "summary": "An example of cat",
+ "description": "An example of cat, using `value` property, which value is an array",
+ "value": [{
+ "id": 1,
+ "category": {
+ "id": 1,
+ "name": "cat"
+ },
+ "name": "fluffy",
+ "photoUrls": [
+ "http://example.com/path/to/cat/1.jpg",
+ "http://example.com/path/to/cat/2.jpg"
+ ],
+ "tags": [{
+ "id": 1,
+ "name": "cat"
+ }],
+ "status": "available"
+ }]
+ },
+ "Dog": {
+ "summary": "An example of dog",
+ "description": "An example of dog, using `externalValue` property",
+ "externalValue": "http://example.com/examples/dog.json"
+ }
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ },
+ "examples": {
+ "Cat": {
+ "summary": "An example of cat",
+ "description": "An example of cat, using `value` property",
+ "value": " "
+ },
+ "Dog": {
+ "summary": "An example of dog",
+ "description": "An example of dog, using `externalValue` property",
+ "externalValue": "http://example.com/examples/dog.xml"
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Category": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Category"
+ }
+ },
+ "Tag": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Tag"
+ }
+ },
+ "Pet": {
+ "type": "object",
+ "required": ["name", "photoUrls"],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "category": {
+ "$ref": "#/components/schemas/Category"
+ },
+ "name": {
+ "type": "string",
+ "example": "doggie"
+ },
+ "photoUrls": {
+ "type": "array",
+ "xml": {
+ "name": "photoUrl",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string"
+ }
+ },
+ "tags": {
+ "type": "array",
+ "xml": {
+ "name": "tag",
+ "wrapped": true
+ },
+ "items": {
+ "$ref": "#/components/schemas/Tag"
+ }
+ },
+ "status": {
+ "type": "string",
+ "description": "pet status in the store",
+ "enum": ["available", "pending", "sold"]
+ }
+ },
+ "xml": {
+ "name": "Pet"
+ }
+ }
+ }
+ },
+ "externalDocs": {
+ "description": "Find out more about Swagger",
+ "url": "http://swagger.io"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/fhir.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/fhir.json
new file mode 100644
index 00000000..9a5ff7e7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/fhir.json
@@ -0,0 +1,308 @@
+ {
+ "swagger": "2.0",
+ "info": {
+ "title": "FHIR Patient",
+ "description": "Demographics and other administrative information about an individual or animal receiving care or other health-related services. For more information: https://www.hl7.org/fhir/patient.html",
+ "version": "2.1"
+ },
+ "host": "localhost:8000",
+ "basePath": "/v2",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/Patient/{id}": {
+ "get": {
+ "description": "",
+ "operationId": "GET /Patient/{id}",
+ "produces": [
+ "application/json+fhir",
+ "application/xml+fhir"
+ ],
+ "tags": [
+ "Patient"
+ ],
+ "parameters": [{
+ "required": true,
+ "in": "path",
+ "name": "id",
+ "type": "string"
+ }],
+ "responses": {
+ "200": {
+ "description": "Status 200",
+ "examples": {
+ "application/json+fhir": {
+ "resourceType": "Patient",
+ "identifier": [{
+ "use": "official",
+ "value": "1015617902"
+ }],
+ "active": true,
+ "name": [{
+ "use": "official",
+ "family": "Sehl",
+ "given": [
+ "Vivien"
+ ]
+ }],
+ "gender": "male",
+ "birthDate": "1946-05-11",
+ "telecom": [{
+ "system": "email",
+ "use": "home",
+ "value": "dynamic.extras2@medibank.com.au"
+ },
+ {
+ "system": "phone",
+ "use": "mobile",
+ "value": "99999"
+ }
+ ],
+ "address": [{
+ "use": "home",
+ "type": "physical",
+ "text": "720 Bourke Street, Medibank, Docklands, Queensland - 4074 ",
+ "line": [
+ "720 Bourke Street",
+ "",
+ "Medibank"
+ ],
+ "city": "Docklands",
+ "state": "Queensland",
+ "postalCode": "4074"
+ }]
+ }
+ }
+ },
+ "404": {
+ "description": "Tried to get an unknown resource\n"
+ },
+ "410": {
+ "description": "Tried to get a deleted resource\n"
+ },
+ "422": {
+ "description": "Unprocessable Entity - the proposed resource violated applicable FHIR profiles or server business rules. This should be accompanied by an OperationOutcome resource providing additional detail.\n"
+ }
+ }
+ },
+ "put": {
+ "description": "Update an existing instance\n",
+ "operationId": "PUT /Patient/{id}",
+ "consumes": [
+ "application/json+fhir",
+ "application/xml+fhir"
+ ],
+ "produces": [
+ "application/json+fhir",
+ "application/xml+fhir"
+ ],
+ "parameters": [{
+ "required": true,
+ "in": "path",
+ "name": "id",
+ "type": "string"
+ }],
+ "tags": [
+ "Patient"
+ ],
+ "responses": {
+ "200": {
+ "description": "Succesfully updated the instance \n"
+ },
+ "201": {
+ "description": "Succesfully created the instance \n"
+ },
+ "400": {
+ "description": "Bad Request - Resource cound not be parsed or failed basic FHIR validation rules\n"
+ },
+ "404": {
+ "description": "Not Found - resource type not support, or not a FHIR validation rules\n"
+ },
+ "405": {
+ "description": "Method Not allowed - the resource did not exist prior to the update, and the server does not allow client defined ids\n"
+ },
+ "409": {
+ "description": "Version conflict management\n"
+ },
+ "412": {
+ "description": "Version conflict management\n"
+ },
+ "422": {
+ "description": "Unprocessable Entity - the proposed resource violated applicable FHIR profiles or server business rules. This should be accompanied by an OperationOutcome resource providing additional detail.\n"
+ }
+ }
+ },
+ "delete": {
+ "description": "Delete resource\n",
+ "operationId": "DELETE /Patient/{id}",
+ "produces": [
+ "application/json+fhir",
+ "application/xml+fhir"
+ ],
+ "parameters": [{
+ "required": true,
+ "in": "path",
+ "name": "id",
+ "type": "string"
+ }],
+ "tags": [
+ "Patient"
+ ],
+ "responses": {
+ "204": {
+ "description": "Succesfully deleted resource\n"
+ },
+ "404": {
+ "description": "Not Found - resource was not found\n"
+ },
+ "405": {
+ "description": "Method Not allowed - delete is not allowed\n"
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "Patient": {
+ "properties": {
+ "resourceType": {
+ "type": "string",
+ "example": "Patient"
+ },
+ "identifier": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "use": {
+ "type": "string",
+ "example": "official"
+ },
+ "value": {
+ "type": "string",
+ "example": "1015617902"
+ }
+ }
+ }
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "name": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "use": {
+ "type": "string",
+ "example": "official"
+ },
+ "family": {
+ "type": "string",
+ "example": "Sehl"
+ },
+ "given": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "example": "Vivien"
+ }
+ }
+ }
+ }
+ },
+ "gender": {
+ "type": "string",
+ "example": "male"
+ },
+ "birthDate": {
+ "type": "string",
+ "format": "date",
+ "example": "1946-05-11"
+ },
+ "telecom": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "system": {
+ "type": "string",
+ "example": "email"
+ },
+ "use": {
+ "type": "string",
+ "example": "home"
+ },
+ "value": {
+ "type": "string",
+ "example": "dynamic.extras2@medibank.com.au"
+ }
+ }
+ }
+ },
+ "address": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "use": {
+ "type": "string",
+ "example": "home"
+ },
+ "type": {
+ "type": "string",
+ "example": "physical"
+ },
+ "text": {
+ "type": "string",
+ "example": "720 Bourke Street, Medibank, Docklands, Queensland - 4074"
+ },
+ "line": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "example": "720 Bourke Street"
+ }
+ },
+ "city": {
+ "type": "string",
+ "example": "Docklands"
+ },
+ "state": {
+ "type": "string",
+ "example": "Queensland"
+ },
+ "postalCode": {
+ "type": "string",
+ "example": "4074"
+ }
+ }
+ }
+ }
+ }
+ },
+ "Error": {
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/multipleexamples.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/multipleexamples.json
new file mode 100644
index 00000000..26b3f1ae
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/multipleexamples.json
@@ -0,0 +1,371 @@
+{
+ "openapi": "3.0.2",
+ "info": {
+ "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
+ "version": "1.0.0",
+ "title": "Swagger Petstore",
+ "termsOfService": "http://swagger.io/terms/",
+ "contact": {
+ "email": "apiteam@swagger.io"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "servers": [{
+ "url": "http://petstore.swagger.io/v2"
+ }],
+ "tags": [{
+ "name": "pet",
+ "description": "Everything about your Pets",
+ "externalDocs": {
+ "description": "Find out more",
+ "url": "http://swagger.io"
+ }
+ }],
+ "paths": {
+ "/pet/findByStatus/MultipleExamples": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings",
+ "operationId": "findPetsByStatus",
+ "produces": ["application/xml", "application/json"],
+ "parameters": [{
+ "name": "status",
+ "in": "query",
+ "description": "Status values that need to be considered for filter",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["available", "pending", "sold"],
+ "default": "available"
+ }
+ },
+ "style": "form",
+ "explode": true,
+ "examples": {
+ "Available": {
+ "summary": "Available",
+ "description": "Showing status of `available`, using `value` property",
+ "value": "available"
+ },
+ "Sold": {
+ "summary": "Sold",
+ "description": "Showing status of `sold`, using `externalValue` property",
+ "externalValue": "http://example.com/examples/dog.json"
+ }
+ }
+ }],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "examples": {
+ "No Content": {
+ "summary": "Example response showing no pets are matched",
+ "description": "An example response, using `value` property",
+ "value": []
+ },
+ "Example 1": {
+ "summary": "Example response showing a regular response",
+ "description": "Two pets are returned in this example.",
+ "value": [{
+ "id": 1,
+ "category": {
+ "id": 1,
+ "name": "cat"
+ },
+ "nickname": "fluffy",
+ "photoUrls": [
+ "http://example.com/path/to/cat/1.jpg",
+ "http://example.com/path/to/cat/2.jpg"
+ ],
+ "tags": [{
+ "id": 1,
+ "name": "cat"
+ }],
+ "status": "available"
+ }, {
+ "id": 2,
+ "category": {
+ "id": 2,
+ "name": "dog"
+ },
+ "nickname": "puppy",
+ "photoUrls": [
+ "http://example.com/path/to/dog/1.jpg"
+ ],
+ "tags": [{
+ "id": 2,
+ "name": "dog"
+ }],
+ "status": "available"
+ }]
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [{
+ "petstore_auth": ["write:pets", "read:pets"]
+ }]
+ }
+ },
+ "/pet/findByStatus/singleExample": {
+ "get": {
+ "tags": ["pet"],
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings",
+ "operationId": "findPetsByStatus",
+ "produces": ["application/xml", "application/json"],
+ "parameters": [{
+ "name": "status",
+ "in": "query",
+ "description": "Status values that need to be considered for filter",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["available", "pending", "sold"],
+ "default": "available"
+ }
+ },
+ "style": "form",
+ "explode": true,
+ "example": {
+ "summary": "Available",
+ "description": "Showing status of `available`, using `value` property",
+ "value": "available"
+ }
+ }],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "example": {
+ "id": 1,
+ "category": {
+ "id": 1,
+ "name": "cat"
+ },
+ "nickname": "fluffy",
+ "photoUrls": [
+ "http://example.com/path/to/cat/1.jpg",
+ "http://example.com/path/to/cat/2.jpg"
+ ],
+ "tags": [{
+ "id": 1,
+ "name": "cat"
+ }],
+ "status": "available"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [{
+ "petstore_auth": ["write:pets", "read:pets"]
+ }]
+ }
+ },
+ "/pet": {
+ "post": {
+ "tags": ["pet"],
+ "summary": "Add a new pet to the store",
+ "description": "",
+ "operationId": "addPet",
+ "requestBody": {
+ "description": "Pet object that needs to be added to the store",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ },
+ "examples": {
+ "Cat": {
+ "summary": "An example of cat",
+ "description": "An example of cat, using `value` property",
+ "value": {
+ "id": 1,
+ "category": {
+ "id": 1,
+ "name": "cat"
+ },
+ "name": "fluffy",
+ "photoUrls": [
+ "http://example.com/path/to/cat/1.jpg",
+ "http://example.com/path/to/cat/2.jpg"
+ ],
+ "tags": [{
+ "id": 1,
+ "name": "cat"
+ }],
+ "status": "available"
+ }
+ },
+ "Cat2": {
+ "summary": "An example of cat",
+ "description": "An example of cat, using `value` property, which value is an array",
+ "value": [{
+ "id": 1,
+ "category": {
+ "id": 1,
+ "name": "cat"
+ },
+ "name": "fluffy",
+ "photoUrls": [
+ "http://example.com/path/to/cat/1.jpg",
+ "http://example.com/path/to/cat/2.jpg"
+ ],
+ "tags": [{
+ "id": 1,
+ "name": "cat"
+ }],
+ "status": "available"
+ }]
+ },
+ "Dog": {
+ "summary": "An example of dog",
+ "description": "An example of dog, using `externalValue` property",
+ "externalValue": "http://example.com/examples/dog.json"
+ }
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ },
+ "examples": {
+ "Cat": {
+ "summary": "An example of cat",
+ "description": "An example of cat, using `value` property",
+ "value": " "
+ },
+ "Dog": {
+ "summary": "An example of dog",
+ "description": "An example of dog, using `externalValue` property",
+ "externalValue": "http://example.com/examples/dog.xml"
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Category": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Category"
+ }
+ },
+ "Tag": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Tag"
+ }
+ },
+ "Pet": {
+ "type": "object",
+ "required": ["name", "photoUrls"],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "category": {
+ "$ref": "#/components/schemas/Category"
+ },
+ "name": {
+ "type": "string",
+ "example": "doggie"
+ },
+ "photoUrls": {
+ "type": "array",
+ "xml": {
+ "name": "photoUrl",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string"
+ }
+ },
+ "tags": {
+ "type": "array",
+ "xml": {
+ "name": "tag",
+ "wrapped": true
+ },
+ "items": {
+ "$ref": "#/components/schemas/Tag"
+ }
+ },
+ "status": {
+ "type": "string",
+ "description": "pet status in the store",
+ "enum": ["available", "pending", "sold"]
+ }
+ },
+ "xml": {
+ "name": "Pet"
+ }
+ }
+ }
+ },
+ "externalDocs": {
+ "description": "Find out more about Swagger",
+ "url": "http://swagger.io"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openApi3.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openApi3.yaml
new file mode 100644
index 00000000..f5089c6e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openApi3.yaml
@@ -0,0 +1,1045 @@
+openapi: 3.0.0
+# Added by API Auto Mocking Plugin
+servers:
+ - description: SwaggerHub API Auto Mocking
+ url: https://virtserver.swaggerhub.com/rosireddy2020/CUOOrder/1.0.0
+info:
+ description: This is a simple API
+ version: "1.0.0"
+ title: CUO_Order
+ contact:
+ email: you@your-company.com
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+tags:
+
+ - name: login
+ description: order login api
+ - name: user
+ description: login user api
+ - name: location
+ description: Location API for eCUo orders
+ - name: orderEntry
+ description: order entry/editing APi for eCUO orders
+ - name: standingorder
+ description: Standing orders view and Activation for eCUO orders
+ - name: commitedAmounts
+ description: Commited amount Api for eCUO orders
+ - name: pickList
+ description: PickList Api for eCUO orders
+ - name: orderprint
+ description: Orderprint Api for eCUO orders
+ - name: manifest
+ description: Manifest Api for eCUO orders
+paths:
+ /login:
+ post:
+ tags:
+ - login
+ summary: find user is valid or not
+ operationId: login
+ requestBody:
+ description: login input deatails
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LoginRequest'
+ required: true
+
+ responses:
+ '204':
+ description: login user is valid
+ '400':
+ description: Invalid login user name
+ /adLogin:
+ post:
+ tags:
+ - login
+ summary: find ad user is valid or not
+ operationId: adLogin
+ requestBody:
+ description: Ad login input deatails
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LoginRequest'
+ required: true
+
+ responses:
+ '204':
+ description: Ad name is valid
+ '400':
+ description: Invalid Ad name
+ /locations:
+ post:
+ tags:
+ - location
+ summary: find all locations by login details
+ operationId: getLocationsForOrders
+ requestBody:
+ description: Input details for locations
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EcuoLoginBean'
+ required: true
+
+ responses:
+ "200":
+ description: Location deatails featched successfully
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/LocationEnqResponseBean'
+ '412':
+ description: Invalid Input
+ /location:
+ post:
+ tags:
+ - location
+ summary: find New Order confiuration deatils by location
+ operationId: validateLocation
+ requestBody:
+ description: Input Valid location details
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/EcuoLoginBean'
+ required: true
+
+ responses:
+ "200":
+ description: New order details featched successfully
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/CuoorderLocationDetailResponse'
+ '412':
+ description: Invalid location details
+ /cuoorder:
+ post:
+ tags:
+ - orderEntry
+ summary: creating order
+ operationId: createOrder
+ requestBody:
+ description: enter valid order details
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/NewOrderEntryBean'
+ responses:
+ "200":
+ description: Order create successfully.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AddOrderResponse'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+
+ put:
+ tags:
+ - orderEntry
+ summary: update order
+ operationId: updateOrder
+ requestBody:
+ description: enter valid order details
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UpdateOrderRequest'
+ responses:
+ "200":
+ description: Order create successfully.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AddOrderResponse'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+ /cuoorder/delorder:
+ post:
+ tags:
+ - orderEntry
+ summary: Cancel order
+ operationId: cancelOrder
+ requestBody:
+ description: enter valid order info
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CancelOrderRequest'
+ responses:
+ "200":
+ description: Order Canceled
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CancelOrderResponse'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+ /cuoorder/orderstatus:
+ post:
+ tags:
+ - orderEntry
+ summary: order subtypes
+ operationId: findOrdersWithStatus
+ requestBody:
+ description: enter valid order info
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FindOrderBean'
+ responses:
+ "200":
+ description: order subtypes featched successfully
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/IBrowseElement'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+ /cuoorder/subtypes:
+ post:
+ tags:
+ - orderEntry
+ summary: find order subtypes
+ operationId: findOrderSubtypes
+ requestBody:
+ description: enter valid order info
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FindOrderBean'
+ responses:
+ "200":
+ description: Order Canceled
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/IBrowseElement'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+ /standingOrder/view:
+ post:
+ tags:
+ - standingorder
+ summary: find standing orders
+ operationId: viewStandingOrders
+ requestBody:
+ description: enter valid standing orders by given input
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ViewStdOrderBean'
+ responses:
+ "200":
+ description: Standing orders featched successfully
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/StandingOrderBean'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+ /standingOrder/activate:
+ post:
+ tags:
+ - standingorder
+ summary: Activating standing orders
+ operationId: activateStdOrder
+ requestBody:
+ description: Activating standing orders
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/StandingOrderBean'
+ parameters:
+ - name: siteno
+ in: query
+ schema:
+ type: integer
+ - name: systemName
+ in: header
+ schema:
+ type: string
+
+ responses:
+ "200":
+ description: Standing orders featched successfully
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/StandingOrderBean'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+ /commitedAmounts/view:
+ post:
+ tags:
+ - commitedAmounts
+ summary: find Commited Amount records
+ operationId: viewReport
+ requestBody:
+ description: Input valid date, ordertypes and debitsiteno
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CommitedAmountsRequest'
+ required: true
+
+ responses:
+ "200":
+ description: Commited Amount records featched successfully
+ content:
+ application/pdf:
+ schema:
+ type: string
+ format: byte
+ '412':
+ description: Invalid Commited Amount input
+ /picklist/view:
+ post:
+ tags:
+ - pickList
+ summary: view or print Picklist records
+ operationId: viewreport
+ requestBody:
+ description: Input valid date, ordertypes and debitsiteno
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PickListParamsBean'
+ required: true
+
+ responses:
+ "200":
+ description: PickList records featched successfully
+ content:
+ application/pdf:
+ schema:
+ type: string
+ format: byte
+ '412':
+ description: Invalid Picklist input
+ /manifest/view:
+ post:
+ tags:
+ - manifest
+ summary: view or print Manifest records
+ operationId: viewREport
+ requestBody:
+ description: Input valid date, ordertypes and debitsiteno
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ManifestReportRequest'
+ required: true
+
+ responses:
+ "200":
+ description: Manifest records featched successfully
+ content:
+ application/pdf:
+ schema:
+ type: string
+ format: byte
+ '412':
+ description: Invalid Picklist input
+ /orderprint/view:
+ post:
+ tags:
+ - orderprint
+ summary: view or print OrderPrint records
+ operationId: viewReports
+ requestBody:
+ description: Input valid date, ordertypes and debitsiteno
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/OrderPrintReportRequest'
+ required: true
+
+ responses:
+ "200":
+ description: Manifest records featched successfully
+ content:
+ application/pdf:
+ schema:
+ type: string
+ format: byte
+ '412':
+ description: Invalid Picklist input
+ /user/{userid}:
+ get:
+ tags:
+ - user
+ summary: find login user detail like alternate gloryid
+ operationId: findUser
+ parameters:
+ - in: path
+ name: userid
+ schema:
+ type: integer
+ required: true
+ description: Numeric ID of the user to get
+ responses:
+ "201":
+ description: Envelop records featched successfully
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/UserModel'
+ "400":
+ description: Invalid Input
+ "404":
+ description: Page not found
+
+
+components:
+ schemas:
+ LoginRequest:
+ type: object
+ properties:
+ stationName:
+ type: number
+ format: long
+ adName:
+ type: string
+ userId:
+ type: string
+ password:
+ type: string
+ UserModel:
+ type: object
+ properties:
+ gloryid:
+ type: string
+ name:
+ type: string
+ title:
+ type: string
+ expireDate:
+ type: string
+ lockStatus:
+ type: integer
+ retryCount:
+ type: integer
+ overShort:
+ type: number
+ format: double
+ siteno:
+ type: integer
+ autoSellBuy:
+ type: integer
+ invGroupNo:
+ type: integer
+ rotateFlg:
+ type: integer
+ glId:
+ type: string
+ modifyDate:
+ type: string
+ modifyTime:
+ type: string
+ modifier:
+ type: string
+ deleteFlg:
+ type: string
+ shift:
+ type: string
+ bankId:
+ type: string
+ altUsers:
+ type: array
+ items:
+ $ref: '#/components/schemas/UsersAltIdModel'
+ clientList:
+ type: array
+ items:
+ $ref: '#/components/schemas/ClientSet'
+ groups:
+ type: array
+ items:
+ $ref: '#/components/schemas/GroupsModel'
+ adName:
+ type: string
+ employeeId:
+ type: string
+ reSellGId:
+ type: string
+ buyGId:
+ type: string
+ sellQuality:
+ type: string
+ buysiteno:
+ type: integer
+ eodinv:
+ type: integer
+ creditDateChngCtrl:
+ type: integer
+ fedBuySellCtrl:
+ type: integer
+ invAdjCtrl:
+ type: integer
+ reCountCtrl:
+ type: integer
+ sellManifest:
+ type: integer
+ invverifyReq:
+ type: integer
+ sysAltId:
+ type: integer
+ sysAltSupervisor:
+ type: integer
+ sellMfstLoc:
+ type: string
+ sellMfstAddr:
+ type: string
+ # shift:
+ # type: array
+ # items:
+ # $ref: '#/components/schemas/ShiftModel'
+ menuSecurity:
+ type: array
+ items:
+ $ref: '#/components/schemas/MenuView'
+ clientno:
+ type: integer
+ # name:
+ # type: string
+ MenuView:
+ type: object
+ properties:
+ privilege:
+ type: integer
+ nodeno:
+ type: integer
+ itemno:
+ type: integer
+ name:
+ type: string
+ module:
+ type: string
+ param:
+ type: string
+ nextnode:
+ type: integer
+ dualcustody:
+ type: integer
+ moduletype:
+ type: integer
+ deleteflg:
+ type: string
+ address:
+ type: string
+ ShiftModel:
+ type: object
+ properties:
+ sun:
+ type: integer
+ mon:
+ type: integer
+ tue:
+ type: integer
+ wed:
+ type: integer
+ thu:
+ type: integer
+ fri:
+ type: integer
+ sat:
+ type: integer
+ GroupsModel:
+ type: object
+ properties:
+ groupID:
+ type: string
+ name:
+ type: string
+ glid:
+ type: string
+ privilege:
+ type: integer
+ deleteFLG:
+ type: string
+ overShort:
+ type: number
+ format: double
+ icom:
+ type: integer
+ UsersAltIdModel:
+ type: object
+ properties:
+ gloryId:
+ type: string
+ altGloryId:
+ type: string
+ sysAltId:
+ type: string
+ altGloryIdName:
+ type: string
+ ClientSet:
+ type: object
+ properties:
+ clientno:
+ type: integer
+ name:
+ type: string
+ EcuoLoginBean:
+ type: object
+ properties:
+ siteno:
+ type: integer
+ clientno:
+ type: integer
+ loginUserId:
+ type: integer
+ loginUserName:
+ type: integer
+
+ LocationEnqResponseBean:
+ type: object
+ properties:
+ response:
+ $ref: '#/components/schemas/CommonResponseBean'
+ locList:
+ type: array
+ items:
+ $ref: '#/components/schemas/IBrowseElement'
+ ValidateLocationRequest:
+ type: object
+ properties:
+ siteno:
+ type: integer
+ clientno:
+ type: integer
+ cuoid:
+ type: number
+ format: long
+ locationid:
+ type: string
+ locname:
+ type: string
+ CuoorderLocationDetailResponse:
+ type: object
+ properties:
+ response:
+ $ref: '#/components/schemas/CommonResponseBean'
+ neworderentbean:
+ $ref: '#/components/schemas/NewOrderEntryBean'
+
+ AddOrderResponse:
+ type: object
+ NewOrderEntryBean:
+ type: object
+ properties:
+ siteno:
+ type: integer
+ clientno:
+ type: integer
+ locationid:
+ type: number
+ format: long
+ orderid:
+ type: number
+ format: long
+ previd:
+ type: number
+ format: long
+ orderdate:
+ type: string
+ format: date
+ ordertime:
+ type: string
+ format: date
+ orderby:
+ type: string
+ gtotal:
+ type: number
+ format: long
+ mediacount:
+ type: integer
+ shipdate:
+ type: string
+ deliverydate:
+ type: string
+ routeid:
+ type: integer
+ courierid:
+ type: integer
+ firstshipdate:
+ type: string
+ format: date
+ manifestdate:
+ type: string
+ format: date
+ canceldate:
+ type: string
+ format: date
+ cancelby:
+ type: string
+ entrytype:
+ type: string
+ orOrdersubtypeid:
+ type: integer
+ passthruLong:
+ type: number
+ format: long
+ cashadd:
+ type: integer
+ status:
+ type: string
+ accountno:
+ type: string
+ rtno:
+ type: string
+ acctlocid:
+ type: string
+ memo:
+ type: string
+ minOrderAmount:
+ type: number
+ format: long
+ maxOrderAmount:
+ type: number
+ format: long
+ shpWks:
+ type: array
+ items:
+ type: integer
+ deliWks:
+ type: array
+ items:
+ type: integer
+ shpHolidaysList:
+ type: array
+ items:
+ type: string
+ format: date
+ deliHolidaysList:
+ type: array
+ items:
+ type: string
+ format: date
+ supervisor:
+ type: string
+ UpdateOrderRequest:
+ type: object
+ properties:
+ orderid:
+ type: number
+ format: long
+ subType:
+ type: integer
+ memo:
+ type: string
+ cashAdd:
+ type: integer
+ clientno:
+ type: integer
+ siteno:
+ type: integer
+ cuoid:
+ type: number
+ format: long
+ updatedDate:
+ type: string
+ format: date
+ userId:
+ type: string
+ userName:
+ type: string
+ CancelOrderRequest:
+ type: object
+ properties:
+ siteno:
+ type: integer
+ clientno:
+ type: integer
+ orderid:
+ type: number
+ format: long
+ cuoid:
+ type: number
+ format: long
+ cancelDate:
+ type: string
+ format: date
+ cancelBy:
+ type: string
+ CancelOrderResponse:
+ type: object
+ FindOrderBean:
+ type: object
+ properties:
+ siteno:
+ type: integer
+ clientno:
+ type: integer
+ cuoid:
+ type: number
+ format: long
+ clientLocId:
+ type: string
+ ordeno:
+ type: number
+ format: long
+ GetOrderResponse:
+ type: object
+ IBrowseElement:
+ type: object
+ properties:
+ id:
+ type: number
+ format: long
+ name:
+ type: string
+ key:
+ type: string
+ StandingOrderBean:
+ type: object
+ properties:
+ maxShipdate:
+ type: string
+ clientno:
+ type: integer
+ siteno:
+ type: integer
+ orderamount:
+ type: number
+ format: long
+ mediacount:
+ type: number
+ format: long
+ stdorderno:
+ type: integer
+ locationid:
+ type: string
+ accountno:
+ type: string
+ rtno:
+ type: string
+ cuoid:
+ type: number
+ format: long
+ nextdelidate:
+ type: string
+ format: date
+ deliverydays:
+ type: integer
+ prepardays:
+ type: integer
+ delileaddays:
+ type: integer
+ ordprepdays:
+ type: integer
+ delivleaddays:
+ type: integer
+ workdaysC:
+ type: integer
+ shiproute:
+ type: integer
+ courierid:
+ type: string
+ wklyinterval:
+ type: integer
+ shipdate:
+ type: string
+ format: date
+ deliverydate:
+ type: string
+ format: date
+ orderby:
+ type: string
+ siteZoneId:
+ type: string
+ ViewStdOrderBean:
+ type: object
+ properties:
+ siteno:
+ type: integer
+ clientno:
+ type: integer
+ stdOrdDate:
+ type: string
+ format: date
+ groupid:
+ type: integer
+ siteZoneId:
+ type: string
+ CommonResponseBean:
+ type: object
+ properties:
+ responseMessage:
+ type: string
+ responseCode:
+ type: string
+ responseDetailMessage:
+ type: string
+ processCount:
+ type: string
+ versionNo:
+ type: string
+ timestamp:
+ type: string
+ format: timestamp
+ CommitedAmountsRequest:
+ type: object
+ properties:
+ fromDate:
+ type: string
+ toDate:
+ type: string
+ debitSites:
+ type: array
+ items:
+ type: integer
+ ordertypes:
+ type: array
+ items:
+ type: integer
+ funcType:
+ type: integer
+ clientNo:
+ type: integer
+ siteNo:
+ type: integer
+ clientName:
+ type: string
+ PickListParamsBean:
+ type: object
+ properties:
+ fromDate:
+ type: string
+ toDate:
+ type: string
+ clientNo:
+ type: integer
+ siteNo:
+ type: integer
+ clientName:
+ type: string
+ entryTypes:
+ type: array
+ items:
+ type: integer
+ couriers:
+ type: array
+ items:
+ $ref: '#/components/schemas/IBrowseElement'
+ funcType:
+ type: integer
+ totalsOnly:
+ type: boolean
+ ManifestReportRequest:
+ type: object
+ properties:
+ fromDate:
+ type: string
+ toDate:
+ type: string
+ clientNo:
+ type: integer
+ siteNo:
+ type: integer
+ clientName:
+ type: string
+ couriers:
+ type: array
+ items:
+ $ref: '#/components/schemas/IBrowseElement'
+ deliveryDateSorting:
+ type: boolean
+ isrouteDetail:
+ type: boolean
+ useLocationId:
+ type: boolean
+ pageTotals:
+ type: boolean
+ printShipped:
+ type: boolean
+ rePrintManifested:
+ type: boolean
+ orderMediaType:
+ type: boolean
+ orderStatusType:
+ type: boolean
+ orderUpdateToManifestSts:
+ type: boolean
+ orderCurrUpdateSts:
+ type: boolean
+ orderCoinUpdateSts:
+ type: boolean
+ OrderPrintReportRequest:
+ type: object
+ properties:
+ fromDate:
+ type: string
+ toDate:
+ type: string
+ debitSites:
+ type: array
+ items:
+ type: integer
+ couriers:
+ type: array
+ items:
+ $ref: '#/components/schemas/IBrowseElement'
+ ordertypes:
+ type: array
+ items:
+ type: integer
+ clientNo:
+ type: integer
+ siteNo:
+ type: integer
+ toOrder:
+ type: string
+ fromOrder:
+ type: string
+ iscurrLabel:
+ type: boolean
+ iscoinLabel:
+ type: boolean
+ isshippingLabel:
+ type: boolean
+ isreturnLabel:
+ type: boolean
+ isprintNew:
+ type: boolean
+ isrePrintShipped:
+ type: boolean
+ isorderPickList:
+ type: boolean
+ istotalsOnly:
+ type: boolean
+ isupdateSts:
+ type: boolean
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi.json
new file mode 100644
index 00000000..142821b8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi.json
@@ -0,0 +1,167 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Simple API overview",
+ "version": "2.0.0"
+ },
+ "paths": {
+ "/": {
+ "get": {
+ "operationId": "listVersionsv2",
+ "summary": "List API versions",
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "foo": {
+ "value": {
+ "versions": [
+ {
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "id": "v2.0",
+ "links": [
+ {
+ "href": "http://127.0.0.1:8774/v2/",
+ "rel": "self"
+ }
+ ]
+ },
+ {
+ "status": "EXPERIMENTAL",
+ "updated": "2013-07-23T11:33:21Z",
+ "id": "v3.0",
+ "links": [
+ {
+ "href": "http://127.0.0.1:8774/v3/",
+ "rel": "self"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "300": {
+ "description": "300 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "foo": {
+ "value": "{\n \"versions\": [\n {\n \"status\": \"CURRENT\",\n \"updated\": \"2011-01-21T11:33:21Z\",\n \"id\": \"v2.0\",\n \"links\": [\n {\n \"href\": \"http://127.0.0.1:8774/v2/\",\n \"rel\": \"self\"\n }\n ]\n },\n {\n \"status\": \"EXPERIMENTAL\",\n \"updated\": \"2013-07-23T11:33:21Z\",\n \"id\": \"v3.0\",\n \"links\": [\n {\n \"href\": \"http://127.0.0.1:8774/v3/\",\n \"rel\": \"self\"\n }\n ]\n }\n ]\n}\n"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v2": {
+ "get": {
+ "operationId": "getVersionDetailsv2",
+ "summary": "Show API version details",
+ "responses": {
+ "200": {
+ "description": "200 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "foo": {
+ "value": {
+ "version": {
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute+xml;version=2"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute+json;version=2"
+ }
+ ],
+ "id": "v2.0",
+ "links": [
+ {
+ "href": "http://127.0.0.1:8774/v2/",
+ "rel": "self"
+ },
+ {
+ "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf",
+ "type": "application/pdf",
+ "rel": "describedby"
+ },
+ {
+ "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl",
+ "type": "application/vnd.sun.wadl+xml",
+ "rel": "describedby"
+ },
+ {
+ "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl",
+ "type": "application/vnd.sun.wadl+xml",
+ "rel": "describedby"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "203": {
+ "description": "203 response",
+ "content": {
+ "application/json": {
+ "examples": {
+ "foo": {
+ "value": {
+ "version": {
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute+xml;version=2"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute+json;version=2"
+ }
+ ],
+ "id": "v2.0",
+ "links": [
+ {
+ "href": "http://23.253.228.211:8774/v2/",
+ "rel": "self"
+ },
+ {
+ "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf",
+ "type": "application/pdf",
+ "rel": "describedby"
+ },
+ {
+ "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl",
+ "type": "application/vnd.sun.wadl+xml",
+ "rel": "describedby"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_2.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_2.json
new file mode 100644
index 00000000..8b4f8deb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_2.json
@@ -0,0 +1,291 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "Simple Inventory API",
+ "description": "This is a simple API",
+ "version": "1.0.0",
+ "contact": {
+ "email": "example@example.com"
+ }
+ },
+ "host": "api.example.com",
+ "basePath": "/v1",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "paths": {
+ "/inventory": {
+ "get": {
+ "summary": "searches inventory",
+ "operationId": "searchInventory",
+ "description": "By passing in the appropriate options, you can search for available inventory in the system",
+ "produces": [
+ "application/json",
+ "application/xml",
+ "text/plain",
+ "text/html"
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "searchString",
+ "description": "pass an optional search string for looking up inventory",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "in": "query",
+ "name": "skip",
+ "description": "number of records to skip for pagination",
+ "type": "integer",
+ "format": "int32",
+ "minimum": 0
+ },
+ {
+ "in": "query",
+ "name": "limit",
+ "description": "maximum number of records to return",
+ "type": "integer",
+ "format": "int32",
+ "minimum": 0,
+ "maximum": 50
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "bad input parameter",
+ "examples": {
+ "text/html": "Hello, world!
",
+ "application/xml": "Alice Bob ",
+ "application/json": {
+ "id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+ "name": "test",
+ "release_date": "2016-08-29T09:12:33.001Z",
+ "manufacturer": {
+ "name": "ACME Corporation",
+ "home_page": "https://www.acme-corp.com",
+ "phone": "408-867-5309"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "bad input parameter",
+ "examples": {
+ "text/plain": "Hello, world!",
+ "text/html": "Hello, world!
",
+ "application/xml": "Alice Bob "
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "adds an inventory item",
+ "operationId": "addInventory",
+ "description": "Adds an item to the system",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "inventoryItem",
+ "description": "Inventory item to add",
+ "schema": {
+ "$ref": "#/definitions/InventoryItem"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "item created"
+ },
+ "400": {
+ "description": "invalid input, object invalid"
+ },
+ "409": {
+ "description": "an existing item already exists"
+ }
+ }
+ }
+ },
+ "/inventory.v2": {
+ "get": {
+ "tags": [
+ "developers"
+ ],
+ "summary": "searches inventory",
+ "operationId": "searchInventoryV2",
+ "description": "",
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "bad input parameter",
+ "examples": {
+ "application/json": {
+ "id": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/inventory_empty_responses": {
+ "get": {
+ "responses": {}
+ }
+ },
+ "/inventory_without_examples": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "ok"
+ }
+ }
+ }
+ },
+ "/inventory_empty_examples": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "ok",
+ "examples": {}
+ }
+ }
+ }
+ },
+ "/ref/inventory": {
+ "get": {
+ "summary": "",
+ "operationId": "GetRefInventory",
+ "description": "",
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/InventoryItem"
+ }
+ }
+ }
+ }
+ },
+ "/ping-with-one-default-response": {
+ "get": {
+ "summary": "",
+ "operationId": "ping",
+ "description": "",
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "default": {
+ "description": "default response",
+ "examples": {
+ "application/json": {
+ "msg": "pong"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/ping-with-multiple-response": {
+ "get": {
+ "summary": "",
+ "operationId": "ping",
+ "description": "",
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "examples": {
+ "application/json": {
+ "msg": "pong"
+ }
+ }
+ },
+ "504": {
+ "description": "error",
+ "examples": {
+ "application/json": {
+ "msg": "gateway timeout"
+ }
+ }
+ },
+ "default": {
+ "description": "default response",
+ "examples": {
+ "application/json": {
+ "msg": "unknow error"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "InventoryItem": {
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "manufacturer",
+ "releaseDate"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "name": {
+ "type": "string"
+ },
+ "releaseDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "manufacturer": {
+ "$ref": "#/definitions/Manufacturer"
+ }
+ }
+ },
+ "Manufacturer": {
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "example": "ACME Corporation"
+ },
+ "homePage": {
+ "type": "string",
+ "format": "url",
+ "example": "https://www.acme-corp.com"
+ },
+ "phone": {
+ "type": "string",
+ "example": "408-867-5309"
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_2.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_2.yaml
new file mode 100644
index 00000000..ea364541
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_2.yaml
@@ -0,0 +1,211 @@
+swagger: '2.0'
+
+info:
+ title: Simple Inventory API
+ description: This is a simple API
+ version: 1.0.0
+ contact:
+ email: example@example.com
+
+host: api.example.com
+basePath: "/v1"
+
+schemes:
+ - http
+ - https
+
+paths:
+ /inventory:
+ get:
+ summary: searches inventory
+ operationId: searchInventory
+ description: By passing in the appropriate options, you can search for available inventory in the system
+ produces:
+ - application/json
+ - application/xml
+ - text/plain
+ - text/html
+ parameters:
+ - in: query
+ name: searchString
+ description: pass an optional search string for looking up inventory
+ required: false
+ type: string
+ - in: query
+ name: skip
+ description: number of records to skip for pagination
+ type: integer
+ format: int32
+ minimum: 0
+ - in: query
+ name: limit
+ description: maximum number of records to return
+ type: integer
+ format: int32
+ minimum: 0
+ maximum: 50
+ responses:
+ 200:
+ description: bad input parameter
+ examples:
+ text/html: 'Hello, world!
'
+ application/xml: 'Alice Bob '
+ application/json:
+ id: d290f1ee-6c54-4b01-90e6-d701748f0851
+ name: test
+ release_date: 2016-08-29T09:12:33.001Z
+ manufacturer:
+ name: ACME Corporation
+ home_page: https://www.acme-corp.com
+ phone: 408-867-5309
+ 400:
+ description: bad input parameter
+ examples:
+ text/plain: Hello, world!
+ text/html: 'Hello, world!
'
+ application/xml: 'Alice Bob '
+
+ post:
+ summary: adds an inventory item
+ operationId: addInventory
+ description: Adds an item to the system
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: inventoryItem
+ description: Inventory item to add
+ schema:
+ $ref: '#/definitions/InventoryItem'
+ responses:
+ 201:
+ description: item created
+ 400:
+ description: invalid input, object invalid
+ 409:
+ description: an existing item already exists
+
+ /inventory.v2:
+ get:
+ tags:
+ - developers
+ summary: searches inventory
+ operationId: searchInventoryV2
+ description: ''
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: bad input parameter
+ examples:
+ application/json:
+ id: d290f1ee-6c54-4b01-90e6-d701748f0851
+
+ /inventory_empty_responses:
+ get:
+ responses: {}
+
+ /inventory_without_examples:
+ get:
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: ok
+
+ /inventory_empty_examples:
+ get:
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: ok
+ examples: {}
+
+ /ref/inventory:
+ get:
+ summary: ''
+ operationId: GetRefInventory
+ description: ''
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: ''
+ schema:
+ $ref: '#/definitions/InventoryItem'
+
+ /ping-with-one-default-response:
+ get:
+ summary: ''
+ operationId: ping
+ description: ''
+ produces:
+ - application/json
+ responses:
+ default:
+ description: default response
+ examples:
+ application/json:
+ msg: pong
+
+ /ping-with-multiple-response:
+ get:
+ summary: ''
+ operationId: ping
+ description: ''
+ produces:
+ - application/json
+ responses:
+ 200:
+ description: OK
+ examples:
+ application/json:
+ msg: pong
+ 504:
+ description: error
+ examples:
+ application/json:
+ msg: gateway timeout
+ default:
+ description: default response
+ examples:
+ application/json:
+ msg: unknow error
+
+definitions:
+ InventoryItem:
+ type: object
+ required:
+ - id
+ - name
+ - manufacturer
+ - releaseDate
+ properties:
+ id:
+ type: string
+ example: d290f1ee-6c54-4b01-90e6-d701748f0851
+ name:
+ type: string
+ releaseDate:
+ type: string
+ format: date-time
+ manufacturer:
+ $ref: '#/definitions/Manufacturer'
+ Manufacturer:
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ example: ACME Corporation
+ homePage:
+ type: string
+ format: url
+ example: https://www.acme-corp.com
+ phone:
+ type: string
+ example: 408-867-5309
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_3.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_3.json
new file mode 100644
index 00000000..df3b28c8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_3.json
@@ -0,0 +1,366 @@
+{
+ "openapi": "3.0.1",
+ "info": {
+ "title": "OpenAPI 3.0 API",
+ "description": "This is a simple API",
+ "contact": {
+ "email": "example@example.com"
+ },
+ "version": "1.0.0"
+ },
+ "servers": [
+ {
+ "url": "/v1"
+ }
+ ],
+ "paths": {
+ "/inventory": {
+ "summary": "inventory API",
+ "description": "One line Two line\n",
+ "get": {
+ "summary": "searches inventory",
+ "description": "By passing in the appropriate options, you can search for\navailable inventory in the system\n",
+ "operationId": "searchInventory",
+ "parameters": [
+ {
+ "name": "searchString",
+ "in": "query",
+ "description": "pass an optional search string for looking up inventory",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "bad input parameter",
+ "content": {
+ "text/html": {
+ "examples": {
+ "example_first": {
+ "value": "first"
+ },
+ "example_second": {
+ "value": "second"
+ }
+ },
+ "example": "Hello, world!
"
+ },
+ "application/xml": {
+ "examples": {
+ "1": {
+ "value": "1 "
+ },
+ "2": {
+ "value": "2 "
+ },
+ "three": {
+ "value": "three "
+ }
+ }
+ },
+ "application/json": {
+ "example": {
+ "id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+ "name": "test",
+ "release_date": "2016-08-29T09:12:33.001Z",
+ "manufacturer": {
+ "name": "ACME Corporation",
+ "home_page": "https://www.acme-corp.com",
+ "phone": "408-867-5309"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "bad input parameter",
+ "content": {
+ "text/plain": {
+ "example": "Hello, world!"
+ },
+ "text/html": {
+ "example": "Hello, world!
"
+ },
+ "application/xml": {
+ "example": "Alice Bob "
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "adds an inventory item",
+ "description": "Adds an item to the system",
+ "operationId": "addInventory",
+ "requestBody": {
+ "description": "Inventory item to add",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InventoryItem"
+ }
+ }
+ },
+ "required": false
+ },
+ "responses": {
+ "201": {
+ "description": "item created",
+ "content": {
+ }
+ },
+ "400": {
+ "description": "invalid input, object invalid",
+ "content": {
+ }
+ },
+ "409": {
+ "description": "an existing item already exists",
+ "content": {
+ }
+ }
+ },
+ "x-codegen-request-body-name": "inventoryItem"
+ }
+ },
+ "/inventory.v2": {
+ "get": {
+ "summary": "searches inventory",
+ "operationId": "searchInventoryV2",
+ "responses": {
+ "200": {
+ "description": "bad input parameter",
+ "content": {
+ "application/json": {
+ "example": {
+ "id": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/inventory_empty_responses": {
+ "get": {
+ "responses": {
+ }
+ }
+ },
+ "/inventory_without_content": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "success"
+ }
+ }
+ }
+ },
+ "/inventory_empty_content": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "success",
+ "content": {
+ }
+ }
+ }
+ }
+ },
+ "/inventory_empty_content_examples": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "success",
+ "content": {
+ "application/json": {
+ "examples": {
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/ref/inventory_ref_schema": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetInventoryItemResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/ref/inventory_ref_response": {
+ "get": {
+ "responses": {
+ "400": {
+ "$ref": "#/components/responses/InvalidRequestResponse"
+ }
+ }
+ }
+ },
+ "/ping-with-one-default-response": {
+ "get": {
+ "responses": {
+ "default": {
+ "description": "default response",
+ "content": {
+ "application/json": {
+ "example": {
+ "msg": "pong"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/ping-with-multiple-response": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "example": {
+ "msg": "pong"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "example": {
+ "msg": "invalid request"
+ }
+ }
+ }
+ },
+ "5XX": {
+ "description": "error",
+ "content": {
+ "application/json": {
+ "example": {
+ "msg": "gateway timeout"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "default response",
+ "content": {
+ "application/json": {
+ "example": {
+ "msg": "unknow error"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "responses": {
+ "InvalidRequestResponse": {
+ "description": "400",
+ "content": {
+ "application/json": {
+ "example": {
+ "code": "INVALID_REQUEST",
+ "msg": "Invalid Request"
+ }
+ }
+ }
+ }
+ },
+ "schemas": {
+ "APIResponse": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string"
+ },
+ "msg": {
+ "type": "string"
+ }
+ }
+ },
+ "GetInventoryItemResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/APIResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/components/schemas/InventoryItem"
+ }
+ }
+ }
+ ]
+ },
+ "InventoryItem": {
+ "required": [
+ "id",
+ "manufacturer",
+ "name",
+ "releaseDate"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "format": "uuid",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "name": {
+ "type": "string",
+ "example": "Doge111"
+ },
+ "releaseDate": {
+ "type": "string",
+ "format": "date-time",
+ "example": "2016-08-29T09:12:33.001Z"
+ },
+ "manufacturer": {
+ "$ref": "#/components/schemas/Manufacturer"
+ }
+ }
+ },
+ "Manufacturer": {
+ "required": [
+ "name"
+ ],
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "example": "ACME Corporation"
+ },
+ "homePage": {
+ "type": "string",
+ "format": "url",
+ "example": "https://www.acme-corp.com"
+ },
+ "phone": {
+ "type": "string",
+ "example": "408-867-5309"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_3.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_3.yaml
new file mode 100644
index 00000000..0d477512
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/openapi_3.yaml
@@ -0,0 +1,241 @@
+openapi: 3.0.1
+info:
+ title: OpenAPI 3.0 API
+ description: This is a simple API
+ contact:
+ email: example@example.com
+ version: 1.0.0
+
+servers:
+ - url: "/v1"
+
+paths:
+ /inventory:
+ summary: inventory API
+ description: >
+ One line
+ Two line
+ get:
+ summary: searches inventory
+ description: |
+ By passing in the appropriate options, you can search for
+ available inventory in the system
+ operationId: searchInventory
+ parameters:
+ - name: searchString
+ in: query
+ description: pass an optional search string for looking up inventory
+ schema:
+ type: string
+ responses:
+ 200:
+ description: bad input parameter
+ content:
+ text/html:
+ examples:
+ example_first:
+ value: first
+ example_second:
+ value: second
+ example: Hello, world!
+ application/xml:
+ examples:
+ 1:
+ value: 1
+ 2:
+ value: 2
+ three:
+ value: three
+ application/json:
+ example:
+ id: d290f1ee-6c54-4b01-90e6-d701748f0851
+ name: test
+ release_date: 2016-08-29T09:12:33.001Z
+ manufacturer:
+ name: ACME Corporation
+ home_page: https://www.acme-corp.com
+ phone: 408-867-5309
+ 400:
+ description: bad input parameter
+ content:
+ text/plain:
+ example: Hello, world!
+ text/html:
+ example: Hello, world!
+ application/xml:
+ example: Alice Bob
+ post:
+ summary: adds an inventory item
+ description: Adds an item to the system
+ operationId: addInventory
+ requestBody:
+ description: Inventory item to add
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/InventoryItem'
+ required: false
+ responses:
+ 201:
+ description: item created
+ content: {}
+ 400:
+ description: invalid input, object invalid
+ content: {}
+ 409:
+ description: an existing item already exists
+ content: {}
+ x-codegen-request-body-name: inventoryItem
+ /inventory.v2:
+ get:
+ summary: searches inventory
+ operationId: searchInventoryV2
+ responses:
+ 200:
+ description: bad input parameter
+ content:
+ application/json:
+ example:
+ id: d290f1ee-6c54-4b01-90e6-d701748f0851
+
+
+ /inventory_empty_responses:
+ get:
+ responses: {}
+
+ /inventory_without_content:
+ get:
+ responses:
+ '200':
+ description: success
+
+ /inventory_empty_content:
+ get:
+ responses:
+ '200':
+ description: success
+ content: {}
+
+ /inventory_empty_content_examples:
+ get:
+ responses:
+ '200':
+ description: success
+ content:
+ application/json:
+ examples: {}
+
+ /ref/inventory_ref_schema:
+ get:
+ responses:
+ '200':
+ description: 'success'
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/GetInventoryItemResponse'
+
+ /ref/inventory_ref_response:
+ get:
+ responses:
+ '400':
+ $ref: '#/components/responses/InvalidRequestResponse'
+
+ /ping-with-one-default-response:
+ get:
+ responses:
+ default:
+ description: default response
+ content:
+ application/json:
+ example:
+ msg: pong
+
+ /ping-with-multiple-response:
+ get:
+ responses:
+ 200:
+ description: OK
+ content:
+ application/json:
+ example:
+ msg: pong
+ 400:
+ description: OK
+ content:
+ application/json:
+ example:
+ msg: invalid request
+ 5XX:
+ description: error
+ content:
+ application/json:
+ example:
+ msg: gateway timeout
+ default:
+ description: default response
+ content:
+ application/json:
+ example:
+ msg: unknow error
+
+components:
+ responses:
+ InvalidRequestResponse:
+ description: '400'
+ content:
+ application/json:
+ example:
+ code: "INVALID_REQUEST"
+ msg: "Invalid Request"
+ schemas:
+ APIResponse:
+ type: object
+ properties:
+ code:
+ type: string
+ msg:
+ type: string
+ GetInventoryItemResponse:
+ allOf:
+ - $ref: '#/components/schemas/APIResponse'
+ - type: object
+ properties:
+ data:
+ $ref: '#/components/schemas/InventoryItem'
+ InventoryItem:
+ required:
+ - id
+ - manufacturer
+ - name
+ - releaseDate
+ type: object
+ properties:
+ id:
+ type: string
+ format: uuid
+ example: d290f1ee-6c54-4b01-90e6-d701748f0851
+ name:
+ type: string
+ example: Doge111
+ releaseDate:
+ type: string
+ format: date-time
+ example: 2016-08-29T09:12:33.001Z
+ manufacturer:
+ $ref: '#/components/schemas/Manufacturer'
+ Manufacturer:
+ required:
+ - name
+ type: object
+ properties:
+ name:
+ type: string
+ example: ACME Corporation
+ homePage:
+ type: string
+ format: url
+ example: https://www.acme-corp.com
+ phone:
+ type: string
+ example: 408-867-5309
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/path-match-oas.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/path-match-oas.yaml
new file mode 100644
index 00000000..bbc72a62
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/path-match-oas.yaml
@@ -0,0 +1,63 @@
+openapi: "3.0.0"
+info:
+ title: Sample API
+ description: A Sample OpenAPI Spec
+ version: 1.0.0
+
+servers:
+ - url: "/v1"
+
+paths:
+ /user/{username}:
+ get:
+ summary: Get user by user name
+ description: ''
+ operationId: getUserByName
+ parameters:
+ - name: username
+ in: path
+ description: 'The name that needs to be fetched. Use user1 for testing. '
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ '400':
+ description: Invalid username supplied
+ '404':
+ description: User not found
+ /user/{username}/report.{format}:
+ get:
+ parameters:
+ - name: username
+ in: path
+ description: 'The name that needs to be fetched. Use user1 for testing. '
+ required: true
+ schema:
+ type: string
+ - in: path
+ name: format
+ required: true
+ description: The user to create.
+ schema:
+ type: string
+ enum:
+ - pdf
+ - txt
+ - word
+ responses:
+ 201:
+ description: Created
+ /user/{username}.pdf:
+ get:
+ parameters:
+ - name: username
+ in: path
+ description: 'The name that needs to be fetched. Use user1 for testing. '
+ required: true
+ schema:
+ type: string
+ responses:
+ 201:
+ description: Created
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/reference-oas.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/reference-oas.yaml
new file mode 100644
index 00000000..42d04fc9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/reference-oas.yaml
@@ -0,0 +1,132 @@
+openapi: 3.0.1
+info:
+ title: Sample API
+ description: A Sample OpenAPI Spec
+ version: 1.0.0
+
+paths:
+ /ref-case-1:
+ post:
+ responses:
+ 200:
+ description: OK
+ content:
+ 'application/json':
+ schema:
+ $ref: '#/components/schemas/Simple'
+ /ref-case-2:
+ post:
+ responses:
+ 200:
+ description: OK
+ content:
+ 'application/json':
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Simple'
+ minItems: 2
+ maxItems: 2
+ /ref-case-3:
+ post:
+ responses:
+ '200':
+ description: OK
+ content:
+ 'application/json':
+ schema:
+ type: object
+ properties:
+ key_integer:
+ type: integer
+ key_boolean:
+ type: boolean
+ simple:
+ $ref: '#/components/schemas/Simple'
+
+ /recursive-ref-case-1:
+ post:
+ responses:
+ '200':
+ description: OK
+ content:
+ 'application/json':
+ schema:
+ $ref: '#/components/schemas/Node'
+ /recursive-ref-case-2:
+ post:
+ responses:
+ '200':
+ description: OK
+ content:
+ 'application/json':
+ schema:
+ $ref: '#/components/schemas/ComparedTcmAgreementsDto'
+
+ /recursive-ref-case-3:
+ post:
+ responses:
+ '200':
+ description: OK
+ content:
+ 'application/json':
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Customer'
+ minItems: 1
+ maxItems: 1
+
+components:
+ schemas:
+ Simple:
+ type: object
+ properties:
+ key_string:
+ type: string
+ Node:
+ type: object
+ properties:
+ value:
+ type: string
+ node:
+ $ref: '#/components/schemas/Node'
+ ComparedTcmAgreementsDto:
+ type: object
+ properties:
+ differentFields:
+ type: array
+ items:
+ type: string
+ securityEligibilityRules:
+ $ref: '#/components/schemas/ConstraintTree'
+ ConstraintTree:
+ type: object
+ properties:
+ constraint:
+ type: string
+ leaves:
+ type: array
+ items:
+ $ref: '#/components/schemas/ConstraintTree'
+ operation:
+ type: string
+ enum:
+ - IS
+ - AND
+ - OR
+ Customer:
+ type: object
+ properties:
+ title:
+ type: string
+ profile:
+ $ref: '#/components/schemas/Profile'
+ Profile:
+ type: object
+ properties:
+ customer:
+ $ref: '#/components/schemas/Customer'
+ createdDate:
+ type: string
+ format: date-time
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/stock.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/stock.json
new file mode 100644
index 00000000..60391e1d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/stock.json
@@ -0,0 +1,161 @@
+{"swagger": "2.0",
+ "info" : {
+ "title": "Stock API",
+ "description": "Stock Information Service",
+ "version": "0.1"
+ },
+ "host": "apistore.kong.com:8000",
+ "basePath": "/",
+ "schemes": [
+ "http",
+ "https"
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {
+ "/stock/historical": {
+ "get": {
+ "description": "",
+ "operationId": "GET /stock/historical",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Production"
+ ],
+ "parameters": [
+ {
+ "required": true,
+ "in": "query",
+ "name": "tickers",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Status 200",
+ "examples": {
+ "application/json":
+ {
+ "meta_data" : {
+ "api_name" : "historical_stock_price_v2",
+ "num_total_data_points" : 1,
+ "credit_cost" : 10,
+ "start_date" : "yesterday",
+ "end_date" : "yesterday"
+ },
+ "result_data" : {
+ "AAPL" : [ {
+ "date" : "2000-04-23",
+ "volume" : 33,
+ "high" : 100.75,
+ "low" : 100.87,
+ "adj_close" : 275.03,
+ "close" : 100.03,
+ "open" : 100.87
+ } ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/stock/closing": {
+ "get": {
+ "description": "",
+ "operationId": "GET /stock/closing",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Beta"
+ ],
+ "parameters": [
+ {
+ "required": true,
+ "in": "query",
+ "name": "tickers",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Status 200",
+ "examples": {
+ "application/json":
+ {
+ "meta_data" : {
+ "api_name" : "closing_stock_price_v1"
+ },
+ "result_data" : {
+ "AAPL" : [ {
+ "date" : "2000-06-23",
+ "volume" : 33,
+ "high" : 100.75,
+ "low" : 100.87,
+ "adj_close" : 275.03,
+ "close" : 100.03,
+ "open" : 100.87
+ } ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/stock-historical.v3": {
+ "get": {
+ "description": "",
+ "operationId": "GET /stock-historical.v3",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Production"
+ ],
+ "parameters": [
+ {
+ "required": true,
+ "in": "query",
+ "name": "tickers",
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Status 200",
+ "examples": {
+ "application/json":
+ {
+ "meta_data" : {
+ "api_name" : "historical_stock_price_v3",
+ "num_total_data_points" : 1,
+ "credit_cost" : 10,
+ "start_date" : "yesterday",
+ "end_date" : "yesterday"
+ },
+ "result_data" : {
+ "AAPL" : [ {
+ "date" : "2000-04-23",
+ "volume" : 33,
+ "high" : 100.75,
+ "low" : 100.87,
+ "adj_close" : 275.03,
+ "close" : 100.03,
+ "open" : 100.87
+ } ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/stock_500.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/stock_500.yaml
new file mode 100644
index 00000000..16346669
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/stock_500.yaml
@@ -0,0 +1,84 @@
+openapi: 3.0.2
+info:
+ title: Stock API
+ description: Stock Information Service
+ version: "1.0"
+paths:
+ /stock/historical:
+ get:
+ description: ""
+ operationId: GET /stock/historical
+ tags:
+ - Production
+ parameters:
+ - required: true
+ in: query
+ name: tickers
+ schema:
+ type: string
+ responses:
+ "200":
+ description: Status 200
+ content:
+ application/json:
+ examples:
+ response:
+ value:
+ meta_data:
+ api_name: historical_stock_price_v2
+ num_total_data_points: 1
+ credit_cost: 10
+ start_date: yesterday
+ end_date: yesterday
+ result_data:
+ AAPL:
+ - date: 2000-04-23
+ volume: 33
+ high: 100.75
+ low: 100.87
+ adj_close: 275.03
+ close: 100.03
+ open: 100.87
+ /stock/closing:
+ get:
+ description: ""
+ operationId: GET /stock/closing
+ tags:
+ - Beta
+ parameters:
+ - required: true
+ in: query
+ name: tickers
+ schema:
+ type: string
+ responses:
+ "200":
+ description: Status 200
+ content:
+ application/json:
+ examples:
+ response:
+ value:
+ meta_data:
+ api_name: closing_stock_price_v1
+ result_data:
+ AAPL:
+ - date: 2000-06-23
+ volume: 33
+ high: 100.75
+ low: 100.87
+ adj_close: 275.03
+ close: 100.03
+ open: 100.87
+ /stock/delete:
+ delete:
+ description: ""
+ operationId: DELETE /stock/historical
+ tags:
+ - Production
+ responses:
+ "204":
+ description: delete success
+servers:
+ - url: http://localhost:8000/
+ - url: https://localhost:8443/
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/users.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/users.yaml
new file mode 100644
index 00000000..6c1a2406
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocking/users.yaml
@@ -0,0 +1,35 @@
+swagger: "2.0"
+info:
+ title: Sample API
+ description: A Sample API
+ version: 1.0.0
+host: api.example.com
+basePath: /v1
+schemes:
+ - https
+paths:
+ /users/{id}:
+ get:
+ summary: Returns an individual user
+ description: Returns an individual user
+ produces:
+ - application/json
+ parameters:
+ - in: path
+ name: id # Note the name is the same as in the path
+ required: true
+ schema:
+ type: integer
+ minimum: 1
+ description: The user ID
+ responses:
+ "200":
+ description: OK
+ examples:
+ "application/json" : {
+ "sub": "jdoe",
+ "name": "Jane Doe",
+ "given_name": "Jane",
+ "family_name": "Doe",
+ "email": "janedoe@example.com"
+ }
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocks/lua-resty-dns/resty/dns/resolver.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocks/lua-resty-dns/resty/dns/resolver.lua
new file mode 100644
index 00000000..b21835fb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocks/lua-resty-dns/resty/dns/resolver.lua
@@ -0,0 +1,129 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- Mock for the underlying 'resty.dns.resolver' library
+-- (so NOT the Kong dns client)
+
+-- this file should be in the Kong working directory (prefix)
+local MOCK_RECORD_FILENAME = "dns_mock_records.json"
+
+
+local LOG_PREFIX = "[mock_dns_resolver] "
+local cjson = require "cjson.safe"
+
+-- first thing is to get the original (non-mock) resolver
+local resolver
+do
+ local function get_source_path()
+ -- find script path remember to strip off the starting @
+ -- should be like: 'spec/fixtures/mocks/lua-resty-dns/resty/dns/resolver.lua'
+ return debug.getinfo(2, "S").source:sub(2) --only works in a function, hence the wrapper
+ end
+ local path = get_source_path()
+
+ -- module part is like: 'resty.dns.resolver'
+ local module_part = select(1,...)
+
+ -- create the packagepath part, like: 'spec/fixtures/mocks/lua-resty-dns/?.lua'
+ path = path:gsub(module_part:gsub("%.", "/"), "?") .. ";" -- prefix path, so semi-colon at end
+
+ -- grab current paths
+ local old_paths = package.path
+
+ -- drop the element that picked this mock from the path
+ local s, e = old_paths:find(path, 1, true)
+ package.path = old_paths:sub(1, s-1) .. old_paths:sub(e + 1, -1)
+
+ -- With the mock out of the path, require the module again to get the original.
+ -- Problem is that package.loaded contains a userdata now, because we're in
+ -- the middle of loading that same module name. So swap it.
+ local swap
+ swap, package.loaded[module_part] = package.loaded[module_part], nil
+ resolver = require(module_part)
+ package.loaded[module_part] = swap
+
+ -- restore the package path
+ package.path = old_paths
+end
+
+
+-- load and cache the mock-records
+local get_mock_records
+do
+ local mock_file
+ get_mock_records = function()
+ if mock_file then
+ return mock_file.records, mock_file.mocks_only
+ end
+
+ local is_file = require("pl.path").isfile
+ local prefix = ((kong or {}).configuration or {}).prefix
+ if not prefix then
+ -- we might be invoked before the Kong config was loaded, so exit early
+ -- and do not set _mock_records yet.
+ return {}
+ end
+
+ local filename = prefix .. "/" .. MOCK_RECORD_FILENAME
+
+ mock_file = {}
+ if not is_file(filename) then
+ -- no mock records set up, return empty default
+ ngx.log(ngx.DEBUG, LOG_PREFIX, "bypassing mock, no mock records found")
+ return mock_file
+ end
+
+ -- there is a file with mock records available, go load it
+ local f = assert(io.open(filename))
+ local json_file = assert(f:read("*a"))
+ f:close()
+
+ mock_file = assert(cjson.decode(json_file))
+ return mock_file.records, mock_file.mocks_only
+ end
+end
+
+
+-- patch the actual query method
+local old_query = resolver.query
+resolver.query = function(self, name, options, tries)
+ local mock_records, mocks_only = get_mock_records()
+ local qtype = (options or {}).qtype or resolver.TYPE_A
+
+ local answer = (mock_records[qtype] or {})[name]
+ if answer then
+ -- we actually have a mock answer, return it
+ ngx.log(ngx.DEBUG, LOG_PREFIX, "serving '", name, "' from mocks")
+ return answer, nil, tries
+ end
+
+ if not mocks_only then
+ -- No mock, so invoke original resolver. Note that if the original resolver fails (i.e. because an
+ -- invalid domain name like example.com was used), we return an empty result set instead of passing
+ -- the error up to the caller. This is done so that if the mock contains "A" records (which would
+ -- be the most common case), the initial query for a SRV record does not fail, but appear not to have
+ -- yielded any results. This will make dns/client.lua try finding an A record next.
+ local records, err, tries = old_query(self, name, options, tries)
+ if records then
+ return records, err, tries
+ end
+ end
+
+ return {}, nil, tries
+end
+
+-- do
+-- local semaphore = require "ngx.semaphore"
+-- local old_post = semaphore.post
+-- function semaphore.post(self, n)
+-- old_post(self, n)
+-- ngx.sleep(0)
+-- end
+-- end
+
+
+return resolver
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mocks/lua-resty-websocket/resty/websocket/peer.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocks/lua-resty-websocket/resty/websocket/peer.lua
new file mode 100644
index 00000000..ea36c414
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mocks/lua-resty-websocket/resty/websocket/peer.lua
@@ -0,0 +1,150 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local semaphore = require "ngx.semaphore"
+local semaphore_new = semaphore.new
+
+local remove = table.remove
+local insert = table.insert
+
+-- buffer
+local recv_buf = {}
+local recv_buf_mt = { __index = recv_buf }
+
+local default_timeout = 5
+
+function recv_buf.new()
+ return setmetatable({ smph = semaphore_new() }, recv_buf_mt)
+end
+
+function recv_buf:push(obj)
+ insert(self, obj)
+ if #self == 1 then
+ self.smph:post()
+ end
+
+ return true
+end
+
+function recv_buf:pop_no_wait()
+ return remove(self)
+end
+
+function recv_buf:pop(timeout)
+ if #self == 0 then
+ local ok, err = self.smph:wait(timeout or default_timeout)
+ if not ok then
+ return nil, err
+ end
+ end
+
+ return remove(self)
+end
+
+-- end buffer
+
+local unpack = unpack
+
+local _M = {}
+local mt = { __index = _M }
+
+local empty = {}
+
+-- we ignore mask problems and most of error handling
+
+function _M:new(opts)
+ opts = opts or empty
+
+ local new_peer = setmetatable({
+ timeout = opts.timeout,
+ buf = recv_buf.new(),
+ }, mt)
+
+ return new_peer
+end
+
+function _M:set_timeout(time)
+ self.timeout = time
+ return true
+end
+
+local types = {
+ [0x0] = "continuation",
+ [0x1] = "text",
+ [0x2] = "binary",
+ [0x8] = "close",
+ [0x9] = "ping",
+ [0xa] = "pong",
+}
+
+function _M:translate_frame(fin, op, payload)
+ payload = payload or ""
+ local payload_len = #payload
+ op = types[op]
+ if op == "close" then
+ -- being a close frame
+ if payload_len > 0 then
+ return payload[2], "close", payload[1]
+ end
+
+ return "", "close", nil
+ end
+
+ return payload, op, not fin and "again" or nil
+end
+
+function _M:recv_frame()
+ local buf = self.buf
+ local obj, err = buf:pop(self.timeout)
+ if not obj then
+ return nil, nil, err
+ end
+
+ return self:translate_frame(unpack(obj)) -- data, typ, err
+end
+
+local function send_frame(self, fin, op, payload)
+ local message = { fin, op, payload }
+
+ return self.peer.buf:push(message)
+end
+
+_M.send_frame = send_frame
+
+function _M:send_text(data)
+ return self:send_frame(true, 0x1, data)
+end
+
+function _M:send_binary(data)
+ return self:send_frame(true, 0x2, data)
+end
+
+function _M:send_close(code, msg)
+ local payload
+ if code then
+ payload = {code, msg}
+ end
+ return self:send_frame(true, 0x8, payload)
+end
+
+function _M:send_ping(data)
+ return self:send_frame(true, 0x9, data)
+end
+
+function _M:send_pong(data)
+ return self:send_frame(true, 0xa, data)
+end
+
+-- for clients
+function _M.connect()
+end
+function _M.set_keepalive()
+end
+function _M.close()
+end
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/ca.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/ca.crt
new file mode 100644
index 00000000..6b5555eb
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/ca.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFoTCCA4mgAwIBAgIUQDBLwIychoRbVRO44IzBBk9R4oYwDQYJKoZIhvcNAQEL
+BQAwWDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoM
+DEtvbmcgVGVzdGluZzEdMBsGA1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcN
+MTkwNTAyMTkzNDQyWhcNMzkwNDI3MTkzNDQyWjBYMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMS29uZyBUZXN0aW5nMR0wGwYDVQQD
+DBRLb25nIFRlc3RpbmcgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
+AgoCggIBAMp6IggUp3aSNRbLAac8oOkrbUnFuxtlKGYgg8vfA2UU71qTktigdwO6
+Kod0/M+daO3RDqJJXQL2rD14NDO3MaextICanoQSEe+nYyMFUIk+QplXLD3fbshU
+nHoJcMS2w0x4cm1os4ebxR2Evndo6luz39ivcjau+BL+9iBAYL1g6+eGOjcSy7ft
+1nAMvbxcQ7dmbAH2KP6OmF8cok+eQWVqXEjqtVx5GDMDlj1BjX6Kulmh/vhNi3Hr
+NEi+kPrw/YtRgnqnN0sv3NnAyKnantxy7w0TDicFjiBsSIhjB5aUfWYErBR+Nj/m
+uumwc/kRJcHWklqDzxrZKCIyOyWcE5Dyjjr46cnF8HxhYwgZcwkmgTtaXOLpBMlo
+XUTgOQrWpm9HYg2vOJMMA/ZPUJ2tJ34/4RgiA00EJ5xG8r24suZmT775l+XFLFzp
+Ihxvs3BMbrWsXlcZkI5neNk7Q/1jLoBhWeTYjMpUS7bJ/49YVGQZFs3xu2IcLqeD
+5WsB1i+EqBAI0jm4vWEynsyX+kS2BqAiDtCsS6WYT2q00DTeP5eIHh/vHsm75jJ+
+yUEb1xFxGnNevLKNTcHUeXxPUnowdC6wqFnaJm7l09qVGDom7tLX9i6MCojgpAP0
+hMpBxzh8jLxHh+zZQdiORSFdYxNnlnWwbic2GUJruiQVLuhpseenAgMBAAGjYzBh
+MB0GA1UdDgQWBBQHT/IIheEC2kdBxI/TfGqUxWJw9zAfBgNVHSMEGDAWgBQHT/II
+heEC2kdBxI/TfGqUxWJw9zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQsFAAOCAgEAqXZjy4EltJCRtBmN0ohAHPWqH4ZJQCI2HrM3
+wHB6c4oPWcJ+M2PfmYPUJo9VMjvn4S3sZuAysyoHduvRdGDnElW4wglL1xxpoUOx
+FqoZUoYWV8hDFmUTWM5b4CtJxOPdTAd8VgypulM3iUEzBQrjR6tnMOdkiFMOmVag
+0/Nnr+Tcfk/crMCx3xsVnisYjJoQBFBH4UY+gWE/V/MS1Sya4/qTbuuCUq+Qym5P
+r8TkWAJlg7iVVLbZ2j94VUdpiQPWJEGMtJck/NEmOTruhhQlT7c1u/lqXCGj7uci
+LmhLsBVmdtWT9AWS8Rl7Qo5GXbjxKIaP3IM9axhDLm8WHwPRLx7DuIFEc+OBxJhz
+wkr0g0yLS0AMZpaC6UGbWX01ed10U01mQ/qPU5uZiB0GvruwsYWZsyL1QXUeqLz3
+/KKrx3XsXjtBu3ZG4LAnwuxfeZCNw9ofg8CqF9c20ko+7tZAv6DCu9UL+2oZnEyQ
+CboRDwpnAlQ7qJVSp2xMgunO3xxVMlhD5LZpEJz1lRT0nQV3uuLpMYNM4FS9OW/X
+MZSzwHhDdCTDWtc/iRszimOnYYV8Y0ubJcb59uhwcsHmdfnwL9DVO6X5xyzb8wsf
+wWaPbub8SN2jKnT0g6ZWuca4VwEo1fRaBkzSZDqXwhkBDWP8UBqLXMXWHdZaT8NK
+0NEO74c=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/ca.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/ca.key
new file mode 100644
index 00000000..22f7391c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/ca.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAynoiCBSndpI1FssBpzyg6SttScW7G2UoZiCDy98DZRTvWpOS
+2KB3A7oqh3T8z51o7dEOokldAvasPXg0M7cxp7G0gJqehBIR76djIwVQiT5CmVcs
+Pd9uyFSceglwxLbDTHhybWizh5vFHYS+d2jqW7Pf2K9yNq74Ev72IEBgvWDr54Y6
+NxLLt+3WcAy9vFxDt2ZsAfYo/o6YXxyiT55BZWpcSOq1XHkYMwOWPUGNfoq6WaH+
++E2Lces0SL6Q+vD9i1GCeqc3Sy/c2cDIqdqe3HLvDRMOJwWOIGxIiGMHlpR9ZgSs
+FH42P+a66bBz+RElwdaSWoPPGtkoIjI7JZwTkPKOOvjpycXwfGFjCBlzCSaBO1pc
+4ukEyWhdROA5Ctamb0diDa84kwwD9k9Qna0nfj/hGCIDTQQnnEbyvbiy5mZPvvmX
+5cUsXOkiHG+zcExutaxeVxmQjmd42TtD/WMugGFZ5NiMylRLtsn/j1hUZBkWzfG7
+Yhwup4PlawHWL4SoEAjSObi9YTKezJf6RLYGoCIO0KxLpZhParTQNN4/l4geH+8e
+ybvmMn7JQRvXEXEac168so1NwdR5fE9SejB0LrCoWdombuXT2pUYOibu0tf2LowK
+iOCkA/SEykHHOHyMvEeH7NlB2I5FIV1jE2eWdbBuJzYZQmu6JBUu6Gmx56cCAwEA
+AQKCAgBh8MQHbp42r7B4bwhEsgIP5868kaXZMYxiIjY+ZojI22CQSrQMj0oihmnO
+Dhu//Z9k8ewHOj+AkHtuXHe70FB3knECiEhHEEqWxzwgE5EKYhBrBgzDfRGkW7E5
+ItnmfZVopxaKr8uvu/yUM8LCFgDPDOopcWxo4SfkYGoD3cAtuvVBj98XBsN+G9DP
+cIpS07p5u1RheoYH5Ef2Me6dXqq5eMJdDxNdQMIg4wpIZS4hWM+dTcv8pd3e4+vt
+iCivCeVK/8mCtOH9P5Cv0B4Ac1zGu93AUEhXPcurCVXoiyZ/gyJJN9dZLlflfyFI
+qu7eOpot8jHnEL0cepB8Qhn0LlQTuv6rjJqmnl3tJA3S6rcM/mOjihkk1uo7JdDK
+vH498XR5qZPDlXZ8PVu3nI5EgXpmFIcCBuuVFS5QI63NZ32YqoGYXK37K7C9lQsL
+L/iR+YpwuQqDmM+UEETjBCIMKvxghFH0ICR041yg9tkjRhNKCAGc6n70wQDUq57s
+jjZmTQ4ZydxCsWVjLo7fCcoyQ9B7IUGPUUn8WavPUwtz1kG6VK7RDGa0KtgCD0vc
+iEwbWi9uwkZdoZdHcB8qLgCPjMGgRJLOyJ67xQ0RP+r+WkhUAjYcaucFonyyBhtv
+OrqNyEM3SEpgrzgttyyg+dP/cDvPbp4NXoxKAMyd8c7mjPploQKCAQEA+BL/qxLe
+LTKwe3TKpjAeyTB2iOxoWjtCqe3/xKbTS32Tn/VGwqhXyNeh+FTRhQu7fg5iL2C2
+JCOdYXWxRYIBwUh4HfApkgYzznUAU2vOh653MzW6LsOtDdgYF2cijN1ZFcbRTGpw
+eoA6U/cijuglwpTHF7zmRd9rSsv+PZ/fTDgY82MOdeaOUwyKuVyPUpNWfqSwxPd9
+tWEdOYjgq1llPbl1mktR0gYHIdHcSr1By7kmFw3/TQuic5Nm+FDidtfJYO36xFI1
+/CfwGVYeH42iul+KzdlITLAMRm2PAcWFjvxpw78T+xeUNpZlnZSgCIjtpfjywmXb
+uQvJoMMEX5PN1wKCAQEA0PIx4sgXiwqASa/foBB0Tk5jG3QWxucpqnLJURZeRqLQ
+BmF4WRrjs5V2y6iizegIcNmL0ZfwFBU79HwtAgFiTELLQL2xivhpMVjXL7QHeE4r
+A/9+49iO8wu8W/hwKxCDdGqXKyCKtW9b1yfUVB09j29GtApcV9v8KCTmDwYGrHI0
+DcEMtNLUbJvUeWFYFadJNFKxbsBtJPJIrYaiIyv2sL+Y3tZrYES72tTAYhMFwd0r
+9ooL5Ufrpuh4pHOxxA0Sh0EVUhNmyoq/ZJZ5wia+WB5NXBSD9JbciC5M4J8BMl/u
+Bx5RZbJSoAktYiOzev//6NHUmXsDjg3Kh9P48JVasQKCAQBVjt/k1bYQ6pmZirdV
+x+TmSLOpF7gJ3sRoLTB4V30qXR4sHgEQo9Ta7RvstPwqIdjBah6M7pMDNdFSyq+g
+JG2Mhvz+flUoCsGVZB7/pn/tpctwuwgClvQ5gR0V/TkaUkEmVJLdAxzV8yGq0eJ2
+XTSgvoVH95uH3712Z5LBGEGAXRyl3LUhDqpplDrIIVdBCJXdSdm5pQ4TH3Jf5Ihw
+MH3NYwhfdbi7cd7F2EZc9Jcbtzie3PH/VZLqv5zU6bihelz29Dz3ts7tr6yMYHo1
+Mbk9BDSwOE9KO7GQHLskxkYBAadMnrs6b3Brv0U+qwLizq7//jNjvpOgZ6Nbscbx
+W92zAoIBAQCNCK17cavSgggNtNSw6epXYLmssjMdlrKdBlW0kfCYpRTc+bWOD4Ra
+lyxUU0Nw0InCAlVJ59B4/cw2PgrzK5P5/avLyz6nmv0F/f1hiZbxMXH/hNlVWbtD
+ekxtl8e+iarxTXEz/wchaEUJeSzsicAfrPCAXe3ur+IIBr/yrBKdG4jfL8sv0o7n
+sFc+huI522yiEJ8LLn99TLyZxCJ0sxwUOX8qCnj3xe02zBv/Fu/v5yXhh1R4Mo9x
+XcDw39bBikFTYi7N86KSXAzMDHWrAxO/ztRQrthSo/G/SeFCTJE2O2IjE+fFSRRU
+SV2EvKxM/bbyo49o+YtwuwZVoFKLsYRBAoIBADaL9sx49XTHIIFGqEQP7NLEhs7D
+eJgSKP5oQ54J0iaoVpsoxng8DrTBkMVW75hiWzTW75EJnMXrauo/YfAbvsMM//3e
+BfRWvYpS5xKcHmXg2QJxy2VpvElHLg5Y2lligEZhO+5Sm2OG/hixBmiFvEvxPEB8
+8YIvYKcRAGA/HgDY9hGWSNsBP7qDXWP5kRm8qnB6zn33TVZMsXwUv6TP0cwsBKf7
+XDbnPBpOQK9nicehY7oscy9yTB9Q3bUHecYLY822ueCwaJgwJWFUH+Xe4u6xIH5l
+A/IyIfyOqxjUc34Me+37ehNmbTIxZ1BqLddppm9QsSAD7cDMurfb3pRpju4=
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example.com.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example.com.crt
new file mode 100644
index 00000000..8a3257f9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example.com.crt
@@ -0,0 +1,63 @@
+-----BEGIN CERTIFICATE-----
+MIIFTDCCAzSgAwIBAgICIAAwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzElMCMG
+A1UEAwwcS29uZyBUZXN0aW5nIEludGVybWlkaWF0ZSBDQTAeFw0xOTA1MDIxOTU1
+MjFaFw0yOTA0MjgxOTU1MjFaME8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxFDASBgNVBAMMC2V4YW1wbGUu
+Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqCozbPPts2H7CsUf
+4KlyKwbCOjqUa7ZhBcXfX5KuEHOvAUZfJOlm2TCNbO1wMTI1QHwjn6a9HM1njhBG
+4r9HH8CLckF83b247iJQbqUEjjvbb6DMTxjbC7dBunIikv6gUXeWGlRHupy/UEh8
+K0Y2KM2fm+HEbKI6zvjg/wb7zb0agzNaKV6fyEourKL0Xjz8ePm3kH58HaUmqhfk
+PPf7GnGW1xk/aIm6tsi9wzj2VBI/T3E5hVnMGrJTYnXh5DoFQrbuLvWtOB6MdZcM
+BWN/he8ISvvhKrctjWvUjpWgoZE9bRoMxkzxpHF/agM++WlHJrJ7my3yRHN3LspF
+4ER+/QIDAQABo4IBHzCCARswCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAw
+MwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZp
+Y2F0ZTAdBgNVHQ4EFgQUlpSl7QvKjdvJLx/sI3CXST3SqwowgYEGA1UdIwR6MHiA
+FAsOBA6X+G1iTyTwO8Zv0go7jRERoVykWjBYMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMS29uZyBUZXN0aW5nMR0wGwYDVQQDDBRL
+b25nIFRlc3RpbmcgUm9vdCBDQYICEAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQM
+MAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQA6kfAONRKeg0ygcyY8OwVi
+y9SGPKnKtOGjF2BSk74UF3bj63kx7utIQ0w5LZA3CwjcE2VzdPx+lQEDy/dbv442
+2bWrc7kG0/Dcr92KoUbzuuI0kPRoM/4rcOb8shKG9txFL1j44a60SWlbvkoNUD88
+vKZy2uSJ++OeQ7vuxrt2UDsfO2jRk74p/ztQibTX/cpwjvHrz2JcLTSxUZXK342x
+o7bWl1f7XMn8o7nPtNWHZq418uwFJ6OZO/rLc+FxE+31SnHYJUYC6/TSAg9kGomk
+Ws+K453QVoiPsG08Uz1JRjUQWotlEmqFAwax3kmfnrsiKmKy451CcwVAlyEIvnSb
+s2hEePHUaJltsatvFNPLnjcsOtqA46zJN0mv63BKuoa9fWAYr81D8wilcPgx534j
+KQcSv24cAoWesp/KhERK5G+F5mE0qnlCfMpFJFtfMjh+CDLbR//L4/0KQrSS/eRn
+ooeXinTpO5S2WOxk0W96rZMsBL2rBUI2qhfjBW8aQAiew4cMtddxryyUKskDlJPx
+bZXB2OmPibOOOTrTrBFkQ+tjKCuPKbOQDsIPTasZQKc2jK0boixXE8AXhN/A+3J4
+muvYnypmWGb0jMLEQT2u+RQzCNDjIOEHBP50XnoEX3jkOgEwknje89VDm/JXcClR
+l5HH4/9/AbS7rFCRnphOjQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFmjCCA4KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzEdMBsG
+A1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcNMTkwNTAyMTk0MDQ4WhcNMjkw
+NDI5MTk0MDQ4WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEV
+MBMGA1UECgwMS29uZyBUZXN0aW5nMSUwIwYDVQQDDBxLb25nIFRlc3RpbmcgSW50
+ZXJtaWRpYXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0dnj
+oHlJmNM94vQnK2FIIQJm9OAVvyMtAAkBKL7Cxt8G062GHDhq6gjQ9enuNQE0l3Vv
+mSAh7N9gNlma6YbRB9VeG54BCuRQwCxveOBiwQvC2qrTzYI34kF/AeflrDOdzuLb
+zj5cLADKXGCbGDtrSPKUwdlkuLs3pRr/YAyIQr7zJtlLz+E0GBYp0GWnLs0FiLSP
+qSBWllC9u8gt2MiKyNlXw+kZ8lofOehCJzfFr6qagVklPw+8IpU6OGmRLFQVwVhp
+zdAJmAGmSo/AGNKGqDdjzC4N2l4uYGH6n2KmY2yxsLBGZgwtLDst3fK4a3Wa5Tj7
+cUwCcGLGtfVTaIXZYbqQ0nGsaYUd/mhx3B3Jk1p3ILZ72nVYowhpj22ipPGal5hp
+ABh1MX3s/B+2ybWyDTtSaspcyhsRQsS6axB3DwLOLRy5Xp/kqEdConCtGCsjgm+U
+FzdupubXK+KIAmTKXDx8OM7Af/K7kLDfFTre40sEB6fwrWwH8yFojeqkA/Uqhn5S
+CzB0o4F3ON0xajsw2dRCziiq7pSe6ALLXetKpBr+xnVbUswH6BANUoDvh9thVPPx
+1trkv+OuoJalkruZaT+38+iV9xwdqxnR7PUawqSyvrEAxjqUo7dDPsEuOpx1DJjO
+XwRJCUjd7Ux913Iks24BqpPhEQz/rZzJLBApRVsCAwEAAaNmMGQwHQYDVR0OBBYE
+FAsOBA6X+G1iTyTwO8Zv0go7jRERMB8GA1UdIwQYMBaAFAdP8giF4QLaR0HEj9N8
+apTFYnD3MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4ICAQAWzIvIVM32iurqM451Amz0HNDG9j84cORnnaRR5opFTr3P
+EqI3QkgCyP6YOs9t0QSbA4ur9WUzd3c9Ktj3qRRgTE+98JBOPO0rv+Kjj48aANDV
+5tcbI9TZ9ap6g0jYr4XNT+KOO7E8QYlpY/wtokudCUDJE9vrsp1on4Bal2gjvCdh
+SU0C1lnj6q6kBdQSYHrcjiEIGJH21ayVoNaBVP/fxyCHz472w1xN220dxUI/GqB6
+pjcuy9cHjJHJKJbrkdt2eDRAFP5cILXc3mzUoGUDHY2JA1gtOHV0p4ix9R9AfI9x
+snBEFiD8oIpcQay8MJH/z3NLEPLoBW+JaAAs89P+jcppea5N9vbiAkrPi687BFTP
+PWPdstyttw6KrvtPQR1+FsVFcGeTjo32/UrckJixdiOEZgHk+deXpp7JoRdcsgzD
++okrsG79/LgS4icLmzNEp0IV36QckEq0+ALKDu6BXvWTkb5DB/FUrovZKJgkYeWj
+GKogyrPIXrYi725Ff306124kLbxiA+6iBbKUtCutQnvut78puC6iP+a2SrfsbUJ4
+qpvBFOY29Mlww88oWNGTA8QeW84Y1EJbRkHavzSsMFB73sxidQW0cHNC5t9RCKAQ
+uibeZgK1Yk7YQKXdvbZvXwrgTcAjCdbppw2L6e0Uy+OGgNjnIps8K460SdaIiA==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example.com.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example.com.key
new file mode 100644
index 00000000..c8ad8f2e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example.com.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAqCozbPPts2H7CsUf4KlyKwbCOjqUa7ZhBcXfX5KuEHOvAUZf
+JOlm2TCNbO1wMTI1QHwjn6a9HM1njhBG4r9HH8CLckF83b247iJQbqUEjjvbb6DM
+TxjbC7dBunIikv6gUXeWGlRHupy/UEh8K0Y2KM2fm+HEbKI6zvjg/wb7zb0agzNa
+KV6fyEourKL0Xjz8ePm3kH58HaUmqhfkPPf7GnGW1xk/aIm6tsi9wzj2VBI/T3E5
+hVnMGrJTYnXh5DoFQrbuLvWtOB6MdZcMBWN/he8ISvvhKrctjWvUjpWgoZE9bRoM
+xkzxpHF/agM++WlHJrJ7my3yRHN3LspF4ER+/QIDAQABAoIBACHkx5KpI3qpP+ju
+zDsCzAECDrmfvvRqwOlh9WCU9sJYHqi6H0kYReN2lrqirJ8tyG/j1WZDPBCHEd0f
+SLpA5TvwGesAagNjTteoUN/MILvuMo8wMJ2sm9GjsPq8MF3CNlvVJ4rM+9wP5btv
+sJ8kOpxEvWu0uFtQ41t97BNau/u+UtMk3oNCYBhiUWDg0rWPrUeX8cKzFSM8VAt4
+vvsybRHPbBmSLW01xO1Hq5cZdqbN4SxyQC1Ug9gW/afJQNRK7CubpWjOOQAsla0j
+ExyBxMMwDLLZfYCQpRn92ZB4x+LiaXqnbrtyfA+GLLjtlUPY6ClpdXa7KSN/mEuE
+LIIjiV0CgYEA2YsCt1+Yak+GOX6tSx1YUDz6UpZyOo2OKwqJ+G+SKT7JLT84A5nP
+rn6r6UUpNKhK3glpU1A1VJKiFnZ9qJi/gHJcNiSEIcUFHNFflDSmiZc6MyfcIkCZ
+xLUCrYHpETubTnB5P5jOhmsA3/uApaAc/Pv5hfSRzQv524k08TRgCU8CgYEAxeSQ
+MIRV1SKDYsEdEfXJE+WJLz2rlpR19l/9Wfuc5QVgAgDhFCCvHUX1ULU8yGQUSHqz
+DseR3iQF+Jvo3MK7pgC2fH6UePjakWOCXqXey1CpAzUHM/Qhwd451VqAGAT+Pabj
+tzPJ0cSC7sszxhwmAzotIUrjZDbuAzwQyRXdh/MCgYEApP2KVNt69H5V9bs+4W5j
+MY/d5s9V2VTNE5XNqI+uEfwdhmShLhH08on+BlC+/MH67kXDDT4TBI6lwlWh3kHj
+VB7oEuRFFnuf8ghV7ki0WjxJFs1PZucJ+Ke0XTXfN4O2uZoSS4qwcEAtjLLqEjPK
+aJEO4WrpPdOsb7WzYpDvmX8CgYEAwPunXZkAN0xkAl8+4S/mup+Ci+5BMiRvcSek
+4yaLl5AJU4rV9JH3E74QgHdt4iIu4Yu+qHAYoSBSLmKk0PyakEVrsLakReCxDU2U
+aoapYW60k6sX7iNq9CuqDJUoC8R6x1bEBPndG9LeuM6zG8SBkW4farMkU6t5qu/d
+kqvfEN8CgYAQphK9AoATrEomLbGwmcW+8JkjU9Sxnq+zo8io75wFAY2cUnQJQEId
+zGWwYjwHxXQCNCZ3ZwD7+iYgFHfxbPaiTWELkV2nEpBHQI0cLgzlcEBwo+uoFiYK
+33Yxb6EhNFSy7d2GPVZMjIR+KifCIem+i/3BiIlzneuFSRlnKORekQ==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example2.com.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example2.com.crt
new file mode 100644
index 00000000..85756548
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example2.com.crt
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEJzCCAg8CFAQ6oTnLBUHbumx1bxyY9kV0W21BMA0GCSqGSIb3DQEBCwUAMFgx
+CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxLb25n
+IFRlc3RpbmcxHTAbBgNVBAMMFEtvbmcgVGVzdGluZyBSb290IENBMB4XDTIwMDQx
+MzIzNDg0MVoXDTMwMDQxMTIzNDg0MVowSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
+AkNBMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxFTATBgNVBAMMDGV4YW1wbGUyLmNv
+bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+kZhxdN8PA3SW9cXiv
+xgtANq57PIWNnSDsg9Yxn0/+JKR45pSU+SKYtZUJJJuOdkv4IIJLm6uG6LbOPUDO
+g9EaV0Zw7RQtbY6EDFDFzeyq0/Mwl9wLJtXf0fPsXGyFIdeelBjzoSVsGGJKPWbP
+rlUtSHCrpFX53NTPnNVUJz9V6CdzZJgbyoiWP7ggKJeRPq6jCW5pt+cd+sR4+EPh
+daBmEVWeifLEKCbBvsQaOGfU1aVG1AlX0RpLBkTxOOFIIk/3dgWOsrek2ofjku4F
+g0MeWmD8oXOHUX2JxO77/BbLDQt0lzD27y/EkDoqy6mMAH85/LTYrU+P0WsEyexg
+CHcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAwWAxQjQOoGxU5LQu4ZmsCkps9y0B
+kNj8MUpLcFmK+02VIUh3hM4vuB6Gct2Ph+6zqCge3oqTXltU0Bs2MTwAKs/scsxq
+Mtanz4W00UlmG3QdgHaEs196tYQ8cKIaGZsNBv15VgBBduUG478pKqYE8bJKBbw7
+1Ym390hSPo0dNe7jLFXx6AaJvlEYh09P4FgfkXuY5VVTGXfN7XgJI073pLRY6iGH
+Qd+Egymh86AQtnoNpmqSCMNcjRVAyR8Ti3qnyro8ruZCnNYHieGeh/ZsZvhGDeWX
+v4YXjW2NDQ5+Ok6Gtvhf/l6RSrnXLbZtv8NStqwQJ+ydu05BJglZ/7Sb0uQYVNq2
+H8V+MtApFT6fG6ANM6hadNFG+p8Hwz6k4BLrc9ZxeWYKWIIusqExR9JIlGzEjvFJ
+6NmNjm3eZE9Ue4YgURj1KTr53wAso4LbJpz/zuZS+m9PMz7n8fRL25/Z5b/92L3a
+w0vsxUJyTDeMvYf8oT6OkxNVJ0zBRZNtEg5AJKdP6nU53V998jHP9vUisrU2ALhu
+Jw3QiWiDKnRtx8PiiRx7dWo+Xwn9+xVypytqNz3w/XJlOjMwOg73q399w+vMiFTl
+qdr7eYvaQBGOZVc3OdiP8afyVxlhHBowUoi8G+iPbgOsARHv/j4UeMVyIThzxv73
+a2EQ5BzyOzQ81H4=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example2.com.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example2.com.key
new file mode 100644
index 00000000..05c29251
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/mtls_certs/example2.com.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAr6RmHF03w8DdJb1xeK/GC0A2rns8hY2dIOyD1jGfT/4kpHjm
+lJT5Ipi1lQkkm452S/gggkubq4bots49QM6D0RpXRnDtFC1tjoQMUMXN7KrT8zCX
+3Asm1d/R8+xcbIUh156UGPOhJWwYYko9Zs+uVS1IcKukVfnc1M+c1VQnP1XoJ3Nk
+mBvKiJY/uCAol5E+rqMJbmm35x36xHj4Q+F1oGYRVZ6J8sQoJsG+xBo4Z9TVpUbU
+CVfRGksGRPE44UgiT/d2BY6yt6Tah+OS7gWDQx5aYPyhc4dRfYnE7vv8FssNC3SX
+MPbvL8SQOirLqYwAfzn8tNitT4/RawTJ7GAIdwIDAQABAoIBAHMJzgdN1rRToYSS
+a7uMBL5htG7bMGyYsA1cW4zyu1F9NyqyNPOkDvjl5ChU8LEhwcFIJqKwOqlBlzIE
+KoJDwHo4MmlklSLeDh+FxTsyEwmraV6iuRPaCfmSusR0TqSVHfFHX+Bn0WfdQKs/
+zK+F3rzTB9sj0GKvYD/SKvpeP8Zuu9EBqo4N7PU3VHwDq5t32Ut/+M5XWtulsQcs
+qXr2R3agj/DnODANT6Dn/mJboTrYOSV18S/Yw/+OnWBcLzlT5sj0aLgrtXvIputv
+9caux4HklAQr29+lKB8nBTfjhXnBntMaEgqCVJ3ri83MuEfVDhmjwo6PnX22/J0h
+2XbCyUECgYEA2v8m+CTBTjdAqOuje34+UiWRzT2P9OFONV8nYgzEcQW5JkUoFCun
+KgQQIvjCsX4jY6/8w/IPF1ieTconZYJUWSyMZFtBBDCVif1GZRiiM2C4Zcero1KV
+U0V3wZcnYkzafzIHkqFUYZwamvdKWVI4c6F5MhSEKCgcbgKKI52TEokCgYEAzVHr
+KjQt+dqNkbipYoGH2ywLdcogDwKoyUFbgcvz/q625gu4am025wF25yRKExm7Dyjx
+eCQC+KOsBfJSc78fG5R6KPIDK1JrpUEGSCeqFICiqGv9kUzPf5zeGZVf9cU4tyPT
+5wYUEM7NX8VRoasZ4OUvYyYBw1Cx8vMdvQn/gv8CgYAIhxcFYqkEWrJx4XskO+5B
+VKUw0MziREO/YE0wTD76B7cF/ntpDaocwLvAIN+z+a13HEtDdhGQXysK7GxMT57p
+OgrdfZAykZHBJdOv7B2k0odbr0LHwVd/Pp1DNJecBFId0dzpoM6gXmvKzQZgJAt+
+tTL6+EGNLsKspfyrFl+7wQKBgQDAt2VuJbAJ1xQOdS+4IDCujfbrxp60uCBJVylW
++WK56LAP2WxtqLlhtsQuTKeiqgIkRp/vzo1jZ+0tX7f4oKnIL2NCT3aeESys3g3R
+aDmCKQOD5mkJGvmgpFLr3INHoqiLbfuV2uS2qgWnIQRwJLOTnksOWzxIYdPFYGDH
+cTz9bQKBgQDGv929DUinrKXe/uKJHLAcq+MjmF/+kZU9yn+svq6SSdplqp7xbXX4
+3T5HCWqD4Sy+PVzGaDg5YfXC8yaFPPfY0/35T2FoQEiCAPQO+07Smg6RqJ3yVpIm
+LTsbLleJTc8CX0bI4SukQ7MVQsiHimzyEzx3eyLt1S8aBdJuRFZ2mg==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/nginx-directives.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/nginx-directives.conf
new file mode 100644
index 00000000..c7a73f59
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/nginx-directives.conf
@@ -0,0 +1,8 @@
+nginx_http_variables_hash_bucket_size = 128
+nginx_stream_variables_hash_bucket_size = 128
+nginx_http_lua_shared_dict = custom_cache 5m
+nginx_stream_lua_shared_dict = custom_cache 5m
+nginx_proxy_proxy_bind = 127.0.0.1
+nginx_sproxy_proxy_bind = 127.0.0.1
+nginx_admin_server_tokens = off
+nginx_status_client_body_buffer_size = 8k
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ca-chain.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ca-chain.crt
new file mode 100644
index 00000000..9c3449c5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ca-chain.crt
@@ -0,0 +1,65 @@
+-----BEGIN CERTIFICATE-----
+MIIFmTCCA4GgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsGA1UECgwES29uZzEUMBIGA1UE
+CwwLRW5naW5lZXJpbmcxEDAOBgNVBAMMB3Jvb3RfY2EwHhcNMjEwMzA0MTEyMjM1
+WhcNNDEwMjI3MTEyMjM1WjBZMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExDTAL
+BgNVBAoMBEtvbmcxFDASBgNVBAsMC0VuZ2luZWVyaW5nMRgwFgYDVQQDDA9pbnRl
+cm1lZGlhdGVfY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXcXvt
+kyiEY7Nr8VeEF2KTpGkEwPLNoLW9eNpFH0bFlKcmM/+IocSUErD091SCf0fGCRuo
+8bISb4MVa5esq8XMUSR63cDmz2IbWOtvNir4wLfAoUuA4JBhubaSyaslXLf376QT
+sYDjLH9jQ3rFYskz9hrlX1HFFmm0hlDnuqr1w0GF+6PxPdxRKkdvKexPuQp3qaVZ
+dzKvFcGuCu7nuqemb37CBBkdRAgVUj37pXBIcc5p5h8PVAU24r7pAaYppDmmZMWa
+uTSjz4K/PTh5GzG+snf8iH+EpTLGUqPElR+ABP8YiNrQQzgA91lPjwLqp6D34sMT
+7xvk4Ri1cOpOsNA4hCdFPilzolMQ6Zpz0ELI25vBt8qY57JMlsUjaY2PR1gI+pE+
+jc52e18Bt/axNnbgxoxTQWPcw27mUs7H4+1WiZufhzz59obgWRnoCom0Fb6RN9Rl
+8ezl+h30+Dgk8ftiFm/fI1BttL6dihveSP/xvuMYRFyT0F1ZNZhgME218cOB5hZN
+dKOFynRgI4SfCeMNSy3KnrBoPYE3P/f1ljBbrR5x/xQE0V5iWFJwZwWexO0+Hf7h
+Aaql5dcwsMIjf8MfHKuQOfZauUmPxu0EbM4NiQu5GEK/9rGEIO51Tlo237l6k4tF
+oKCj3EUZ9cM02CROKGDM6vfkyeyGDbuDPGmdLwIDAQABo2YwZDAdBgNVHQ4EFgQU
+xTAm/Gj7/9K32+Wdc0BOHFKRFeEwHwYDVR0jBBgwFoAU9NE7mu7p9CqGxLNWl7km
+VfMeEO0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
+hvcNAQELBQADggIBAKFvNX1Wv7lOsYvDGOCmIrJEDdjW2Q60p23v1U/R9Wv7xo7X
+2SRuQvqKd3AB/9dCSgaaXKHwYgTPIS8NwUJ0SMvSwrnnRpZS5qkOB9JgRgAX5Ebd
+eyupB2AumZ1BGaw2gqPYHm8zxu3N2yw2pVV9LJ2nM+IPTqiQrYCV7BxNpAd/v9OA
+EC9XbKhPqdJ4bD6dGg7w5iBPadb6amAKkGutKjjB+AC/lJlM9bMEGd6RP0ywptQx
+jAfY0VTElLsN30Q6pn31Xf4UzZk4xzyW04GaPFcJVoHTWSl969p0k0L/WAMakDHB
+/g4VvkMTFDoH1Mi7ohakHnMC9XQbMVj2t/EE3XLiD4gcNEyCjXczIxDYJRYe2X3f
+51vQNR921P1KUNTooGusltMmHuWBnT046o9rp/2uQvHm2y/qv1kCPHTiP7vhb/TG
+2JCc+3LZ621EjH5jRvL60Pji4RnGGqLDBykLK68dymHVfrSAi+ZCx6PNxSm0Ydm6
+ZM1Vb8lD2EwEm20qKWM484ItWcVHgWEWDvaMjh0iIq45LA0KmN47iUN8X6rmdulZ
+MDSnSYwJfRt1DdyUC0nDWMQaW1JOQxQxoJCoDmiLwv9BIeNB8LNJEU0FTOPc8xhf
+VdlbjNIC1fs2OMWOc3A1hAFlf+vU8UYLRgYhLiAhFT2iwhBksSzGURY7eKqM
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFrTCCA5WgAwIBAgIUFQe9z25yjw26iWzS+P7+hz1zx6AwDQYJKoZIhvcNAQEL
+BQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsG
+A1UECgwES29uZzEUMBIGA1UECwwLRW5naW5lZXJpbmcxEDAOBgNVBAMMB3Jvb3Rf
+Y2EwHhcNMjEwMzA0MTEyMjM0WhcNNDEwMjI3MTEyMjM0WjBeMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMQ0wCwYDVQQKDARLb25nMRQwEgYD
+VQQLDAtFbmdpbmVlcmluZzEQMA4GA1UEAwwHcm9vdF9jYTCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBAKKjido39I5SEmPhme0Z+hG0buOylXg+jmqHpJ/K
+rs+dSq/PsJCjSke81eOP2MFa5duyBxdnXmMJwZYxuQ91bKxdzWVE9ZgCJgNJYsB6
+y5+Fe7ypERwa2ebS/M99FFJ3EzpF017XdsgnSfVh1GEQOZkWQ1+7YrEUEgtwN5lO
+MVUmj1EfoL+jQ/zwxwdxpLu3dh3Ica3szmx3YxqIPRnpyoYYqbktjL63gmFCjLeW
+zEXdVZyoisdaA4iZ9e/wmuLR2/F4cbZ0SjU7QULZ2Zt/SCrs3CaJ3/ZAa6s84kjg
+JBMav+GxbvATSuWQEajiVQrkW9HvXD/NUQBCzzZsOfpzn0044Ls7XvWDCCXs+xtG
+Uhd5cJfmlcbHbZ9PU1xTBqdbwiRX+XlmX7CJRcfgnYnU/B3m5IheA1XKYhoXikgv
+geRwq5uZ8Z2E/WONmFts46MLSmH43Ft+gIXA1u1g3eDHkU2bx9u592lZoluZtL3m
+bmebyk+5bd0GdiHjBGvDSCf/fgaWROgGO9e0PBgdsngHEFmRspipaH39qveM1Cdh
+83q4I96BRmjU5tvFXydFCvp8ABpZz9Gj0h8IRP+bK5ukU46YrEIxQxjBee1c1AAb
+oatRJSJc2J6zSYXRnQfwf5OkhpmVYc+1TAyqPBfixa2TQ7OOhXxDYsJHAb7WySKP
+lfonAgMBAAGjYzBhMB0GA1UdDgQWBBT00Tua7un0KobEs1aXuSZV8x4Q7TAfBgNV
+HSMEGDAWgBT00Tua7un0KobEs1aXuSZV8x4Q7TAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAgI8CSmjvzQgmnzcNwqX5
+o+KBWEMHJEqQfowaZE7o6xkvEljb1YHRDE0hlwUtD1vbKUthoHD8Mqim3No5z4J0
+dEE+mXQ3zlJWKl5gqHs9KtcLhk51mf4VJ2TW8Z7AoE2OjWSnycLNdlpqUvxzCQOn
+CIhvyDfs4OV1RYywbfiLLmzTCYT7Mt5ye1ZafoRNZ37DCnI/uqoOaMb+a6VaE+0F
+ZXlDonXmy54QUmt6foSG/+kYaqdVLribsE6H+GpePmPTKKOvgE1RutR5+nvMJUB3
++zMQSPVVYLzizwV+Tq9il81qNQB2hZGvM8iSRraBNn8mwpx7M6kcoJ4gvCA3kHCI
+rmuuzlhkNcmZYh0uG378CzhdEOV+JMmuCh4xt2SbQIr5Luqm/+Xoq4tDplKoUVkC
+DScxPoFNoi9bZYW/ppcaeX5KT3Gt0JBaCfD7d0CtbUp/iPS1HtgXTIL9XiYPipsV
+oPLtqvfeORl6aUuqs1xX8HvZrSgcld51+r8X31YIs6feYTFvlbfP0/Jhf2Cs0K/j
+jhC0sGVdWO1C0akDlEBfuE5YMrehjYrrOnEavtTi9+H0vNaB+BGAJHIAj+BGj5C7
+0EkbQdEyhB0pliy9qzbPtN5nt+y0I1lgN9VlFMub6r1u5novNzuVm+5ceBrxG+ga
+T6nsr9aTE1yghO6GTWEPssw=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ca.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ca.crt
new file mode 100644
index 00000000..a33350b0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ca.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFrTCCA5WgAwIBAgIUFQe9z25yjw26iWzS+P7+hz1zx6AwDQYJKoZIhvcNAQEL
+BQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsG
+A1UECgwES29uZzEUMBIGA1UECwwLRW5naW5lZXJpbmcxEDAOBgNVBAMMB3Jvb3Rf
+Y2EwHhcNMjEwMzA0MTEyMjM0WhcNNDEwMjI3MTEyMjM0WjBeMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMQ0wCwYDVQQKDARLb25nMRQwEgYD
+VQQLDAtFbmdpbmVlcmluZzEQMA4GA1UEAwwHcm9vdF9jYTCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBAKKjido39I5SEmPhme0Z+hG0buOylXg+jmqHpJ/K
+rs+dSq/PsJCjSke81eOP2MFa5duyBxdnXmMJwZYxuQ91bKxdzWVE9ZgCJgNJYsB6
+y5+Fe7ypERwa2ebS/M99FFJ3EzpF017XdsgnSfVh1GEQOZkWQ1+7YrEUEgtwN5lO
+MVUmj1EfoL+jQ/zwxwdxpLu3dh3Ica3szmx3YxqIPRnpyoYYqbktjL63gmFCjLeW
+zEXdVZyoisdaA4iZ9e/wmuLR2/F4cbZ0SjU7QULZ2Zt/SCrs3CaJ3/ZAa6s84kjg
+JBMav+GxbvATSuWQEajiVQrkW9HvXD/NUQBCzzZsOfpzn0044Ls7XvWDCCXs+xtG
+Uhd5cJfmlcbHbZ9PU1xTBqdbwiRX+XlmX7CJRcfgnYnU/B3m5IheA1XKYhoXikgv
+geRwq5uZ8Z2E/WONmFts46MLSmH43Ft+gIXA1u1g3eDHkU2bx9u592lZoluZtL3m
+bmebyk+5bd0GdiHjBGvDSCf/fgaWROgGO9e0PBgdsngHEFmRspipaH39qveM1Cdh
+83q4I96BRmjU5tvFXydFCvp8ABpZz9Gj0h8IRP+bK5ukU46YrEIxQxjBee1c1AAb
+oatRJSJc2J6zSYXRnQfwf5OkhpmVYc+1TAyqPBfixa2TQ7OOhXxDYsJHAb7WySKP
+lfonAgMBAAGjYzBhMB0GA1UdDgQWBBT00Tua7un0KobEs1aXuSZV8x4Q7TAfBgNV
+HSMEGDAWgBT00Tua7un0KobEs1aXuSZV8x4Q7TAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAgI8CSmjvzQgmnzcNwqX5
+o+KBWEMHJEqQfowaZE7o6xkvEljb1YHRDE0hlwUtD1vbKUthoHD8Mqim3No5z4J0
+dEE+mXQ3zlJWKl5gqHs9KtcLhk51mf4VJ2TW8Z7AoE2OjWSnycLNdlpqUvxzCQOn
+CIhvyDfs4OV1RYywbfiLLmzTCYT7Mt5ye1ZafoRNZ37DCnI/uqoOaMb+a6VaE+0F
+ZXlDonXmy54QUmt6foSG/+kYaqdVLribsE6H+GpePmPTKKOvgE1RutR5+nvMJUB3
++zMQSPVVYLzizwV+Tq9il81qNQB2hZGvM8iSRraBNn8mwpx7M6kcoJ4gvCA3kHCI
+rmuuzlhkNcmZYh0uG378CzhdEOV+JMmuCh4xt2SbQIr5Luqm/+Xoq4tDplKoUVkC
+DScxPoFNoi9bZYW/ppcaeX5KT3Gt0JBaCfD7d0CtbUp/iPS1HtgXTIL9XiYPipsV
+oPLtqvfeORl6aUuqs1xX8HvZrSgcld51+r8X31YIs6feYTFvlbfP0/Jhf2Cs0K/j
+jhC0sGVdWO1C0akDlEBfuE5YMrehjYrrOnEavtTi9+H0vNaB+BGAJHIAj+BGj5C7
+0EkbQdEyhB0pliy9qzbPtN5nt+y0I1lgN9VlFMub6r1u5novNzuVm+5ceBrxG+ga
+T6nsr9aTE1yghO6GTWEPssw=
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/index.txt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/index.txt
new file mode 100644
index 00000000..d9c4e670
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/index.txt
@@ -0,0 +1,3 @@
+V 310302112235Z 1000 unknown /C=US/ST=CA/L=SF/O=Kong/OU=Engineering/CN=ocsp
+V 310302112337Z 1001 unknown /C=US/ST=CA/L=SF/O=Kong/OU=Engineering/CN=kong_clustering
+V 310302112425Z 1002 unknown /C=US/ST=CA/L=SF/O=Kong/OU=Engineering/CN=kong_data_plane
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/index.txt.revoked b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/index.txt.revoked
new file mode 100644
index 00000000..adc8959a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/index.txt.revoked
@@ -0,0 +1,3 @@
+V 310302112235Z 1000 unknown /C=US/ST=CA/L=SF/O=Kong/OU=Engineering/CN=ocsp
+V 310302112337Z 1001 unknown /C=US/ST=CA/L=SF/O=Kong/OU=Engineering/CN=kong_clustering
+R 310302112425Z 210304112822Z 1002 unknown /C=US/ST=CA/L=SF/O=Kong/OU=Engineering/CN=kong_data_plane
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_clustering.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_clustering.crt
new file mode 100644
index 00000000..43ae9de2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_clustering.crt
@@ -0,0 +1,63 @@
+-----BEGIN CERTIFICATE-----
+MIIFYzCCA0ugAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARLb25nMRQwEgYDVQQLDAtFbmdpbmVlcmlu
+ZzEYMBYGA1UEAwwPaW50ZXJtZWRpYXRlX2NhMB4XDTIxMDMwNDExMjMzN1oXDTMx
+MDMwMjExMjMzN1owZjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQH
+DAJTRjENMAsGA1UECgwES29uZzEUMBIGA1UECwwLRW5naW5lZXJpbmcxGDAWBgNV
+BAMMD2tvbmdfY2x1c3RlcmluZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANaCyZeJxFogkiIBjNBTZcztOsW/8vfljzK/m6Yi589hbrTw2CgDGBl+3Pnl
+3AA8bkpAG0Tl0TjB3h/hij5Ywopd/dDEeNAhjvlBNiDMy2cN93t3XvGyp7w4hF9u
+yaUiJgyTH7AjuCDO01jvj8GT31cfNDBMBwjQF0AltsGScZhanwy82fHUZzvpHMr6
+OQ+riBwb2rXKHoSCukxbs/Y1HfPxmpNShWDPXFFMbsujPRT6meVcCKQuThIWLdwA
+KvJYSYC3gTHQyadjwTF9nLZgMu6cWxTheWXXZ/sF4tZ1DPQCjd/1Pdi/TwEawAUa
+vOHP7ArfB2vHOX59bJgJbFyGB3ECAwEAAaOCASYwggEiMAkGA1UdEwQCMAAwEQYJ
+YIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRl
+ZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFCOojCEbuBGgdyxoUfYTEyYU
+ioxGMIGIBgNVHSMEgYAwfoAUxTAm/Gj7/9K32+Wdc0BOHFKRFeGhYqRgMF4xCzAJ
+BgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCU0YxDTALBgNVBAoMBEtv
+bmcxFDASBgNVBAsMC0VuZ2luZWVyaW5nMRAwDgYDVQQDDAdyb290X2NhggIQADAO
+BgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEL
+BQADggIBAHJmrqebjeI7dz34cJBZH0iKTv/dHqinck4u2Dnol85SUICDAlIk1ZDm
++VHA7ZH3ittPjWbiR8N8CJ+GW+2zfcenM+9BfCm95FJ8K/4vU9Xrx/NZH621NELm
+fneD/rFWahf+KT9J7H9SeR3oIiecsDEDM0RAFrqU3innLAnWoTKDNQukjBGUrTnm
+3YPv6+MAUHl1AaZpX4yEtakevQHxY0Kfpt0U1iZbvL5jeC9HuswTFCplARI5qnxG
+0RhRveApQ6Im0clUKx8eqG2Iez4clSNLGJEsUpvyr3CwbyOU/9UneK98hbf13rww
+DPoYT1U6JMEJuKBuL1qDECH8UUF0X41KEPP9s+dgBcAXE/gaD89hjIzOhbkDXbLO
+L8511Fr466Gcab05FBGupXjnJ63EB3Ct4TAkRS6W8IrOtWRPOtOCbRJtSKqcZnfE
+F8UCklCfIHaM7y8JClVLWSIjaOtg2UVJqCJnEMo+5h4WLnkGfBZr7VAJRKPKC3Vt
+jfKbOUcWcMJxR+UWWu+9A/kMb97cAXuSwZRyeeKAnMzQHWYMx6Bo/m2JHWwhlsf+
+vX6mp4C+FeyoXejP1cZBVkMh1JsRKWHAxbGLQkrYHhTKwYudLvsXURatJ0i+BNjI
+QmBTKAfIh2q6qJDRcsDURDsvBD46f2p/kzg08+f5eQv+cMx2ryAq
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFmTCCA4GgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsGA1UECgwES29uZzEUMBIGA1UE
+CwwLRW5naW5lZXJpbmcxEDAOBgNVBAMMB3Jvb3RfY2EwHhcNMjEwMzA0MTEyMjM1
+WhcNNDEwMjI3MTEyMjM1WjBZMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExDTAL
+BgNVBAoMBEtvbmcxFDASBgNVBAsMC0VuZ2luZWVyaW5nMRgwFgYDVQQDDA9pbnRl
+cm1lZGlhdGVfY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXcXvt
+kyiEY7Nr8VeEF2KTpGkEwPLNoLW9eNpFH0bFlKcmM/+IocSUErD091SCf0fGCRuo
+8bISb4MVa5esq8XMUSR63cDmz2IbWOtvNir4wLfAoUuA4JBhubaSyaslXLf376QT
+sYDjLH9jQ3rFYskz9hrlX1HFFmm0hlDnuqr1w0GF+6PxPdxRKkdvKexPuQp3qaVZ
+dzKvFcGuCu7nuqemb37CBBkdRAgVUj37pXBIcc5p5h8PVAU24r7pAaYppDmmZMWa
+uTSjz4K/PTh5GzG+snf8iH+EpTLGUqPElR+ABP8YiNrQQzgA91lPjwLqp6D34sMT
+7xvk4Ri1cOpOsNA4hCdFPilzolMQ6Zpz0ELI25vBt8qY57JMlsUjaY2PR1gI+pE+
+jc52e18Bt/axNnbgxoxTQWPcw27mUs7H4+1WiZufhzz59obgWRnoCom0Fb6RN9Rl
+8ezl+h30+Dgk8ftiFm/fI1BttL6dihveSP/xvuMYRFyT0F1ZNZhgME218cOB5hZN
+dKOFynRgI4SfCeMNSy3KnrBoPYE3P/f1ljBbrR5x/xQE0V5iWFJwZwWexO0+Hf7h
+Aaql5dcwsMIjf8MfHKuQOfZauUmPxu0EbM4NiQu5GEK/9rGEIO51Tlo237l6k4tF
+oKCj3EUZ9cM02CROKGDM6vfkyeyGDbuDPGmdLwIDAQABo2YwZDAdBgNVHQ4EFgQU
+xTAm/Gj7/9K32+Wdc0BOHFKRFeEwHwYDVR0jBBgwFoAU9NE7mu7p9CqGxLNWl7km
+VfMeEO0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
+hvcNAQELBQADggIBAKFvNX1Wv7lOsYvDGOCmIrJEDdjW2Q60p23v1U/R9Wv7xo7X
+2SRuQvqKd3AB/9dCSgaaXKHwYgTPIS8NwUJ0SMvSwrnnRpZS5qkOB9JgRgAX5Ebd
+eyupB2AumZ1BGaw2gqPYHm8zxu3N2yw2pVV9LJ2nM+IPTqiQrYCV7BxNpAd/v9OA
+EC9XbKhPqdJ4bD6dGg7w5iBPadb6amAKkGutKjjB+AC/lJlM9bMEGd6RP0ywptQx
+jAfY0VTElLsN30Q6pn31Xf4UzZk4xzyW04GaPFcJVoHTWSl969p0k0L/WAMakDHB
+/g4VvkMTFDoH1Mi7ohakHnMC9XQbMVj2t/EE3XLiD4gcNEyCjXczIxDYJRYe2X3f
+51vQNR921P1KUNTooGusltMmHuWBnT046o9rp/2uQvHm2y/qv1kCPHTiP7vhb/TG
+2JCc+3LZ621EjH5jRvL60Pji4RnGGqLDBykLK68dymHVfrSAi+ZCx6PNxSm0Ydm6
+ZM1Vb8lD2EwEm20qKWM484ItWcVHgWEWDvaMjh0iIq45LA0KmN47iUN8X6rmdulZ
+MDSnSYwJfRt1DdyUC0nDWMQaW1JOQxQxoJCoDmiLwv9BIeNB8LNJEU0FTOPc8xhf
+VdlbjNIC1fs2OMWOc3A1hAFlf+vU8UYLRgYhLiAhFT2iwhBksSzGURY7eKqM
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_clustering.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_clustering.key
new file mode 100644
index 00000000..77e81fde
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_clustering.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA1oLJl4nEWiCSIgGM0FNlzO06xb/y9+WPMr+bpiLnz2FutPDY
+KAMYGX7c+eXcADxuSkAbROXROMHeH+GKPljCil390MR40CGO+UE2IMzLZw33e3de
+8bKnvDiEX27JpSImDJMfsCO4IM7TWO+PwZPfVx80MEwHCNAXQCW2wZJxmFqfDLzZ
+8dRnO+kcyvo5D6uIHBvatcoehIK6TFuz9jUd8/Gak1KFYM9cUUxuy6M9FPqZ5VwI
+pC5OEhYt3AAq8lhJgLeBMdDJp2PBMX2ctmAy7pxbFOF5Zddn+wXi1nUM9AKN3/U9
+2L9PARrABRq84c/sCt8Ha8c5fn1smAlsXIYHcQIDAQABAoIBAGXOrN6/A/HCg5ig
+I7S74BTigoJYF3iP+uabCcRPzLUgCOrXY7+ZuFZhX387GK8D/1Q+GLMaX7IQUNvQ
+r0vn1GzXLx9mH/Cn/LNPv+DRUbgXaN2wSd9say9po2mnqww0qNpO+TsfuMPZZXVQ
+PWoiRF2U8a/6ZVxJZr+LJrG4TzF+8gyHoUJrlncob8ma5EQKOR3HzMxfJx9OrjQW
+ISu+g2D+0qpaiPGlEUOMKBnrFhVCd5LUaCKM1F7qTe08hvyywqp7CJ4rw2loa+4W
+cdl2UTl01lTke3uEPsBdtvpwQ0DAvFyXgA09LmnwRjHtPzHjNxnkGE2qz14sctBi
+OXt2KAECgYEA+qGxJ5tYkqob+JTvrAQp1zD9ECs9ElVbJZwZ5Z/lhxMBJ07v11Ku
+++M9rSUjhWcYbd2gfGEkxXzSK9yYc4ryzDvCREuNmGxYs52VnaBlKPY3T/h046/9
+S0NivzApCTVK+CYdntT4XNyn9+WGO7zq7x6FQkSsFuj2lnfHrahq0i0CgYEA2xsI
+KMYMe0NTJFNzhfFQMzkSU1R1+TvhaXjpqfneeizF4plYxrZOtOb3vzriWepB70de
+4atsoxZUfqS34RKkLX1WyuoLm3WtpJHHmhUfj18PlIMu0gXrisUZM8evltTOyRsg
+mn+V/nZeXn34tZARbJ4rAMrmOKIz/z0OCtZ6yNUCgYEArb6ZiNNwO3whl9nnrF/W
+gY88X5EZ7TOu1Au7CCwoedL64b0fFy4CkCuf/f/Y+AnYLZGOR6swSpeVO0LZjH+u
+gVaL/bxClH/HnfyIU5V1i0fkYFPk9FJ0TVCRi+hfCjsflZcWwZzx764n4voCbDcy
+xkFqL95bTiaBix3OFtzB8KECgYEAuyhbDuEllkELCxORsY9Qz4Bnq/CQmWXSBVM4
+XW5H4RrPqeENWSgvEQ3eAGZfJSkaSzcu0BurP5/6avdu7n4K6aSP9+J2KcQaoGG6
+G18Bx2kPGO/5lYNjEPWNspJW5cNAI77dWbu0N1mLALIgOY8nox10ZEfs4eGEAvl3
+PkC5P0UCgYBh1qnXWvj5KT0pt7p2X7ayg2FHunNHO1QLpFBWhhgQlyvLns/toQWc
+XXKogwiMZ8D6g3eex8vZ25K01JNn7uQUDUNVhzyUcSjfVFB1Bc9dLdNfIYvL4adz
+BTZ7TjBYdScSIhjHKbaLwimHcNllYqz3vJ6hAK9YFVt5sI+/EixHmQ==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_data_plane.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_data_plane.crt
new file mode 100644
index 00000000..4f30d968
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_data_plane.crt
@@ -0,0 +1,62 @@
+-----BEGIN CERTIFICATE-----
+MIIFOjCCAyKgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARLb25nMRQwEgYDVQQLDAtFbmdpbmVlcmlu
+ZzEYMBYGA1UEAwwPaW50ZXJtZWRpYXRlX2NhMB4XDTIxMDMwNDExMjQyNVoXDTMx
+MDMwMjExMjQyNVowZjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQH
+DAJTRjENMAsGA1UECgwES29uZzEUMBIGA1UECwwLRW5naW5lZXJpbmcxGDAWBgNV
+BAMMD2tvbmdfZGF0YV9wbGFuZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAK8wCJuLAIZa+hihNEOx/YBSx+05lXXgoNirVuJjmUhx80oZQsrUnCA3PPe0
+snkUfUTpe0g1P1YPMDQBoXK+MffpanTL2e5EIkD+T1ti9EkTZg+jnjdGaAZ27Y70
+9k6KTb/rFj1Lezswdo86WlXpxcbCFyHlMO590pCPpQ/VpQodqn9bTPPoTKTHlL4q
+8no26rb16t7wBVrbADOu7lzcIZYB2xLslM5B8fk7jzIBPMb3uQxh+AhKn2bw07jv
+unPMHJK64alfUwqRk9krd/1WICSm51eKi400kefrtzXRwtxjr72EmOCnZdHr2A0O
+7ZKlHhZolVXaACghMd4IRI/eUD8CAwEAAaOB/jCB+zAJBgNVHRMEAjAAMBEGCWCG
+SAGG+EIBAQQEAwIFoDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQg
+Q2xpZW50IENlcnRpZmljYXRlMB0GA1UdDgQWBBSpSKxdiN8o9qYMOy158lXYg2MI
+qTAfBgNVHSMEGDAWgBTFMCb8aPv/0rfb5Z1zQE4cUpEV4TAOBgNVHQ8BAf8EBAMC
+BeAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMDcGCCsGAQUFBwEBBCsw
+KTAnBggrBgEFBQcwAYYbaHR0cDovLzEyNy4wLjAuMToxNTU1NS9vY3NwMA0GCSqG
+SIb3DQEBCwUAA4ICAQCxdljA6Yxg7DLCs0RDZwzjHf4/H7SO888v7Tad+mbQcIzI
+dzxEaAcXf0qvDsDEPcE8n3vKDC9kHm3wKFJqK04Clc6Su+BLvGwVO0TTi9yC5X0j
+OFQQQAeth6ByJh7fqXkY4qkER7aunN77tpdmz4/m19we8U3DO/oQm87Fu89l6hMH
+942rpu4uSz2J0d+PpnXulLOJx0xLWv8ARmJkGD8oWUrVMmeeq9q1Yz41Oyyf37Xn
+iGUMLC1ejDTBw89wuMuqD9smka9stMdC/7mQWJJqz/Ww16MqGwqLvcK2WZzvU6jE
+V42n0Zdq/rnPV63B/NZgwGEpfqWqZj5K0NfjboCoRmS70vWurFlP/oeaYZBoCoUW
+cMiw8lnS53fFtX5Mt8gV4NLzoy5a4Q6qoZMzuudcabmEKMNONBp8phkqosSgkDKY
+wJvhK1mYdWf3VcX2mjyW3j/shTXlKibesNZAR2XiKqEFqTkqFY6Dx+wpvsNIyvHw
+ZWLwa71ZONVhVgt5Hu6w/u+hzrL22v/s9ticVx1jJ6lopppiXf/0Ex4EHjZI5h3w
+Sdj2fCBsv05FWJqFadMY6gt92/pmyA1HL7S5rK5odoDviRGQxsCvBax2Vv4WDrIh
+tcGGGUZ4nLeWPOqb9e8fJKJtILvQ9x4qRMmeYKMAkflsbjDNw6g612D3TV2WXQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFmTCCA4GgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsGA1UECgwES29uZzEUMBIGA1UE
+CwwLRW5naW5lZXJpbmcxEDAOBgNVBAMMB3Jvb3RfY2EwHhcNMjEwMzA0MTEyMjM1
+WhcNNDEwMjI3MTEyMjM1WjBZMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExDTAL
+BgNVBAoMBEtvbmcxFDASBgNVBAsMC0VuZ2luZWVyaW5nMRgwFgYDVQQDDA9pbnRl
+cm1lZGlhdGVfY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXcXvt
+kyiEY7Nr8VeEF2KTpGkEwPLNoLW9eNpFH0bFlKcmM/+IocSUErD091SCf0fGCRuo
+8bISb4MVa5esq8XMUSR63cDmz2IbWOtvNir4wLfAoUuA4JBhubaSyaslXLf376QT
+sYDjLH9jQ3rFYskz9hrlX1HFFmm0hlDnuqr1w0GF+6PxPdxRKkdvKexPuQp3qaVZ
+dzKvFcGuCu7nuqemb37CBBkdRAgVUj37pXBIcc5p5h8PVAU24r7pAaYppDmmZMWa
+uTSjz4K/PTh5GzG+snf8iH+EpTLGUqPElR+ABP8YiNrQQzgA91lPjwLqp6D34sMT
+7xvk4Ri1cOpOsNA4hCdFPilzolMQ6Zpz0ELI25vBt8qY57JMlsUjaY2PR1gI+pE+
+jc52e18Bt/axNnbgxoxTQWPcw27mUs7H4+1WiZufhzz59obgWRnoCom0Fb6RN9Rl
+8ezl+h30+Dgk8ftiFm/fI1BttL6dihveSP/xvuMYRFyT0F1ZNZhgME218cOB5hZN
+dKOFynRgI4SfCeMNSy3KnrBoPYE3P/f1ljBbrR5x/xQE0V5iWFJwZwWexO0+Hf7h
+Aaql5dcwsMIjf8MfHKuQOfZauUmPxu0EbM4NiQu5GEK/9rGEIO51Tlo237l6k4tF
+oKCj3EUZ9cM02CROKGDM6vfkyeyGDbuDPGmdLwIDAQABo2YwZDAdBgNVHQ4EFgQU
+xTAm/Gj7/9K32+Wdc0BOHFKRFeEwHwYDVR0jBBgwFoAU9NE7mu7p9CqGxLNWl7km
+VfMeEO0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
+hvcNAQELBQADggIBAKFvNX1Wv7lOsYvDGOCmIrJEDdjW2Q60p23v1U/R9Wv7xo7X
+2SRuQvqKd3AB/9dCSgaaXKHwYgTPIS8NwUJ0SMvSwrnnRpZS5qkOB9JgRgAX5Ebd
+eyupB2AumZ1BGaw2gqPYHm8zxu3N2yw2pVV9LJ2nM+IPTqiQrYCV7BxNpAd/v9OA
+EC9XbKhPqdJ4bD6dGg7w5iBPadb6amAKkGutKjjB+AC/lJlM9bMEGd6RP0ywptQx
+jAfY0VTElLsN30Q6pn31Xf4UzZk4xzyW04GaPFcJVoHTWSl969p0k0L/WAMakDHB
+/g4VvkMTFDoH1Mi7ohakHnMC9XQbMVj2t/EE3XLiD4gcNEyCjXczIxDYJRYe2X3f
+51vQNR921P1KUNTooGusltMmHuWBnT046o9rp/2uQvHm2y/qv1kCPHTiP7vhb/TG
+2JCc+3LZ621EjH5jRvL60Pji4RnGGqLDBykLK68dymHVfrSAi+ZCx6PNxSm0Ydm6
+ZM1Vb8lD2EwEm20qKWM484ItWcVHgWEWDvaMjh0iIq45LA0KmN47iUN8X6rmdulZ
+MDSnSYwJfRt1DdyUC0nDWMQaW1JOQxQxoJCoDmiLwv9BIeNB8LNJEU0FTOPc8xhf
+VdlbjNIC1fs2OMWOc3A1hAFlf+vU8UYLRgYhLiAhFT2iwhBksSzGURY7eKqM
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_data_plane.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_data_plane.key
new file mode 100644
index 00000000..79ea99e4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/kong_data_plane.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEArzAIm4sAhlr6GKE0Q7H9gFLH7TmVdeCg2KtW4mOZSHHzShlC
+ytScIDc897SyeRR9ROl7SDU/Vg8wNAGhcr4x9+lqdMvZ7kQiQP5PW2L0SRNmD6Oe
+N0ZoBnbtjvT2TopNv+sWPUt7OzB2jzpaVenFxsIXIeUw7n3SkI+lD9WlCh2qf1tM
+8+hMpMeUviryejbqtvXq3vAFWtsAM67uXNwhlgHbEuyUzkHx+TuPMgE8xve5DGH4
+CEqfZvDTuO+6c8wckrrhqV9TCpGT2St3/VYgJKbnV4qLjTSR5+u3NdHC3GOvvYSY
+4Kdl0evYDQ7tkqUeFmiVVdoAKCEx3ghEj95QPwIDAQABAoIBACkcGXj+pnHg2X18
+HrqgAv/g7R+C+sq9mqEdm/bmLmssqk3CHcVhHP4GWF08XwFAyKCqNY7dR+6XA9XA
+aDV34lvtv1iHGa3q+SrNQqwMTYz0a2fSGmeYMwMJV3fLjh0iIVqe/QoHM3TRS5ES
+vW4ZvJqGo00F7nSYvBfGTZKorODxb3CIjFXTlJZ1u/+YDVswWv3+XrqXKponoedr
+Zl8SCf7iqLz6cIPGnx6joIR/e7LK9eDBfgV8bxfsyulby51f06V/cBTQk4l8Qjeb
+Z+iFJzZxDuxq219JLWYqKH/JIAchvPoov1PBfEZXK4fKunAR/mw6j4OL/X5H+Dj1
+ziFjbyECgYEA51NHPMiChIRJ4poaTE99CPPcJek3rIAP83+BSRm0e38hibfB7p3C
+FpPhtWlMMM6YRAJsVkYrLWTtdl2nZbw3AuH46MM03nTbEgueQ2ry4hofzUKQfe5Y
+VyQccFQvQx1XF8lFML0TIEZ1ID38G8oNhme/fk5eHsR7Bn8Pi8XlpY8CgYEAwd/S
+qbTph05yCTYGeqqsMh+hUq9oqLvcYJDd7lStJjLlt6b8jEjZspePTHTtcL2vOwx3
+hmA4PQR2Rnc4M4nl8zMHSySk7m7dR9w01uORzk2BzWb2XHy+SdtwFWS+2bKJBWuf
+DAtlQzvfF07ccxZfQQTjMGVkhnsDMinriWCxMlECgYBJTRNSyHrLQRwkiQ5yRfHq
+B1QoUzmIGOB1GV8/abzOMV/QQwFZ+nWJL/0iviYdhSmsy1PHFt8RuFyi2FR2IWkR
+Kcf1Af5by42rrzDMTjR+vyZ6pXAh54fovRGh6ps7Wi3B5M5e/lr0LD9rIxkjOSiG
+AZQlkvGyMDKHwXWMpf36MwKBgGDMslBNpfQK9OEoel+w670zEcdJEYZ+FfCZJFYl
+LTbPXuctlxcsIJYNGl1gXFVYQC/Jb7xGOo4stilEyWjiR1AAgHnCWB88d3uztSY+
+BcTt6gt2hzdyiUgzKmlkHe5wN/3e2FCZN/wz3pWyqFtGJlU+bXjyhximPthDGflD
+r/WhAoGBAKFWK2lmVTiChupq3nihccggW0Dvk1NBLF+OTOGdkPDmNhypMJ7e1OhE
+4KYHCmGKFPvg1M0+CQzqop4lxTzFz8NI/Pt/HkWbkbmRyHE2p2cUY4zGIfF9/nwq
+vS/uKrJbHMqXyu/pa3HzmFtWrMoXd8/yCdSGwaNDK4ZCiP+hVMQ3
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ocsp.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ocsp.crt
new file mode 100644
index 00000000..21930086
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ocsp.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFpTCCA42gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARLb25nMRQwEgYDVQQLDAtFbmdpbmVlcmlu
+ZzEYMBYGA1UEAwwPaW50ZXJtZWRpYXRlX2NhMB4XDTIxMDMwNDExMjIzNVoXDTMx
+MDMwMjExMjIzNVowWzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQH
+DAJTRjENMAsGA1UECgwES29uZzEUMBIGA1UECwwLRW5naW5lZXJpbmcxDTALBgNV
+BAMMBG9jc3AwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDOlydfksGd
+jK5CI2yNdsqpA9/Zr6eksPE0BOkbb1LdrqNyI2pRw9D8tpEY6AqaaYwQQyVDA54U
+BKE0L/PiqpACm4nKWv1XNsRK+REhEw8V4aqgkt8oyVz2w22mXq+DH8+iCmlpap1N
+ZfLXpKz0ZS47uvhFscs3N8bohWI92EHrMxp3JKbmWdoE/NnyAF9wV1WvYpfdpcDT
+BKwxO7lW7cgIB3kArtvGLLrtkVhR/js/B+Ff9CLugImGrNnSXfiXOMeLhui1U4v2
+aYEM+BN5TC8PTIoLwo96SRoBnZBoZ215liJiF3peVQNnR1NCYmJ6jQjtBXC4/wz9
+ganFdYF9+WSzSrBHiwIe7Nn7ARdRAtJPvOUBvaj3/zNpNCfikqcvGSTgJ1ixw0oO
+8o+UvWThQCGfB34FkG3oAl0y7SEpFKU6+8IWqPoM7Kdm0ZFUKXA2G7RNl5gH/o/B
+qVJyx31OvvZZoc3OyTInRpxNdhrWRaJppYw8xxv4mudedf48CToFGQjsWumVkjlU
+VdPSVP4VbgrOeVwwas7YBONES7oqkKnvjmLHqAYdalMLyopSuvnb3X96Fp6L2Oms
+FuNo6/7AUBVQtm0I08uCRWhP2CPeca1fERgTbO/puECMW3XqNAhyBB5e20uxcB/E
+h3qgu4y2xcn6Za1aQ30+RUB5n7DST0odEQIDAQABo3UwczAJBgNVHRMEAjAAMB0G
+A1UdDgQWBBRnqkgve+lZRPAGhX4AwHIMJl++gzAfBgNVHSMEGDAWgBTFMCb8aPv/
+0rfb5Z1zQE4cUpEV4TAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYB
+BQUHAwkwDQYJKoZIhvcNAQELBQADggIBACuoaNr7uVBIfDFo6jdLqtiVAOKsUxO9
+EDoBIpGGiLOk3/NZxKUG2+xhsuAwZxPIVxifkg64qlLmMzZMrWFzOvkvRDYnU2se
+s/1sbOC3h+Xm5G5HjRhwmHczXUljyZySz0m8UHWeJ49zkDVIGzEBXrRnzBtji1N2
+9PddIz8zhqMtP33nKTo9m1kkkdoA3cZ/fcM21doZ6+ZimtRcOOz7BgQLOwPupq0L
+9DxBjJYwPrXj5IRaib0rZQ+kdjPNgggCryvJCk/27dKAwFe4rWLmFYQ+fgY2N2DL
+djXtxDxZ8Gw3x+GM5agI/BUhTscx4AvscZZr7brSPPmW5Q8nAE6NJQtanuT0VCuU
+VoRwNuTs0w4uTXyS7TwXDvfSrQqQLI+O7BWDnJT02FYmakT5CFsf7zqJzsbhSqq7
+11qK32MBN6q7QvH9SZi6A1jK2UgGiZSCZxF8OFQGJxaf5VBL6naP2NlPSeCZUZ5X
+eWVqE/lXi4LLUIWTwGdjbfkY72FFWThZoxtS+lM/CGVjVWS9gwABL+jiirZL++qQ
+y9IzzULMyxd6Xl3/eEzwT8kYjgwUQ2KWnjaHSBxHssJiRyHUhl0cUXuLGiW5fsHE
+TG6WevipP7qdOiIttLzFyC60pLR7v+vW5VrRXGR1kzou5N1ESi/ixl7PY9fg+wp0
+cWEwPQGHYdE/
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ocsp.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ocsp.key
new file mode 100644
index 00000000..5d0bf2cc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/ocsp.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAzpcnX5LBnYyuQiNsjXbKqQPf2a+npLDxNATpG29S3a6jciNq
+UcPQ/LaRGOgKmmmMEEMlQwOeFAShNC/z4qqQApuJylr9VzbESvkRIRMPFeGqoJLf
+KMlc9sNtpl6vgx/PogppaWqdTWXy16Ss9GUuO7r4RbHLNzfG6IViPdhB6zMadySm
+5lnaBPzZ8gBfcFdVr2KX3aXA0wSsMTu5Vu3ICAd5AK7bxiy67ZFYUf47PwfhX/Qi
+7oCJhqzZ0l34lzjHi4botVOL9mmBDPgTeUwvD0yKC8KPekkaAZ2QaGdteZYiYhd6
+XlUDZ0dTQmJieo0I7QVwuP8M/YGpxXWBfflks0qwR4sCHuzZ+wEXUQLST7zlAb2o
+9/8zaTQn4pKnLxkk4CdYscNKDvKPlL1k4UAhnwd+BZBt6AJdMu0hKRSlOvvCFqj6
+DOynZtGRVClwNhu0TZeYB/6PwalScsd9Tr72WaHNzskyJ0acTXYa1kWiaaWMPMcb
++JrnXnX+PAk6BRkI7FrplZI5VFXT0lT+FW4KznlcMGrO2ATjREu6KpCp745ix6gG
+HWpTC8qKUrr5291/ehaei9jprBbjaOv+wFAVULZtCNPLgkVoT9gj3nGtXxEYE2zv
+6bhAjFt16jQIcgQeXttLsXAfxId6oLuMtsXJ+mWtWkN9PkVAeZ+w0k9KHRECAwEA
+AQKCAgBz7ytvXQI+kfYws4R1ltaAJuZ2WTbxG0Mg+CiA9uY/9YNPyEQgAo3DZAjl
+O0LICLdLYQMGKn+JqFd22/o3l5Qdgn+/CXTmfpuFn2RXdUSf+PYyCnolf2smJ+He
+3YANS8rPmpwxRl1kU/QFxCozNJzPdEtjgTUTlb+QOVo6bqP/g7w0ZGMtHftVlTgR
+sNfeSYSSWffzsNMXGKYxMtz9xY7dwqPLGFXJTszQCSLRUKSa6Kc3m+AGjCehZlsO
+zSF2a6y/xkPNjkcbT3XWe1kiVviJ02Ac3WB7NY7cnTmu/WvYMcK13YsUaQzx5nr4
+5BFzyLXbnZP2nVsC4MMPRrQWp+A0Iwu8TJVrb5tUhEoVXGkkj4aUc6awX9g8OTii
+5JhQ2le2BazrCWCXtIfrhbcPyORyGizHqtzXqqVbtLUU6AgRyscWeIFf7v1nxP0K
+PgzFwzTMT8CH4t/CnkV4S9blj+S3JZY+MrmcliSAVz5+45mY3h7+A2vvMrBFnwiw
+5273HZyOCcRdfyufuDGt6vaAC+pgnRuno19i+Q7Mfsp6W3HiutfuNXJwG4YXTLba
+JL47QzOXO5DiJ9AczKo/c0lXMw/K/OkcV7QB5qi+9ynoY42sQFdin1mNW03KwZru
+Yo40wkXfAMAX/0i0dAKmf2ubG8/g1YcHg+NIAr+HQ6SjLuukYQKCAQEA82vIpCt4
+0iW9uOvvF6PpB/PwnUKTwjJYtVlk92TJ/zNhW50MylMMk5Hj4NC2rbH4yBgjHxAn
+jyhYqVi+mrsqidZGXgzmDuS19moqikFV70gFuaf+HbJN4ow3SbvTd5dv4Vv7UKbc
+v/jblLM3Zx5kNBZNP9hoo65o7kywcH38lCxOOr4RheJOCUL0YZmGd+S9BFQgfbur
+dITTHJ1t4g5zYFSevPM+CbkEIUWgOZrdqjhxGsH1SVTGtzR5qwzWiG3tPKe0nwWb
+gejPDrMCzYJm2kd01/WRmkqZU/sAJr7R1iQzrwe9PJmH9BOezyfYremieXqfD4xs
+t7OJD1lxutb//QKCAQEA2UQjaISYnwyVYQPSBiS4FiH5Cn5kTd9jX33Qxy/QUbHJ
+4MaKNq5N6hwb0nCg52kZdOCzLqpX6wVAxHBL3ouKFYFHVk0b1oaoGMb4pxx5EnEZ
+0z18VH+VgfluZH5/ARRwwlB40naqGQq5XlRL+xCm0lcnkqp2/Ie86qwaO+vyYUf0
+L3BcshuWjbVI1aPS81j5lelxpZwBOUAm//tNyPV1KJDRhjV784MxcpH7lNgtrRVf
+4woAZnEu9OnAZcoRHm7IsWnwu3lH7h7rU+WMy163dztKzEzZ6fTZYFemyZV5XQ4N
+q1FwQkav+puSMKZWB6atORY6GormpMK7VZR1XhhLpQKCAQEA0sqThRbgGZr1IB2v
+fhlCwtBLnOL6cUCH8QLonBN7mLM1q8/kM5CXY3MCkrwqdV+YwC2mvE+Q8jdOD9f5
+tqQ9wf78EJW640rLCAgHrpHFiOAllRAUzkKJj5U8i21LQlSxXcX5a24T22n1PF+1
+qmZ2/2QQoSkV9CgkVbezUrbG8skrNVNCeV3vlbWVSq9X8prx073GJRtO7ifXaQAr
+F4bMAq9Ehvtcza6aFPXmOfwR2EXoK/OqJUZ0jlGyypzjamFG/y97CfohH+4q39/E
+nZI+3ubiF+FfpOzUuhSxnNvBel7/IqLhDIknYgVbkKhAytl3CRtWgnBn9OxT1Cbw
+hYuJQQKCAQBVXL0gsoAYdWQ3cr3Q5hphr8VeRxx3sB4mBZPCvtl1T4oGw1rIcyFv
+qs2Pl+rQIO38itA7tHfIgg7ZX1mfvWlqW5nAoZkfZ1aiLYLCfaBgC4nfAhhYRqxi
+HbMuzrhtny9SWTWvUyovnpQIKMyVfwxcNhv5Nvp664XhGe9QvbpEWHXrMZVp8Qbs
+9F4CelRGgh3FtauOKsYcTUVFa+I64529a3C270qc+V2zKwISkAEaMPy0glh3515Q
+oYqTM5oYP+SgOAR6VANb3lANbXIs8TDaKrSPol432piRjr6cExtU4VGjjuKxV36K
+0xbUAHZqmSUT+dSoWwyVjWD3FdYrOxZ1AoIBADwwUIdheYJbVVlcRZclm1pDoVCB
+bzh57GLKQ7KLj+gGp2AoizUKRELLKI844HZ70d2hghEcWei0sfzExRGq61yaNfHa
+lf3SiS6eJ7ivRHWwtrUXTOfO5LcVGzVLp4TMXvl4uZMrHVSB7vGJsDpoRaCK8hKf
+/f3d2lPRWcRQU9Gyc4hQE6daH0pVxrwqnWjmPvf9AIxs9vf6RVGR3y3dyccJeiph
+vC2sZv29dDlfWHSBun0vZzd+Xk73Qn3hSMM083FZrJWhqdzaOA0NfpdYgVxfn5Q+
+B1BYbv02WurCQwdWsCiDiLrXEc4HQT/fY+cV41fBrE2Kb4g565qd93Rh80w=
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/resp-good.dat b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/resp-good.dat
new file mode 100644
index 00000000..0004e5f8
Binary files /dev/null and b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/resp-good.dat differ
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/resp-revoked.dat b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/resp-revoked.dat
new file mode 100644
index 00000000..9744f11d
Binary files /dev/null and b/kong-versions/test9.9.9.3/kong/spec/fixtures/ocsp_certs/resp-revoked.dat differ
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/opentelemetry/otelcol.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/opentelemetry/otelcol.yaml
new file mode 100644
index 00000000..9cd430d1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/opentelemetry/otelcol.yaml
@@ -0,0 +1,28 @@
+receivers:
+ otlp:
+ protocols:
+ grpc:
+ http:
+
+processors:
+ batch:
+
+exporters:
+ logging:
+ loglevel: debug
+ file:
+ path: /etc/otel/file_exporter.json
+
+extensions:
+ health_check:
+ pprof:
+ zpages:
+ endpoint: "0.0.0.0:55679"
+
+service:
+ extensions: [pprof, zpages, health_check]
+ pipelines:
+ traces:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [logging, file]
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/500services-each-4-routes.sql b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/500services-each-4-routes.sql
new file mode 100644
index 00000000..73048645
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/500services-each-4-routes.sql
@@ -0,0 +1,2549 @@
+--
+-- PostgreSQL database dump
+--
+
+-- Dumped from database version 11.16 (Debian 11.16-1.pgdg90+1)
+-- Dumped by pg_dump version 11.16 (Debian 11.16-1.pgdg90+1)
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SELECT pg_catalog.set_config('search_path', '', false);
+SET check_function_bodies = false;
+SET xmloption = content;
+SET client_min_messages = warning;
+SET row_security = off;
+
+
+SET SCHEMA 'public';
+TRUNCATE public.workspaces CASCADE;
+TRUNCATE public.routes CASCADE;
+TRUNCATE public.services CASCADE;
+
+--
+-- Data for Name: workspaces; Type: TABLE DATA; Schema: public; Owner: kong
+--
+
+COPY public.workspaces (id, name, comment, created_at, meta, config) FROM stdin;
+dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 default \N 2022-05-26 09:04:16+00 {} {}
+\.
+
+
+--
+-- Data for Name: services; Type: TABLE DATA; Schema: public; Owner: kong
+--
+
+COPY public.services (id, created_at, updated_at, name, retries, protocol, host, port, path, connect_timeout, write_timeout, read_timeout, tags, client_certificate_id, tls_verify, tls_verify_depth, ca_certificates, ws_id, enabled) FROM stdin;
+a7182665-e3bb-4ad0-91bc-bb013404d465 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3c089a41-3c85-4e95-94bc-9dcbcc02d5bf 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e4e0c0f8-8f86-4138-b90b-1ab4b42c545a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+635667df-d7c8-4c8e-961a-79094fb7edf7 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5db07df7-6efa-42f1-b526-aeea5f46aa7f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0cf9ed94-6fe4-4356-906d-34bf7f5e323d 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b0d849d4-9d3d-48bd-bddd-59aeed02789c 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d609eb1a-3c6c-4867-ae94-ad5757bab196 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d92656d5-a8d8-4bab-93cf-5c5630eceffb 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1e306cf3-2a3b-40b8-91b4-f50caf61d455 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b13775fd-dac8-4322-b7a4-a089d677c22d 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0d5ae4f4-5ab1-4320-8057-cd0b21d81496 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e6a15913-9bdf-46ed-8e9e-b71a91b1197a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9124182f-7ccf-465a-9553-4802b87f4308 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ad9d034f-2de2-4a1a-90ad-7f1cf7039a2a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9d36f4e2-ba97-4da7-9f10-133270adbc2e 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+71164672-4b79-4b4c-8f23-d7b3d193996f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d2c68623-5766-4b26-a956-aa750b23e6b9 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c733f9c1-8fb2-4c99-9229-d9a3fe79420f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+879a9948-ed52-4827-b326-232b434d6586 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6c2f637e-3365-4475-854d-2da53cf54236 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e5322b5b-36ef-4b9d-9238-99de86473537 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d71477b1-e512-4b80-b755-d0a074de32c5 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+548bb3e7-fc07-41c9-9299-84a0708a2a59 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4ce0aa65-7a39-4c13-8560-50cbbfbfb393 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f4dae3be-eb46-4361-b84c-da2f83277f00 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+25076386-d45e-40fb-bf23-6078de3ecab7 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1525a86d-6ae4-421e-a2dc-d5758ba22312 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2c961425-9119-41ad-8df7-7b288060e995 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b960c35a-83b5-425b-9fe3-2602de569f5d 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a882f2cc-b1ac-40a4-8e5d-09d9595c5140 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d730b9c1-e795-4c90-b771-3e3ceb21ab91 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+406467e3-6d3d-40a2-bc8e-9942b8be51b8 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d5ab8d0f-b02b-4bd6-9d46-ab7da78e15ef 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+62131b85-cb9b-43d1-97d8-f4b2966dbb68 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+35fefbaf-66df-47b2-abf0-1231af2788b5 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+63639c14-7690-4f27-8a69-4df1aca28594 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+872066a1-4cfb-4f69-ab14-2de00fe8a82e 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+056302e1-150a-416c-9a4f-a9fb03f3f651 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+73734495-785d-42d2-a755-0ad0b1acf933 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8e691f37-eb65-4e3b-a6e2-0525412a98ab 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+569a3987-9516-4053-92b8-aeebdaeeed5d 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5839b3b1-f03a-41f9-b645-a35ff680acbe 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+649cf33b-3d04-46f8-b849-4bfa449c8a7f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3282f133-b8eb-4e46-80c6-a217df510860 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+da88cad4-bd4b-4a9d-b81d-d1445bf108a8 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+365b2abb-1347-4077-8ffc-5b21984fca7f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e3cc7fa5-1919-4753-9afe-6f30f67a2c2e 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fb53dd51-d113-4650-b980-e761871f3c54 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+851cd368-f1ea-4584-8cec-9a430f9b1a3f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4658664d-4ff6-4ab7-a9bf-8c0492c974de 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4d48bf3c-a575-4520-8817-34f0b84dd4b6 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+26968e02-8bda-4c4e-818c-8ed35d44fd9c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+27f10e41-7155-4eed-bdfa-783271fc8bae 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+73bc0430-7355-4c6d-a974-74f5bf707db1 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ef27392a-1fb8-4611-8757-c42b55900756 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b45da34e-3338-4878-a3e5-d78df8cd22e7 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dc5da515-f616-40e9-9b94-d699fded3db7 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8168f4cc-39af-49bd-8b6e-a365f038bebd 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+051898cd-71d2-457b-9ee8-c080908da498 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cdb3688d-b5fc-421a-8c06-cb14fc6c5ff9 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cae8aca9-818b-450d-97a6-7ea08373e0cc 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1b7c0f6a-9eab-428e-b979-5995a4ff6527 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3e658a76-cb76-4be7-a15a-84d4883b472b 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+800121b2-3644-4ea0-8539-25d513acb472 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+89b2af01-b55f-4425-844e-bc2dea397b93 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+34f521cb-53b9-4824-89b7-15459e96532f 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+33a92a68-5e8d-487b-977e-89dd42a458bd 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dbbe71cb-7ec1-4c43-804d-ef6a92721d90 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+69a88ba4-e530-4723-b7c3-f739b92a5a66 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0d1eb445-8a10-49bb-952f-5eb35a8599d3 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a03dac5a-20dc-492d-b4db-732a79d4a30c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+291a0424-2ad1-47a6-a8b2-c63a037bf03c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4eb8a749-0bd2-47af-8fdc-4cf128bf0b66 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c398e6e1-2f3e-4897-912f-483c03ec6959 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c544969b-0b53-43a7-a6a9-79e400d7b852 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1dc10ac4-8720-49d0-9624-e2320ad83910 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+961eda07-6db4-41a9-b053-55f3d86feab9 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a92dc0e0-3cd3-4c00-bfbd-1b9d849c617b 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6fc0c8de-dd47-4b2d-be48-acff77604738 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c1477ea4-988e-40e5-b7a8-6fa4e688f36d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c0ac16b4-51b2-4388-a75c-99a6e8864567 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b3490c56-2668-4cf8-ac26-9d3c38fb9ce6 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6f607e1a-2baf-4f12-b0ed-270073df30c6 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4284966e-2ef5-45f7-b16c-faba6666c300 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0a3d005f-e8ae-46a0-bc92-0a4a8147fe3f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f7039445-e8fa-44c0-ba30-4db609972643 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+10db8481-4fa8-4531-9e0c-fb20e642dc40 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0069a9d9-459a-4efc-b5a2-c0ae786c92bd 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fa73881d-a74d-4349-8a9c-b2ae17b414fd 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fea825b5-53e7-4d5e-b594-5e6d20822e27 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0f9df5d5-3dd4-4a0b-beef-5aed37af31c6 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7d839f08-fe27-44a8-bbea-abaea85e8ec4 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4e27c8d3-1b57-4837-a62e-7b7129f23b87 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+187a1bbe-8750-47fd-a693-eb832b67106f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+97cac022-7f9a-4eb7-a600-3f99cbdf8484 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f731ee23-32fc-428e-858c-2451542ef358 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7cdc1f2b-844d-44af-80ee-9ee8ce30ec3a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+786c4ca2-f7e2-497f-afe9-04a7d389cffb 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+327348b0-de35-47ef-a46b-292bf1a2ce91 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+42231a53-eac6-41d4-906f-96a6007efd5c 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2e5dce8d-7e56-4037-a53f-5363e78cfb67 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+880c0dfc-3b35-4557-9f4f-20e450605453 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2d1e40d6-8080-4cee-98b2-c64c3dfbeb70 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+92e0b48f-e57a-4b37-a150-ca88c81d14a3 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+837f896d-e596-4681-94af-74e1f8832cec 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dfa8a1f7-4dba-4abe-b98d-11146dddf483 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+87b83cd7-e97b-46e2-b8aa-cfc3f41df930 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+090f6901-a7d3-42e6-94f4-69ff07632983 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f0c01e5e-139d-4458-a3f7-47c6f9eb59de 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c1ad53a6-4115-441a-a162-5a27b3e5c01d 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6b12e083-97d5-4964-82c5-22bc95802ef0 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+75d7f4d4-c369-46cd-bf84-fb40784d4fe1 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5e861b07-f18f-48b1-aa4d-e44f7ca06eb5 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dc67018b-ba17-48f8-962a-e39d4e96eff4 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d025ea98-eb37-4e43-bddc-302f5d4ecee1 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+34f418de-2a74-47b6-ac68-9099b4281763 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+81c2ba99-2238-48c5-9d7b-ee96f85ed0c5 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bebc02c6-4798-4c51-9c65-6ac83e7e2050 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+84579611-336d-4291-ba77-6907426203d0 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+03d2fc5d-582c-4f45-bce2-41f8a1e45f45 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8bd5e802-0de6-462c-89d8-8a3dc33743fc 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+75a284e6-a2d0-4fa0-9210-d1dfbfe393cc 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9462d6ae-3811-488a-8f43-93afe7e8d6ed 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6a8aa9d7-cefe-455e-8671-721e43cd0b96 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1a79fb8d-58e0-42d1-a2b2-a9f730a6d635 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+693ae85e-2dcb-4bac-a88f-832ef036ec35 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cf55043c-e758-4007-9d0b-f29ce449b017 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b0f369f5-47ca-4790-a7c6-f70ef9670801 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f54e8793-3010-4551-8a86-bc026fcdbd71 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+eda8a272-adab-466a-b5c9-ba27137d2bc3 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+78c825c8-abdd-4280-9da9-d3bf00e23f82 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c3dc6599-036f-46b8-a95e-8e5b6ef3a3f5 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4372ca08-22e6-4a0e-8d13-f598ba86cf37 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0766430c-c266-489c-bc27-58df3fd10388 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c7167c55-60fb-45f7-b257-4acddb1d9119 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+76b8797a-0ad8-4a9f-9fdf-561c79e481d9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bad7c636-19ad-430e-8c49-6e4efddc4376 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fd6fd9ca-1169-45ba-bb87-8b846a8d0d3e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a2ee552e-0961-4036-8d1c-8ebd420f28ed 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6fca3f1f-fa31-4c70-8059-aee7dd0d5be3 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+70d03905-4002-4dc1-b3f9-336d25ee164e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4693dd6c-1d27-46df-b5be-259eda6ad3df 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+390c61c3-b91b-44d0-9132-d629f3f7f2c2 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+addbf9ae-c319-4a46-831b-a2c71204cfdc 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d59261e7-93ca-464a-b84d-cc9c64e2d649 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+37262d9e-1dd7-4314-9a5a-d289c7479be0 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d3ec5e93-e9e3-4fd4-a27b-6af1e300aa4b 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0cdb0d81-1c8a-49b4-b5aa-50b627e298c6 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5e987b7a-1d92-49e3-ad2f-362501d07bf9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+98193422-6ec1-4767-8568-e34555d37244 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+23c5d21a-6ff6-4f87-950b-3189611df400 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+61b20f0c-ad75-46c5-bdb1-c9ee4db679eb 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f658e233-91f5-4e42-a97f-43303defe86d 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bf2c91f2-cfdd-4f0a-bb05-0433141ad9ce 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+44e7d282-81cf-4f35-b20d-289a41d57da9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5e9458db-1f76-4728-bf68-8f100dcb5e04 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5cf7efb5-6ce3-4bfa-9b9c-69615c0424c3 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e601de5f-ad58-4d48-83b7-bc0e20cadd7e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3995380e-ac1c-4133-a6e1-65a2b355a121 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+109dabd3-4d13-40ea-b6f4-2a94d74c7f6c 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+502c5b41-66bf-4383-918a-badfea2d25c7 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9557d7a1-d82f-4fab-a4c1-59b705f29b2e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cefbb83a-2d32-4aba-83e1-1ad7811849e9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+24fbd204-d7a7-4d11-9109-a73e52f718b1 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ef9b8d4d-3e83-4353-a80e-426e5fc7cbb9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bd6e4a2a-b1f5-4fdf-bb0d-6e9918275bd6 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a39c21f4-1588-473b-b5f0-ca58437f5670 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cd7ff4b6-0461-43d7-89d4-00df67b34598 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d46890a2-26b2-4d3c-860d-f54cc24b7663 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4d17db21-c723-4052-9a5f-d704fd01862f 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a9c1b4cf-9457-4010-a9b8-4f5236dcc5ce 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e79cb133-66ba-406a-895d-559eddf73902 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8b99e7b2-ccdf-4cb9-b185-e3cde9ec9af7 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d807dd5e-21de-4d30-823e-41d98b76bf8e 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+00284c22-d742-4a15-9a67-4bb4dcd90d8f 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+751853be-1e25-490e-a6ef-9417a6b540ef 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f73bf090-0d18-40e8-b186-7fc9e91e62d1 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+12042bab-a587-44e7-881d-2315a7305c39 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9b0c19f6-6ab2-4119-8a6f-37e8f15cdd98 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d76ebd2e-5ee7-4810-864b-3a12440faca9 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bd3ca0d9-03ac-4021-8de2-08321ccb3277 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+528428e4-3f06-482d-8b4b-65b51c3bb653 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+73e663c8-0f96-4908-a02c-5c7eea81e327 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2c40d9e2-469a-4c7a-9bcf-61552994e02e 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3e2fe25a-fc33-4a1e-a1f1-a60ac070e341 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a344e177-1f6e-4753-8404-a3fbd716a992 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ababbb85-337f-4aba-9922-41daf23c2865 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1b075615-d2ce-4b5c-997d-729c664dc4f4 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fe3e3c81-0f6c-4f7b-82d7-06022c1613b6 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+54d95a23-896b-40b4-b93a-dfe4b4083a23 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+92af388d-d0f3-41a9-ad5f-ed90b03de869 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5a61733d-2684-4d4a-9d35-bf785b7c07c2 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ece058ba-4c37-48de-a640-d7b889c4fb6c 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c2c49d74-23c3-4ce3-a9e5-f0ede3967097 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fbdc551b-4550-4528-a74d-a595aa492b51 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+92c2bcd2-bb73-4339-aaf1-8b552ceb0106 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c60849dc-5675-492f-8bab-5d8cb3626823 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1d6aa622-24ef-4888-a080-ba20e5c89316 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+204833b7-0070-4b55-9583-1df64dc7ab2a 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2cebb659-d522-4e02-9ba6-90e09ced208c 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8fd65cbb-d37c-45ad-95ba-f5bb0acf87e0 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+310fe133-a807-45dc-9dd1-6a6b1fe1d07d 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f7df66fb-1d8f-46dc-b569-de1b63a0344b 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b75d1f70-93f2-4de0-9bb4-7a1fae40e29b 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cde580a3-81d5-4cef-9858-f99a1f629422 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ebc496df-a1c7-4046-bf99-45778c2de1c6 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2a2d78fd-a19a-4a2c-80c1-816deb18c823 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+88c9d8c2-1bfd-4b33-81c7-7d77866b2d7e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0eb52ec4-f6fc-4c6d-ac31-e07b84f7e17e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1c255589-3ec2-42b8-b722-32c1f9ad2510 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b5af350e-6e66-40e4-8333-e0595f756e83 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+607a67a8-1ab1-4c96-869d-71ffc14a90cb 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+97657a2e-8286-4638-b42b-d8f1418f68f3 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8ebbdaa1-2ede-459c-8f20-9eaf6c4c5e34 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dc47a6ab-1456-4e60-95d2-50b7251072be 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+17157627-0993-4a53-ac67-5dc31565a022 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8456d2fa-f8ee-44c4-b062-376c225c6ad9 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+289e1e86-7c79-4686-910d-91d138398782 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ef250969-68ff-4fc9-a9f9-46f776374937 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f75fa431-1d5b-4a84-adc9-f2ab778755f2 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+395b99d4-38f4-4268-9cd0-fa6e0f2cff94 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fd296ad3-4272-4acb-8246-1853ba56f38c 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2128d33e-4e88-442c-a077-753f5bc3cfb1 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0e047d1b-5481-4e2e-949c-8bb2dcf9e5e9 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b3a256a3-3d0f-4a67-9518-dda233dab2a4 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+75b76bb1-fcd9-4b1d-8a07-9c89e323838d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b9fd2d19-6d98-409c-822c-b53d23fc6bf4 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+999a382f-59db-47a3-95e5-3c7c387e519c 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+12475fba-736b-41ef-b7c9-91f0ab42706f 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+991a0eb0-d11a-40c7-9c0c-69134e425825 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a8911c95-832e-49cd-bbbf-adf393a69d28 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+05d5816d-797f-4329-8693-6864ba16fa00 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b198788c-dabc-4723-aaeb-258b242f5bf7 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f827a7cb-3a5d-49dd-b15b-4a6a05c8f76c 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+37142dfa-010c-4d0b-ae54-3285c60e177c 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+82375487-c356-468a-9a2a-3999121b401e 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d15f0c0a-bce7-427d-8da1-07928f5d415b 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+24e96d1e-b429-4a11-8fd1-ec0688531b53 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+eea2568d-e01a-4936-a539-01988a96bda8 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+aea5c9f3-3582-4705-be7d-88c291890572 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+062ddf91-5330-4185-877a-f8cdc29b5580 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+839c749b-aebf-46d3-b72b-ce58fb730dbe 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+75fa1631-c22b-4234-b8e0-0e6a79d24963 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+56e78f0a-a314-4f02-865a-ccfd68eaa009 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+11b2be65-4a17-48f2-8a23-3c377c31b8bb 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8497dff1-9e4d-4a60-b7ba-d4c8ff11af87 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+712a182e-b50a-4efb-a0f0-ca4fe894e577 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ab44cae8-8ac0-41f1-9671-d07d69bb4ad2 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+86074cab-06f4-425d-b52a-7ba8958f3778 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3342939c-cfcb-437b-9ba9-ba20845e2183 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+be8251f2-6fd1-4823-8bf1-bc8c7fcd04be 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3d42dc37-596d-4996-8f00-b3c2fb6de270 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+704f1d16-e489-41d3-8a88-ee2c5b9b603f 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+de8247fa-8178-495c-9fdb-111b5ae55037 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9a548e20-7aef-4cbc-b959-e1680c595689 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6d28de77-2ca4-4bb6-bc60-cd631380e860 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9630e957-6d21-4127-b724-dc7be3e201c1 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+439b1ab5-f5d1-4fce-b52d-b2beca2c2d6b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c385836e-5c56-47a7-b3d8-2388d62b077c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5e375f63-692a-4416-a031-72323da9262b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+15ae2d93-8e77-49a2-a00b-1f8c7bf6b5a4 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b4045684-2ff9-4810-a1ca-9bd3993f7cd4 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+75d178df-1223-4f56-80b4-1bea51adfc97 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b44e03a1-22f5-4443-ba10-921c56788bfe 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8577c35b-106c-418c-8b93-90decb06af58 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+18b21a7d-7f74-48b1-b9db-9ffa2db7d904 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+62f8d892-76fb-4ef9-9b66-b0b81564bce5 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+08da3a9d-5fdf-47a8-be8f-ce287d2f2914 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e6ff5e56-255d-440d-81df-a452a2072297 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5d13ade8-944a-46a1-89db-e6707760f27a 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+783e864e-f9f2-410b-ae7e-f083694fd114 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dd29a63e-9bd9-4a46-99a2-bb4de34b390d 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d308ba72-8ccb-4b74-bc09-c3ea91561b47 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bb545b0f-69e5-4dbe-8b3a-8d692e9f0465 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+09688798-b181-4282-9b47-4ea11cbed88f 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f2f31531-6e81-4e47-8ee5-21db84a28cae 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5718da07-3088-41a8-a8e9-56d83309d49f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+858587ef-4507-470b-bf83-53d9d428607d 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e838f443-11b9-47d3-952c-b29d32c47d99 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3c00d6b0-b98a-4e77-a9e8-3255963487ca 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7968fa6f-3fce-4d76-98b7-ac7e1abd5f3b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0215b396-4130-4073-8c0b-a994e36641fc 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+053a5358-18e8-401d-8eae-709cae78044b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+645d937e-50e6-428b-a66b-b940faa02f28 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+19fa1c11-2031-49e3-8242-33a1fc7aeb18 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9832ee7f-74e0-4e0b-8897-44cfd8c7892a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0a5d0d3b-055c-4338-b19e-1fd4d196234a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+70fae9ae-8e2b-4fe7-8c2d-3c50cf88dbac 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+554fa44c-d64b-4501-84f6-8543e0ac1c42 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ff177547-b49b-4e7e-b3d9-f99ba78df0db 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+76217b97-af15-44da-8565-39546305a786 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5f70b4d9-fcd2-4a6b-b5d5-57f603a2d936 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cddf8c8a-8e68-45c7-a771-d5d2d8aca8f5 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f1e1ff63-b396-4ed6-9305-d4d045a2e9a7 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+22fa79c7-1a20-4b96-afbb-cac2c2c22706 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dc31ed76-081d-4ae2-b4d3-c249a4348842 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6331cb28-6a75-45e7-9d9d-7225d0996e0f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d9a841c6-6bf4-4cd6-921c-f38e9f772cb0 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+49b9e591-2b39-4cca-b0ad-94880347cb6e 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+50d5126f-ed18-4022-a93a-3fee8b5a2a61 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e1e1f82a-936b-49d0-8d28-ebab1f134a1b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b5815188-d327-4734-ad11-6bd6459b38a4 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0808e339-4431-4419-8c80-0bd658eb351a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8e7cf859-20b8-46cf-a515-89cff33cbaf3 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+876e891f-4820-4e1d-96d5-d86cb4ecedc1 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+84c6bde5-724f-4beb-b1c0-16f07b948029 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f612ff85-e276-47b3-a33a-63499962253d 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0e58f9e2-049c-413c-9053-520742687a6e 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+82a6fb35-6254-4f5b-8aa7-c0472632af47 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+258d783d-9e92-48d2-ace4-861cb00df9b7 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bd5dcc38-1fc4-49c0-80e2-f26fa6a49a9f 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1e5ab1ef-87e3-4ebc-92e9-ec9c0f7aaa9f 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5e35d3e9-49a9-4976-a638-4e6764ccd426 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7bab5fa6-6191-49b8-9c7e-8addeb144e8a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9bd52aa4-7158-4d06-81f2-a10f99e33f08 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b26027f8-6fc2-46c7-aef7-d9cd67fbffe3 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c00f7722-3c3f-498d-9808-cd4a86007958 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c512e792-661f-4223-bc9d-6a9c059a4a09 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5f154afd-4a66-4d1a-be2a-15354ad499fa 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6226f972-df24-4f54-a21d-e90352622724 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6337f622-dad3-40f7-9a25-acd776963042 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f60b096f-1249-4270-80eb-b451330fc934 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6f477457-1329-4c51-b556-9ab27a341116 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ba259465-73c0-4035-af03-083de17865cd 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ad7ba3c6-8d4c-4f5e-9c8b-58b6b7bc2b42 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a3caefa8-c914-44c0-ab20-e5420eef9025 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dadc0a91-472d-4792-9b8e-d573a52b9056 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8b00c8a1-b680-492a-87eb-350ca72bc616 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+24fe112c-a8ae-4ee0-9abf-b5d8a8a61f65 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+33da5233-b9f0-4d03-964e-10a619eaa459 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0158712b-2d90-482a-8ca0-5c4dfdf19d42 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+91dbc846-4c2b-48f0-a5a4-651c884f2b5b 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5a2fb39c-5e8a-42ce-bcbe-a84fa6e4d12d 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4994d988-d33f-46ae-bec1-f59018f68103 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3d398236-c1e0-4051-9845-39c6d0d4b547 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e2d0e93c-d371-4a4e-a0c8-f30530c873ab 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ecea8625-a170-4648-b363-e132983ebbcf 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bfb8643d-7f56-4d95-b2a7-cce9f6a75598 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+93947ca9-1278-4b68-bf9a-3be07d766959 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b81aaca3-eebf-4445-8bd9-f803b8b54551 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4f0fe748-796b-413f-a4f5-3cbbe44c27c2 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f406cf4a-75c3-4ccf-8f36-9255b36e0f69 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e2817bf9-36c2-4acf-8de3-4468b149d571 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c3f8cf8e-0683-40bc-aabb-8695dce534a2 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+da395198-c4a7-4d67-9e0f-8ea9bd6a72db 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e5763c8f-13d5-4f01-8ebd-b6db40a89fb0 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1d84611e-9887-40c6-ab00-01210d1f82b7 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c238d775-2523-46fc-8d1a-540fac1f6896 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1d915ba2-c858-4732-a9e9-7b21b9d47b27 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2ddd0eb3-bada-4443-bbfe-5fccde527dca 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+fb6cc1c1-f874-4ad9-9a62-3b406f948218 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a7946bd4-5a6b-4f56-bbd5-59cf59fbacc3 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c2a397d2-8f91-41d8-9158-97dd24955a80 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+959074dc-9a50-4bd8-bb49-d0a9333d0477 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4fafaa54-d47d-4488-8c56-94be290f38b7 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e9556ed2-8e33-4130-a9b9-fc6c799655fc 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9a6c8306-cf36-42a6-9117-724b675fd9a2 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+af36e2ce-968f-4143-926c-34f5827a2319 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+59a3ea50-4f62-4ce2-ad54-8d72abe1ec68 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+45cc6295-8cfc-4e44-b124-0d05c04cdd3e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8b3db5a2-f3c4-4d2b-b60e-55c3f0d42960 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+809b0fa5-91fe-4f0b-bfa4-1b17ca92647f 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c75cdbd1-8145-48ae-8097-d6ce0ee3d383 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e238e1f2-7acb-4caf-a7b9-4abc165b2f78 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+579dd648-5a51-4240-9901-d59ea046dbe4 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+363e3fd7-2510-4b88-8b61-19c6a701a154 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6bfe7e94-4211-492f-a9db-a6c81dd6f547 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+614a1279-a381-4be2-acef-301958e89071 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3861f439-875f-453b-8651-03d9359f5788 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0663d4a9-d9d4-4d92-ab92-8ecae04c5440 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+00a04a0e-8a61-497e-a1b7-555d9edebd3c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a90836ba-dcb3-4f3f-bf2c-02bc1d5f7453 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+001879e3-9e6a-49e1-8893-9bfa1ed0662f 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3b864315-4410-47c4-8d1f-41340443be83 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+da92e9da-c205-44a5-8e55-6cabab24e221 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ec7a7ee9-84ef-4e7e-86dc-6c1ea5db4019 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+de23c01f-138f-4b4f-b077-7966e5301849 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2231820c-c6c6-4b43-8030-60d84ec840df 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+962b06e6-2702-4267-b103-b352f6b842a4 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+63bfee6a-6d44-4301-9cee-df0105f24f5e 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c6a5a31e-2c88-47c4-8e9a-c60bece7ef75 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2d096abd-ffb0-4143-96a4-7779218d6d4f 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a10741c9-4ed7-422d-9f52-54c17c4bbd8b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+234c48dd-9af4-4099-80ff-40ad13f89401 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bb5d6545-d507-4b3a-ba24-bb510c914e95 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+28f712ea-c08c-4e7a-8cf9-4b13e36ff212 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+152a5d0e-dc5a-44d9-af10-8ec63701dd3b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+93857261-5bcb-47aa-9144-22b35b135d4b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+111f99da-d06d-4cb3-b864-8f3e1f49aa74 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3924e923-d2f1-4275-8747-bd11ac4f74d3 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a73038fe-4577-4639-a479-767f244244c3 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4a062dd6-f1c2-4b36-ac1d-998925eb0b83 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8c475290-e87c-4711-a6ac-d2dc4028fad6 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8cec9caf-f09c-4e50-ab29-a23009c77cb7 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3a1b190c-0930-4404-bee0-eca6c7621114 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ccb26ed5-9dd0-46b3-8cb5-3584782c9d06 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6bce2b2a-c6a0-4463-9dfc-bd9366f62b3a 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+050c4646-3958-40b1-92f3-2a7979732b5b 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dfc084df-46cb-4a7e-b89c-b84ae3634ed3 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5c96e4e4-bd3c-458a-aecb-70a0e97258d6 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+643ed9d5-7abd-498c-aa27-e54406f62657 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3b43313b-92e3-4a71-89b9-5c94e508ffa4 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d1f25d2e-1765-431d-b8ce-c971848c140b 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a986ba78-0f21-4714-98af-030c39a99d98 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+186d8c4f-7240-47be-baec-da9793982cfe 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+29eb0b4a-38c1-44e3-a342-a738f884bdb8 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d6344072-d70a-419e-b400-f792fd7816a6 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+65dbc1e9-8bf0-4494-b3e7-c6b6445d805f 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+82e159a7-b83d-4eb9-9228-26eea20c0301 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+85cab86c-ef60-4b00-ab3a-83649782cbdc 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6d8a4447-dba8-40c4-8fa3-9ea447aa4431 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+297aa958-dd8d-4838-8658-21c7a2f6a45c 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+516d1b3c-20ec-4abe-9d05-7c10f45cc2b7 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c2cfb252-5288-4b94-b4a8-79a8d86e6c7c 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d32ddeef-adf4-43e5-b533-d6218f89194e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d735e2a6-44ce-421b-8041-dbeac83b0388 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2f34b698-bdc6-4a34-8568-54e2051c301e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1f25c2c5-b997-474a-82c0-2dfe225b38f7 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+409a0334-ad83-4abe-92bf-9f86cee8e629 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+21a86be9-f740-47d6-aef6-ea678179d442 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dc85040e-5868-4e67-99ae-ae2a83870651 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+83f56af1-9785-4627-8682-5d9f40d9e567 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b8670494-46f7-4ac6-a67b-92662a89eabb 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cb4d87c3-1fb7-4b16-8094-eed4a3d00968 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+106044fb-fc87-41f6-9e71-3faffe47e00b 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+a88fd1e2-7344-47b5-a7b8-9bd716f94c5d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+53f91d1f-e644-4040-bb9c-009b94cdb8e8 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dd07fe79-a01b-4e7e-b0d7-2556523cb39e 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b2faf9ae-52e2-4dae-a484-7e9978de7057 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+587584bd-581c-4ec6-90a4-4196ebe3e639 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c1e06d08-f053-4e2f-98cb-dfe2b4523fc8 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ce17ffbe-39d4-4bba-badd-3fd6a51a909b 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+df0f28b8-833d-4962-9750-0e2c7dcf1aef 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+42463594-07f9-463b-8d3d-e640679cf9a0 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8dc13325-56ce-4b86-bd36-b090b0f6caab 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c629d453-a5a6-431f-8f90-9b27722a415a 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c265592f-8adf-4f8c-bb4f-1b4a984dc600 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bbfadf44-58fe-4693-9f6b-f1897ad92eb6 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+515bf1e2-6b17-448a-ad26-6276526a88c2 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4f1086b3-8849-4d42-a9fb-5395f1cb573f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d0e54e7a-8475-44f5-af06-0852acc18ada 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+cedaaa13-f4a0-4aa1-86bd-29f20d10cb17 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+af2095eb-cb46-45e8-8e62-23c528e8451c 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+39f8b870-e4a7-4f7c-93ba-7354ffdc3b7a 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+8b196676-5e99-4ffb-9cf7-e59dd42c9b61 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3ed2e405-1166-499d-84ca-abf27c4420d6 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6e94f9f7-f322-4be2-a6e3-25220b00d9f6 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+2ee7b426-001c-4f81-a4b9-f5f6e94dacd9 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c235ddd9-4a8b-4ed4-996d-f32d97c2febf 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3443f990-ed97-482a-b60d-f9a4fae6dce7 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+bf3887ae-ebac-4278-aa88-b211be9a6ef4 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f5db483a-11d5-4fb7-b977-ddb1b55b6923 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7560adfa-0d51-42e6-b727-78821e9404f8 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+efe7075c-0084-4620-976d-57dcbaf3893b 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f062ee0d-1d60-4ac5-bf80-fad59a54306f 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+838a3bbf-b6e9-4174-9e2f-4c5903f85b51 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1813a575-32ba-4c94-99a5-19295b0921de 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7aff390f-97f8-4e64-9b95-c85a9002c33c 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c6298096-10b7-441c-9688-4695b88a8660 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+dada2f21-3866-4778-a319-a91f82f8ad76 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f5016d6d-f10c-4846-83d5-7bf231c044d3 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7463f25e-841f-4e23-9fb3-4dbe0c2554d2 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+1e87a29f-8009-41bd-8b71-f8800f1dab1e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+30e14345-9d6a-42c1-b33f-59cb014e5b68 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+86c6fa66-322e-487a-8999-ecc03a830fd3 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+35847d15-de55-4a1b-9493-0d691a83a641 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f18b3241-50bd-45b5-8c61-8858473e10fb 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3f90d40a-eef1-4a6b-953c-6919087c9b6b 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c81f7cfe-c388-4731-88f9-f3eccc0e1aae 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+54f45fd9-b956-4dd8-a9a2-aa025395fe9b 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f0f92b13-e8a2-4208-af35-88c2f57053ed 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+50b2eea6-fcae-41c7-872a-7f725aad8f68 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5d22741a-9f70-4978-a113-4e3370595e14 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+5e9f240d-6e21-4393-b37c-f9f1e8ca70f3 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+84d0828f-fe77-41f1-928e-11706edb8821 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+7c9d3f4c-4e57-450e-b12f-7db6ebcb9aea 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+b1f4f818-0f47-4372-868c-df50e9603ed0 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ea4910d2-9eaa-4e94-8f10-94d0da66aa12 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+84164c99-8064-4616-9b89-4ad2cd3ee6da 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+64f3861f-7ec7-45bf-a781-73de35a51bf3 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+0501b4de-a562-45ac-a4f8-ca0b0a5f2be4 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+edf40205-69ee-4f3b-ba0c-09d70531b17b 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+f18530a1-b79f-404c-97b5-c8cb7d4df0d3 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6b7f220c-1df2-41b3-9ea3-a6bd5ece4a4f 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+06b00f42-c69b-4243-8506-582504283fb7 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+9fa2ce85-2954-470e-9a8f-b80a94d18b5c 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+690744c2-57e5-458b-aa9c-eec197957ecc 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+4a74034a-2448-42f4-98d3-dc1fe050f6ce 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c4507468-ff51-4d6f-977f-0969cca30830 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6c865afc-9439-411c-ade4-6fd8ac429c07 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+e04db553-36a3-468d-82b4-938514fc8cdb 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ecaca662-b04b-474b-a038-c185ac99a3e1 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3c19f673-974e-4d27-8aa8-c8b3be9a268a 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+6c5851b2-0b70-4fd8-9d95-b5f60e89b8d8 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+ca7691e7-644f-4503-8661-255efc4f2d73 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+c520c41e-eaac-436b-8943-9d96b749a386 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+35071e24-8e47-4af5-adfd-b91431777cfb 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+3206e638-1f43-47b7-8b36-e5a70cf785b2 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+d665c6e1-e3a9-4f58-bb0b-29a67711080f 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 5 http 172.17.0.15 18088 /test 60000 60000 60000 \N \N \N \N \N dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t
+\.
+
+
+--
+-- Data for Name: routes; Type: TABLE DATA; Schema: public; Owner: kong
+--
+
+COPY public.routes (id, created_at, updated_at, name, service_id, protocols, methods, hosts, paths, snis, sources, destinations, regex_priority, strip_path, preserve_host, tags, https_redirect_status_code, headers, path_handling, ws_id, request_buffering, response_buffering) FROM stdin;
+ce537a9f-a4b0-4104-aafd-97003b6bd094 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N a7182665-e3bb-4ad0-91bc-bb013404d465 {http,https} \N \N {/s1-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+026dab0d-bb9f-4d78-86c6-573ae01c04d8 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N a7182665-e3bb-4ad0-91bc-bb013404d465 {http,https} \N \N {/s1-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7d278d10-142a-451d-866c-86ae52e3ba14 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N a7182665-e3bb-4ad0-91bc-bb013404d465 {http,https} \N \N {/s1-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+990d5f16-8024-4568-811f-117504c9990b 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N a7182665-e3bb-4ad0-91bc-bb013404d465 {http,https} \N \N {/s1-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3ede165-bfca-4ab9-9db7-f9c2de77039e 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 3c089a41-3c85-4e95-94bc-9dcbcc02d5bf {http,https} \N \N {/s2-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+951b5a6f-b4d2-4ed4-87ff-dfeb57555c7e 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 3c089a41-3c85-4e95-94bc-9dcbcc02d5bf {http,https} \N \N {/s2-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dda0f202-7c28-429d-8ec8-161e9e31514e 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 3c089a41-3c85-4e95-94bc-9dcbcc02d5bf {http,https} \N \N {/s2-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+87655776-806e-47ed-baa3-3fbf5a758c4a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 3c089a41-3c85-4e95-94bc-9dcbcc02d5bf {http,https} \N \N {/s2-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f8b9a2ce-83aa-4af4-8ce7-436cedf59d26 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e4e0c0f8-8f86-4138-b90b-1ab4b42c545a {http,https} \N \N {/s3-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83d60efb-3057-4303-9114-916a98a99889 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e4e0c0f8-8f86-4138-b90b-1ab4b42c545a {http,https} \N \N {/s3-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d32ba84f-ebb5-4ebf-a19f-50d4d0ff3c98 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e4e0c0f8-8f86-4138-b90b-1ab4b42c545a {http,https} \N \N {/s3-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+67f1d309-3609-4eff-ba4d-f05413c56570 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e4e0c0f8-8f86-4138-b90b-1ab4b42c545a {http,https} \N \N {/s3-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2938219c-3438-4647-a665-2a2bfa59a166 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 635667df-d7c8-4c8e-961a-79094fb7edf7 {http,https} \N \N {/s4-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+43acaeda-d0b1-4660-a71a-131268b234b0 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 635667df-d7c8-4c8e-961a-79094fb7edf7 {http,https} \N \N {/s4-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+db8f7f38-cba3-41b1-b824-c939b1dd4386 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 635667df-d7c8-4c8e-961a-79094fb7edf7 {http,https} \N \N {/s4-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b8c7f85d-4ec7-4921-b50b-720c26bac325 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 635667df-d7c8-4c8e-961a-79094fb7edf7 {http,https} \N \N {/s4-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+abca1b75-1d6d-462c-9787-48122922fb65 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5db07df7-6efa-42f1-b526-aeea5f46aa7f {http,https} \N \N {/s5-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1da0d3cf-1d35-4e93-9855-6bd555445561 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5db07df7-6efa-42f1-b526-aeea5f46aa7f {http,https} \N \N {/s5-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e4073ba4-1f39-4ea5-92b9-ee723f1c7726 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5db07df7-6efa-42f1-b526-aeea5f46aa7f {http,https} \N \N {/s5-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+064d691b-e410-414f-9a14-1375cfdfc3c9 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 5db07df7-6efa-42f1-b526-aeea5f46aa7f {http,https} \N \N {/s5-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ab58907f-2df9-4170-b0f0-ad00fb5d387f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0cf9ed94-6fe4-4356-906d-34bf7f5e323d {http,https} \N \N {/s6-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+506a4858-240b-4339-9d13-8018fb2a839c 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0cf9ed94-6fe4-4356-906d-34bf7f5e323d {http,https} \N \N {/s6-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+720ec3bf-2799-43e6-a16a-4e8e21e64c8a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0cf9ed94-6fe4-4356-906d-34bf7f5e323d {http,https} \N \N {/s6-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+89190960-6e45-480a-8a02-13a48244eacc 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0cf9ed94-6fe4-4356-906d-34bf7f5e323d {http,https} \N \N {/s6-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de05c71c-0e19-4909-9dc8-0f02b07f4d3a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b0d849d4-9d3d-48bd-bddd-59aeed02789c {http,https} \N \N {/s7-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0cc280f0-5fc2-4379-b26c-a29564103995 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b0d849d4-9d3d-48bd-bddd-59aeed02789c {http,https} \N \N {/s7-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eded9ada-6e08-41cf-aa4f-217e6c57529e 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b0d849d4-9d3d-48bd-bddd-59aeed02789c {http,https} \N \N {/s7-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+81d8b01a-fd3e-45d2-bb08-329d107c13cf 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b0d849d4-9d3d-48bd-bddd-59aeed02789c {http,https} \N \N {/s7-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9ef63d3e-c320-47ee-a73f-ccf836e589a1 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d609eb1a-3c6c-4867-ae94-ad5757bab196 {http,https} \N \N {/s8-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ba1fa05f-e8f5-4f8d-a3fd-3c2df6dedee2 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d609eb1a-3c6c-4867-ae94-ad5757bab196 {http,https} \N \N {/s8-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0eea660-89a0-4742-b94b-b5f3d13e1750 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d609eb1a-3c6c-4867-ae94-ad5757bab196 {http,https} \N \N {/s8-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+601c7cb8-8e28-4fac-ab85-c7f24b74f0d3 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d609eb1a-3c6c-4867-ae94-ad5757bab196 {http,https} \N \N {/s8-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e1cbed49-b206-4dbe-a7dc-4a92e4eecc39 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d92656d5-a8d8-4bab-93cf-5c5630eceffb {http,https} \N \N {/s9-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+11a07f35-5489-46bf-ac75-9169be6b137e 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d92656d5-a8d8-4bab-93cf-5c5630eceffb {http,https} \N \N {/s9-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d12800df-5095-4753-8269-1a75098bb08f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d92656d5-a8d8-4bab-93cf-5c5630eceffb {http,https} \N \N {/s9-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7e2f69a1-3bd6-4676-be97-f89694953713 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d92656d5-a8d8-4bab-93cf-5c5630eceffb {http,https} \N \N {/s9-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aa2a94b7-2b36-49bc-bd65-e9eeefe04497 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 1e306cf3-2a3b-40b8-91b4-f50caf61d455 {http,https} \N \N {/s10-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+39809835-2739-4f66-b3d4-bfea8be6ede4 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 1e306cf3-2a3b-40b8-91b4-f50caf61d455 {http,https} \N \N {/s10-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+530b83b7-8e49-47a2-86ee-d1fd4f9eaf9f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 1e306cf3-2a3b-40b8-91b4-f50caf61d455 {http,https} \N \N {/s10-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d6817e92-beba-465b-8352-735005f5e981 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 1e306cf3-2a3b-40b8-91b4-f50caf61d455 {http,https} \N \N {/s10-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+df99cf4e-cd34-4be5-98d6-8470c1c1c211 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b13775fd-dac8-4322-b7a4-a089d677c22d {http,https} \N \N {/s11-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ab0e0fb7-5928-48ab-989a-2081b43e7245 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b13775fd-dac8-4322-b7a4-a089d677c22d {http,https} \N \N {/s11-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+687dd969-c8f6-44f3-b371-e631048cb4cc 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b13775fd-dac8-4322-b7a4-a089d677c22d {http,https} \N \N {/s11-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fe454395-7df3-44ed-a95b-9e629e9cd650 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N b13775fd-dac8-4322-b7a4-a089d677c22d {http,https} \N \N {/s11-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb222d61-3fe9-4735-9405-e15ff5e8a121 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0d5ae4f4-5ab1-4320-8057-cd0b21d81496 {http,https} \N \N {/s12-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7ddf114b-6438-4bbf-abd3-413def649544 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0d5ae4f4-5ab1-4320-8057-cd0b21d81496 {http,https} \N \N {/s12-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+268e6d41-da24-4004-81c0-f8921fc1a899 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0d5ae4f4-5ab1-4320-8057-cd0b21d81496 {http,https} \N \N {/s12-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6c748b5f-ddd3-4689-a68f-fc170bc46870 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 0d5ae4f4-5ab1-4320-8057-cd0b21d81496 {http,https} \N \N {/s12-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+87de8f22-9a89-470f-bc3d-d2d6bad9afc0 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e6a15913-9bdf-46ed-8e9e-b71a91b1197a {http,https} \N \N {/s13-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4d34d19f-f9f1-4d8a-9771-33a5b50ed259 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e6a15913-9bdf-46ed-8e9e-b71a91b1197a {http,https} \N \N {/s13-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+85a52175-ec74-448b-8119-167cfc2eb741 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e6a15913-9bdf-46ed-8e9e-b71a91b1197a {http,https} \N \N {/s13-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+518ae3ba-72fa-43eb-9ad4-b74bcbddae72 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N e6a15913-9bdf-46ed-8e9e-b71a91b1197a {http,https} \N \N {/s13-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d74ab53d-6bf3-4927-8905-8f365b6ec8ad 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9124182f-7ccf-465a-9553-4802b87f4308 {http,https} \N \N {/s14-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9d845b80-bdc8-4142-b388-7318003da3b7 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9124182f-7ccf-465a-9553-4802b87f4308 {http,https} \N \N {/s14-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+50cd9f88-ebdf-480f-9ef8-7fb900dc1b2c 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9124182f-7ccf-465a-9553-4802b87f4308 {http,https} \N \N {/s14-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f9362a76-362f-4620-b9e9-8ee86a71fb1f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9124182f-7ccf-465a-9553-4802b87f4308 {http,https} \N \N {/s14-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b105fd40-f6b8-4d6f-b677-b89354ffbe10 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N ad9d034f-2de2-4a1a-90ad-7f1cf7039a2a {http,https} \N \N {/s15-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a9020690-1174-4166-8046-8d7fff7e47dd 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N ad9d034f-2de2-4a1a-90ad-7f1cf7039a2a {http,https} \N \N {/s15-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f30c6ce3-bf1e-4a60-8f7b-bd1381e1ff35 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N ad9d034f-2de2-4a1a-90ad-7f1cf7039a2a {http,https} \N \N {/s15-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18f0c2ff-0553-484d-bcdd-eca0c08ed669 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N ad9d034f-2de2-4a1a-90ad-7f1cf7039a2a {http,https} \N \N {/s15-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bb92af61-c9af-42d1-adab-94110ffa746f 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9d36f4e2-ba97-4da7-9f10-133270adbc2e {http,https} \N \N {/s16-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+56a88ba6-ca21-4209-86d3-1962008dd901 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9d36f4e2-ba97-4da7-9f10-133270adbc2e {http,https} \N \N {/s16-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+886aa74b-b7e2-4b61-8032-5a2b535835fe 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9d36f4e2-ba97-4da7-9f10-133270adbc2e {http,https} \N \N {/s16-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a7a6feb5-505d-434c-ac5f-eb950f1c6182 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 9d36f4e2-ba97-4da7-9f10-133270adbc2e {http,https} \N \N {/s16-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6424529b-bb46-426c-aa19-f152165a324b 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 71164672-4b79-4b4c-8f23-d7b3d193996f {http,https} \N \N {/s17-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be9aad50-ec49-4814-9039-4ff577f7569b 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 71164672-4b79-4b4c-8f23-d7b3d193996f {http,https} \N \N {/s17-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0eefde66-b48e-455d-9bc8-92acd58b560a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 71164672-4b79-4b4c-8f23-d7b3d193996f {http,https} \N \N {/s17-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d635dbe5-5d60-454f-a3da-6ac2533c1e74 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 71164672-4b79-4b4c-8f23-d7b3d193996f {http,https} \N \N {/s17-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b3840619-8d47-4100-a917-7691e5497e38 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d2c68623-5766-4b26-a956-aa750b23e6b9 {http,https} \N \N {/s18-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d2566c3f-2118-4606-bf81-e95fa302e846 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d2c68623-5766-4b26-a956-aa750b23e6b9 {http,https} \N \N {/s18-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e90c02a9-bda8-4bfe-8eb1-d940fcbb7fc2 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d2c68623-5766-4b26-a956-aa750b23e6b9 {http,https} \N \N {/s18-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ed8af14-3b87-4905-b340-59ec4dd04e8a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N d2c68623-5766-4b26-a956-aa750b23e6b9 {http,https} \N \N {/s18-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e4e90c18-64d2-4853-b682-73a469787fe0 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N c733f9c1-8fb2-4c99-9229-d9a3fe79420f {http,https} \N \N {/s19-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fb9f0ded-d0b8-4c03-a073-89c598b19c08 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N c733f9c1-8fb2-4c99-9229-d9a3fe79420f {http,https} \N \N {/s19-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+198ff565-1db6-40d2-8457-2660761f281a 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N c733f9c1-8fb2-4c99-9229-d9a3fe79420f {http,https} \N \N {/s19-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fdb2ac7c-69cd-4564-a503-9b7bfa2d76a0 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N c733f9c1-8fb2-4c99-9229-d9a3fe79420f {http,https} \N \N {/s19-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a3b39229-514e-413c-ae7b-ee17bdf507eb 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 879a9948-ed52-4827-b326-232b434d6586 {http,https} \N \N {/s20-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+26841471-0b61-4845-b128-d428f9919ee7 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 879a9948-ed52-4827-b326-232b434d6586 {http,https} \N \N {/s20-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29ff0e49-5e6d-482a-8a50-72b979170e93 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 879a9948-ed52-4827-b326-232b434d6586 {http,https} \N \N {/s20-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d94f7d16-b7e1-4eec-adfc-c144e166f9b0 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 879a9948-ed52-4827-b326-232b434d6586 {http,https} \N \N {/s20-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c5db351e-2352-43d3-b046-6ec73064c5a0 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 6c2f637e-3365-4475-854d-2da53cf54236 {http,https} \N \N {/s21-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cbb4f546-15a9-482d-a808-1d1359ac1d19 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 6c2f637e-3365-4475-854d-2da53cf54236 {http,https} \N \N {/s21-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+549e80fd-38c1-4cb9-bbf1-561eb56bf039 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 6c2f637e-3365-4475-854d-2da53cf54236 {http,https} \N \N {/s21-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dfc428de-00bc-4def-b283-cf4cfef5d33e 2022-05-26 09:04:20+00 2022-05-26 09:04:20+00 \N 6c2f637e-3365-4475-854d-2da53cf54236 {http,https} \N \N {/s21-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b8a634c1-3431-48e9-949c-dc813a26c0e5 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e5322b5b-36ef-4b9d-9238-99de86473537 {http,https} \N \N {/s22-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ffafdf04-2fff-47ca-a8c0-0af508ebff8b 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e5322b5b-36ef-4b9d-9238-99de86473537 {http,https} \N \N {/s22-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cc56a218-8f01-43a3-bfbf-8898f9f077c3 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e5322b5b-36ef-4b9d-9238-99de86473537 {http,https} \N \N {/s22-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+90ad98ec-a31f-4519-9c73-e862c7d4d6d9 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e5322b5b-36ef-4b9d-9238-99de86473537 {http,https} \N \N {/s22-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0edca7d2-23cc-47e5-b4a6-7f9e7da0c027 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d71477b1-e512-4b80-b755-d0a074de32c5 {http,https} \N \N {/s23-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ddca0b2a-92fe-4a65-9478-6b41ea60c00c 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d71477b1-e512-4b80-b755-d0a074de32c5 {http,https} \N \N {/s23-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+457feef6-a801-40e9-b4ce-d399837dca7d 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d71477b1-e512-4b80-b755-d0a074de32c5 {http,https} \N \N {/s23-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f70623a9-84ca-49ef-aee5-4c52eafa03ab 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d71477b1-e512-4b80-b755-d0a074de32c5 {http,https} \N \N {/s23-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4aa16fb3-d011-4567-8176-657a667209cb 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 548bb3e7-fc07-41c9-9299-84a0708a2a59 {http,https} \N \N {/s24-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ba2fc179-cfcd-4a3b-ab21-ce4b8e972aaf 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 548bb3e7-fc07-41c9-9299-84a0708a2a59 {http,https} \N \N {/s24-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6e85ad75-31f0-4d3d-8e6c-1a9f1bdfe081 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 548bb3e7-fc07-41c9-9299-84a0708a2a59 {http,https} \N \N {/s24-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4a07074a-c606-48bd-abb4-2444416c6d12 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 548bb3e7-fc07-41c9-9299-84a0708a2a59 {http,https} \N \N {/s24-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0c9fe8c7-ae08-45b1-8d4c-2747e825afd4 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 4ce0aa65-7a39-4c13-8560-50cbbfbfb393 {http,https} \N \N {/s25-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+64a162fc-842f-4c07-beaf-55a86c16f24a 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 4ce0aa65-7a39-4c13-8560-50cbbfbfb393 {http,https} \N \N {/s25-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+683651ca-d817-4ab7-8feb-e54d9eddcc53 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 4ce0aa65-7a39-4c13-8560-50cbbfbfb393 {http,https} \N \N {/s25-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ec12d55-4015-4b04-8093-cccc7e7d2661 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 4ce0aa65-7a39-4c13-8560-50cbbfbfb393 {http,https} \N \N {/s25-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e7e4ceb-f130-480c-8241-7a77c918d0f3 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N f4dae3be-eb46-4361-b84c-da2f83277f00 {http,https} \N \N {/s26-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d601e820-4af1-4cb0-af6a-0f7ad0dae115 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N f4dae3be-eb46-4361-b84c-da2f83277f00 {http,https} \N \N {/s26-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b763763f-0334-45cc-9475-947acf30317a 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N f4dae3be-eb46-4361-b84c-da2f83277f00 {http,https} \N \N {/s26-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+918dfc23-1bf0-455f-8246-e9fdf3482af3 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N f4dae3be-eb46-4361-b84c-da2f83277f00 {http,https} \N \N {/s26-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a4069609-ba31-4814-a0c7-b9ee8d929864 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 25076386-d45e-40fb-bf23-6078de3ecab7 {http,https} \N \N {/s27-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e996f687-3c69-42d5-86b9-79bc5a996483 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 25076386-d45e-40fb-bf23-6078de3ecab7 {http,https} \N \N {/s27-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ab23c967-bcac-4ac5-a1d7-91a32dd62f97 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 25076386-d45e-40fb-bf23-6078de3ecab7 {http,https} \N \N {/s27-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a824c45-c692-48be-a227-344f969f79fb 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 25076386-d45e-40fb-bf23-6078de3ecab7 {http,https} \N \N {/s27-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bf57fa62-4d82-421e-8128-b63389a7c31a 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 1525a86d-6ae4-421e-a2dc-d5758ba22312 {http,https} \N \N {/s28-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9dac7bc5-4c4c-418b-9687-bd993813d177 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 1525a86d-6ae4-421e-a2dc-d5758ba22312 {http,https} \N \N {/s28-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9d8db65b-05e9-4eb2-bec1-6ecc475c502e 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 1525a86d-6ae4-421e-a2dc-d5758ba22312 {http,https} \N \N {/s28-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c8a45988-17e9-44a4-b52f-632754ec0e01 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 1525a86d-6ae4-421e-a2dc-d5758ba22312 {http,https} \N \N {/s28-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+669e731d-8cae-4104-a4ef-d66b111b874a 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 2c961425-9119-41ad-8df7-7b288060e995 {http,https} \N \N {/s29-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dbcdd268-877e-4f91-9b60-8b36b84d2c96 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 2c961425-9119-41ad-8df7-7b288060e995 {http,https} \N \N {/s29-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4dfd810-a17e-499d-94b0-7e638aaecba6 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 2c961425-9119-41ad-8df7-7b288060e995 {http,https} \N \N {/s29-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1c7bc1c1-bda1-4ef4-8a62-b7d634f6f203 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 2c961425-9119-41ad-8df7-7b288060e995 {http,https} \N \N {/s29-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5dc8539b-5cca-4efc-8669-2219dc5d448f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N b960c35a-83b5-425b-9fe3-2602de569f5d {http,https} \N \N {/s30-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b58cef55-87f5-4cda-9721-2a4c84b25989 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N b960c35a-83b5-425b-9fe3-2602de569f5d {http,https} \N \N {/s30-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7dd956b6-1ef4-4a41-87e8-368ef00fe657 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N b960c35a-83b5-425b-9fe3-2602de569f5d {http,https} \N \N {/s30-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4947d674-d901-41de-bdbb-3dccd8481324 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N b960c35a-83b5-425b-9fe3-2602de569f5d {http,https} \N \N {/s30-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fefc368e-d9cc-4755-98c3-566e6f09ca09 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N a882f2cc-b1ac-40a4-8e5d-09d9595c5140 {http,https} \N \N {/s31-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+36e460b6-9905-4bb6-861a-86a0ab41a8f8 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N a882f2cc-b1ac-40a4-8e5d-09d9595c5140 {http,https} \N \N {/s31-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7ca48a70-91b4-4a7e-ada0-3557721356e7 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N a882f2cc-b1ac-40a4-8e5d-09d9595c5140 {http,https} \N \N {/s31-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5292334d-0aa6-4bae-815b-251dc6aba82a 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N a882f2cc-b1ac-40a4-8e5d-09d9595c5140 {http,https} \N \N {/s31-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1cd66e88-7b56-4194-a5aa-b085ba8c3fa1 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d730b9c1-e795-4c90-b771-3e3ceb21ab91 {http,https} \N \N {/s32-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9692a20a-63c7-4fa4-b66e-48f4ffc9c357 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d730b9c1-e795-4c90-b771-3e3ceb21ab91 {http,https} \N \N {/s32-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2fc1c1f1-ab58-456d-a2a7-a7a1df329d94 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d730b9c1-e795-4c90-b771-3e3ceb21ab91 {http,https} \N \N {/s32-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+81ef3ae6-5a6c-4d71-9336-33a1c2845adc 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d730b9c1-e795-4c90-b771-3e3ceb21ab91 {http,https} \N \N {/s32-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4d6fc086-96b3-4f41-aa09-02e5a338c0fe 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 406467e3-6d3d-40a2-bc8e-9942b8be51b8 {http,https} \N \N {/s33-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+128ea615-7397-4a1d-b74d-0e4e6ee801ce 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 406467e3-6d3d-40a2-bc8e-9942b8be51b8 {http,https} \N \N {/s33-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e4f52da1-5142-4f5f-ba1f-2b8127a0a2c5 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 406467e3-6d3d-40a2-bc8e-9942b8be51b8 {http,https} \N \N {/s33-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e82380ec-b2d3-4bb6-b8e1-5dcb4f741dc3 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 406467e3-6d3d-40a2-bc8e-9942b8be51b8 {http,https} \N \N {/s33-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+352279df-6cd4-42ef-90dd-3ae028f5b699 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d5ab8d0f-b02b-4bd6-9d46-ab7da78e15ef {http,https} \N \N {/s34-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c7fa960c-c1e6-4623-9ff3-72ce9bd6758d 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d5ab8d0f-b02b-4bd6-9d46-ab7da78e15ef {http,https} \N \N {/s34-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+246ff19e-15b6-4e33-8f2b-6d5b9e687c1c 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d5ab8d0f-b02b-4bd6-9d46-ab7da78e15ef {http,https} \N \N {/s34-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+58e550cd-0677-49a3-8bbc-2d1891873baa 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N d5ab8d0f-b02b-4bd6-9d46-ab7da78e15ef {http,https} \N \N {/s34-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a4532c1-f9dc-49d1-ad39-151239e516fb 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 62131b85-cb9b-43d1-97d8-f4b2966dbb68 {http,https} \N \N {/s35-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2d73aacc-bbaf-445b-bc47-de9e6d80ce16 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 62131b85-cb9b-43d1-97d8-f4b2966dbb68 {http,https} \N \N {/s35-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dd47894e-2118-4d74-8de3-4f91c6bf639f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 62131b85-cb9b-43d1-97d8-f4b2966dbb68 {http,https} \N \N {/s35-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3b5b3fcb-ceab-4701-ae85-6f8e22d6423b 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 62131b85-cb9b-43d1-97d8-f4b2966dbb68 {http,https} \N \N {/s35-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29c14bb1-8764-4af1-9a63-928ba3dd9dea 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 35fefbaf-66df-47b2-abf0-1231af2788b5 {http,https} \N \N {/s36-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d2df53a9-2573-4dfe-be1e-4e7a11c75d77 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 35fefbaf-66df-47b2-abf0-1231af2788b5 {http,https} \N \N {/s36-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+82d7563b-eee3-4340-8ab4-cbdc8472d146 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 35fefbaf-66df-47b2-abf0-1231af2788b5 {http,https} \N \N {/s36-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+20c189d9-f3ed-4bda-953a-9c2b4b519ea3 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 35fefbaf-66df-47b2-abf0-1231af2788b5 {http,https} \N \N {/s36-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fcc15e73-c6ab-4492-8ac7-7fe0a9708dc2 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 63639c14-7690-4f27-8a69-4df1aca28594 {http,https} \N \N {/s37-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a1c1ad43-bf6a-4faf-9156-69b6b9d58050 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 63639c14-7690-4f27-8a69-4df1aca28594 {http,https} \N \N {/s37-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0d78b89e-9791-4da5-835c-4c042bf09a63 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 63639c14-7690-4f27-8a69-4df1aca28594 {http,https} \N \N {/s37-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+454f4856-baee-4b83-9f68-f0802d603a49 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 63639c14-7690-4f27-8a69-4df1aca28594 {http,https} \N \N {/s37-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8897263b-fb1a-4bdd-befb-386b52a8798f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 872066a1-4cfb-4f69-ab14-2de00fe8a82e {http,https} \N \N {/s38-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3a41ff4-4d09-4bae-8352-ac0feed50567 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 872066a1-4cfb-4f69-ab14-2de00fe8a82e {http,https} \N \N {/s38-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f15c7ac8-248d-4dd8-b844-26ec3baebad8 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 872066a1-4cfb-4f69-ab14-2de00fe8a82e {http,https} \N \N {/s38-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0bb3c7fe-b614-4acd-b3bf-1065f8d4cde5 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 872066a1-4cfb-4f69-ab14-2de00fe8a82e {http,https} \N \N {/s38-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3979c902-cefe-431c-8d25-ef04e4d9f5af 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 056302e1-150a-416c-9a4f-a9fb03f3f651 {http,https} \N \N {/s39-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f471bd0a-b25e-424a-9695-1405e5d20c41 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 056302e1-150a-416c-9a4f-a9fb03f3f651 {http,https} \N \N {/s39-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+34a424fa-a31c-485f-bff7-dcee457a0d84 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 056302e1-150a-416c-9a4f-a9fb03f3f651 {http,https} \N \N {/s39-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b95badc7-c614-45dd-a4fb-a4c7d1cbd55f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 056302e1-150a-416c-9a4f-a9fb03f3f651 {http,https} \N \N {/s39-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cddf1649-bd6d-4f46-a919-fc1d75fa1803 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 73734495-785d-42d2-a755-0ad0b1acf933 {http,https} \N \N {/s40-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d223be5-215e-471d-a7dd-e676028641e1 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 73734495-785d-42d2-a755-0ad0b1acf933 {http,https} \N \N {/s40-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e7cd42c1-60a7-4b64-b4c0-299c5e38ddb2 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 73734495-785d-42d2-a755-0ad0b1acf933 {http,https} \N \N {/s40-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+15903791-92c7-477e-9dfe-958d1b8d399c 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 73734495-785d-42d2-a755-0ad0b1acf933 {http,https} \N \N {/s40-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4a3b7d60-35a8-4506-81c3-d8af5f3affe0 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 8e691f37-eb65-4e3b-a6e2-0525412a98ab {http,https} \N \N {/s41-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a190876b-7347-4b29-ab3e-db75a67ea0dd 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 8e691f37-eb65-4e3b-a6e2-0525412a98ab {http,https} \N \N {/s41-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b4e7ca47-5c19-4159-a68a-d6b27824aa5c 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 8e691f37-eb65-4e3b-a6e2-0525412a98ab {http,https} \N \N {/s41-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+511e20f8-840a-4582-ab55-5100cc7d8b24 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 8e691f37-eb65-4e3b-a6e2-0525412a98ab {http,https} \N \N {/s41-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6b541eaa-46c7-4b88-af15-530ef074519f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 569a3987-9516-4053-92b8-aeebdaeeed5d {http,https} \N \N {/s42-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b6ea121e-a797-4fb0-a5a6-0b267cde8e7e 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 569a3987-9516-4053-92b8-aeebdaeeed5d {http,https} \N \N {/s42-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+46835c0e-edcf-4bbf-b2df-5c326648842e 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 569a3987-9516-4053-92b8-aeebdaeeed5d {http,https} \N \N {/s42-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c731e6b0-4082-497c-84c7-8addde5129c0 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 569a3987-9516-4053-92b8-aeebdaeeed5d {http,https} \N \N {/s42-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5dd725b7-e282-4acb-9357-630cea81d641 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5839b3b1-f03a-41f9-b645-a35ff680acbe {http,https} \N \N {/s43-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6dff752b-6cac-421f-81d7-9187e689e979 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5839b3b1-f03a-41f9-b645-a35ff680acbe {http,https} \N \N {/s43-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cf09ded9-12ff-4ac6-a857-70cfd18139ac 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5839b3b1-f03a-41f9-b645-a35ff680acbe {http,https} \N \N {/s43-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+23de1a99-33ae-4e01-af78-d8553c211005 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 5839b3b1-f03a-41f9-b645-a35ff680acbe {http,https} \N \N {/s43-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+40a92416-c7e0-4500-a12d-090403c50837 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 649cf33b-3d04-46f8-b849-4bfa449c8a7f {http,https} \N \N {/s44-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6984d1b3-bd9e-4bed-9307-93aa2794dfe7 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 649cf33b-3d04-46f8-b849-4bfa449c8a7f {http,https} \N \N {/s44-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a3935865-cf8a-4758-be41-cb2963bd3dab 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 649cf33b-3d04-46f8-b849-4bfa449c8a7f {http,https} \N \N {/s44-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9c4be6b1-c4b5-45c9-bbe9-48ed6875bd7e 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 649cf33b-3d04-46f8-b849-4bfa449c8a7f {http,https} \N \N {/s44-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d9d03644-bf13-4438-a41d-35a63f2e8bf7 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 3282f133-b8eb-4e46-80c6-a217df510860 {http,https} \N \N {/s45-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1c502e0f-3da4-4a8c-9a7d-d2574f678d00 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 3282f133-b8eb-4e46-80c6-a217df510860 {http,https} \N \N {/s45-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc87abf2-0fae-44af-baac-56ff20817de5 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 3282f133-b8eb-4e46-80c6-a217df510860 {http,https} \N \N {/s45-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cf377ce3-5d7f-407f-8c7a-b3d94c22dbfb 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 3282f133-b8eb-4e46-80c6-a217df510860 {http,https} \N \N {/s45-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ad56bb2d-fb37-4039-83fc-95bff293db97 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N da88cad4-bd4b-4a9d-b81d-d1445bf108a8 {http,https} \N \N {/s46-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+65c63fb9-3f19-4b14-959e-dc7421392fa9 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N da88cad4-bd4b-4a9d-b81d-d1445bf108a8 {http,https} \N \N {/s46-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+53b43ee6-cce0-4896-a8fa-ca1b771e6ebc 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N da88cad4-bd4b-4a9d-b81d-d1445bf108a8 {http,https} \N \N {/s46-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a3a2036-5aad-4b52-b99b-13a907f4e3d0 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N da88cad4-bd4b-4a9d-b81d-d1445bf108a8 {http,https} \N \N {/s46-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+442a6ef8-96b9-4a6e-ad0e-cb2bc887b9ce 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 365b2abb-1347-4077-8ffc-5b21984fca7f {http,https} \N \N {/s47-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5b3dfeb3-5e99-444e-9455-c99017106217 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 365b2abb-1347-4077-8ffc-5b21984fca7f {http,https} \N \N {/s47-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24191388-c07b-46a5-97f4-462b05d572f1 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 365b2abb-1347-4077-8ffc-5b21984fca7f {http,https} \N \N {/s47-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+33b863b6-748d-45c7-bc56-eb7ba0280591 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 365b2abb-1347-4077-8ffc-5b21984fca7f {http,https} \N \N {/s47-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3184fc79-27b0-4901-ad2e-77bd91729e5a 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e3cc7fa5-1919-4753-9afe-6f30f67a2c2e {http,https} \N \N {/s48-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb659e64-71e6-4014-a0b1-56d8eda12c1d 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e3cc7fa5-1919-4753-9afe-6f30f67a2c2e {http,https} \N \N {/s48-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+646a364a-116d-4c74-8e29-ff6c5c41f90f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e3cc7fa5-1919-4753-9afe-6f30f67a2c2e {http,https} \N \N {/s48-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d2cd486d-22b6-414c-af0a-4da9a0e89f63 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N e3cc7fa5-1919-4753-9afe-6f30f67a2c2e {http,https} \N \N {/s48-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0c5fa868-2707-4129-8ca1-fcea55c4624f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N fb53dd51-d113-4650-b980-e761871f3c54 {http,https} \N \N {/s49-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3a14b1a-113f-4ab0-bf91-a04f5a7054ad 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N fb53dd51-d113-4650-b980-e761871f3c54 {http,https} \N \N {/s49-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eaeae98e-0703-4e17-b196-93c7e54c45bf 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N fb53dd51-d113-4650-b980-e761871f3c54 {http,https} \N \N {/s49-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+51656ed3-fb8d-4b13-a52c-6a747b3b24ef 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N fb53dd51-d113-4650-b980-e761871f3c54 {http,https} \N \N {/s49-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+36dfcf70-1fa3-46b9-ace7-ee6bb5596f7f 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 851cd368-f1ea-4584-8cec-9a430f9b1a3f {http,https} \N \N {/s50-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+db915c87-9f9c-4e3a-b73c-ae571cac51df 2022-05-26 09:04:21+00 2022-05-26 09:04:21+00 \N 851cd368-f1ea-4584-8cec-9a430f9b1a3f {http,https} \N \N {/s50-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+01b2ab0c-a726-4eb2-a8f3-6f4376c1314d 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 851cd368-f1ea-4584-8cec-9a430f9b1a3f {http,https} \N \N {/s50-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+edfb8669-a2f3-432a-ac49-5f915354e433 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 851cd368-f1ea-4584-8cec-9a430f9b1a3f {http,https} \N \N {/s50-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+021e497a-9bf2-4a80-b546-5ccf4b6ff871 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4658664d-4ff6-4ab7-a9bf-8c0492c974de {http,https} \N \N {/s51-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1708116c-89af-4091-a713-3c53b20bb94f 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4658664d-4ff6-4ab7-a9bf-8c0492c974de {http,https} \N \N {/s51-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28e90609-b10b-48e5-b77d-1901c1411da2 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4658664d-4ff6-4ab7-a9bf-8c0492c974de {http,https} \N \N {/s51-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8bcc63d1-46f4-403f-a4d3-4feac7234799 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4658664d-4ff6-4ab7-a9bf-8c0492c974de {http,https} \N \N {/s51-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7b24dde5-5680-4a18-8361-5bc9e1ebbb5e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4d48bf3c-a575-4520-8817-34f0b84dd4b6 {http,https} \N \N {/s52-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c39d03a-3219-4021-a234-bdb1f66558ad 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4d48bf3c-a575-4520-8817-34f0b84dd4b6 {http,https} \N \N {/s52-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b62f7012-e2d6-4893-b73b-a37f17b20923 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4d48bf3c-a575-4520-8817-34f0b84dd4b6 {http,https} \N \N {/s52-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+985a6882-24fc-4c28-a994-ccd0f4853ccf 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4d48bf3c-a575-4520-8817-34f0b84dd4b6 {http,https} \N \N {/s52-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+26f47d54-501c-481e-a057-a655a0f366f4 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 26968e02-8bda-4c4e-818c-8ed35d44fd9c {http,https} \N \N {/s53-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0bc4ebbb-8ab9-4768-bbdd-fe078632137c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 26968e02-8bda-4c4e-818c-8ed35d44fd9c {http,https} \N \N {/s53-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5ddadc08-5c3a-4a33-a6cc-5654dd91ab0d 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 26968e02-8bda-4c4e-818c-8ed35d44fd9c {http,https} \N \N {/s53-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ba1023c3-197c-4c5c-8644-abf21c3d4523 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 26968e02-8bda-4c4e-818c-8ed35d44fd9c {http,https} \N \N {/s53-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0961a24a-4db4-4412-94ae-c662a37bf3d3 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 27f10e41-7155-4eed-bdfa-783271fc8bae {http,https} \N \N {/s54-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8043bb3f-229b-4927-a9da-e7c26e3cd2f5 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 27f10e41-7155-4eed-bdfa-783271fc8bae {http,https} \N \N {/s54-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63e6a3c0-903b-409d-9a21-0bf86dc8798f 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 27f10e41-7155-4eed-bdfa-783271fc8bae {http,https} \N \N {/s54-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c5cdae80-c83c-4e4b-bd99-ee15ac759b87 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 27f10e41-7155-4eed-bdfa-783271fc8bae {http,https} \N \N {/s54-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f73330a-ac60-405e-b592-ce04a111a79b 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 73bc0430-7355-4c6d-a974-74f5bf707db1 {http,https} \N \N {/s55-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f88f2b6c-f27e-4872-87ba-55c683e4f1b4 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 73bc0430-7355-4c6d-a974-74f5bf707db1 {http,https} \N \N {/s55-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d6ec02df-ecaf-4ef5-b4db-b5462bc57ea3 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 73bc0430-7355-4c6d-a974-74f5bf707db1 {http,https} \N \N {/s55-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c06adfe-4399-4ceb-bc58-b6e7f3412051 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 73bc0430-7355-4c6d-a974-74f5bf707db1 {http,https} \N \N {/s55-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5814489d-419d-4f0b-978b-80fc6e715371 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N ef27392a-1fb8-4611-8757-c42b55900756 {http,https} \N \N {/s56-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bb2c3144-6f34-443b-ae1b-c407bcc86573 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N ef27392a-1fb8-4611-8757-c42b55900756 {http,https} \N \N {/s56-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0f5869b0-2a4f-4b94-ac24-8860a9aba9d8 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N ef27392a-1fb8-4611-8757-c42b55900756 {http,https} \N \N {/s56-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c7e117bd-61eb-49a7-b27b-31bd5efa75f8 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N ef27392a-1fb8-4611-8757-c42b55900756 {http,https} \N \N {/s56-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7941c45b-73eb-4ff1-973c-811cf918b567 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N b45da34e-3338-4878-a3e5-d78df8cd22e7 {http,https} \N \N {/s57-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b81652aa-9c7a-4ead-901a-de9abbf03ca7 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N b45da34e-3338-4878-a3e5-d78df8cd22e7 {http,https} \N \N {/s57-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5e402e76-f7d2-42b2-9396-f222fb4e468b 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N b45da34e-3338-4878-a3e5-d78df8cd22e7 {http,https} \N \N {/s57-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c3aba8bd-a9c8-4b8c-b818-cd460c1dbda1 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N b45da34e-3338-4878-a3e5-d78df8cd22e7 {http,https} \N \N {/s57-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3403033f-1ec4-4784-894a-1040e85dddeb 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dc5da515-f616-40e9-9b94-d699fded3db7 {http,https} \N \N {/s58-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+12c929a4-0d97-451e-b9b7-0e86173ecf24 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dc5da515-f616-40e9-9b94-d699fded3db7 {http,https} \N \N {/s58-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d1a9cfb9-68bf-4234-9ef7-878d8b0bc3d0 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dc5da515-f616-40e9-9b94-d699fded3db7 {http,https} \N \N {/s58-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+666c6b7c-ba43-4ae5-a38d-42ebd968f901 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dc5da515-f616-40e9-9b94-d699fded3db7 {http,https} \N \N {/s58-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b8bfeae5-5130-4cc9-9a2f-246a16e53328 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 8168f4cc-39af-49bd-8b6e-a365f038bebd {http,https} \N \N {/s59-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a793732a-905e-4b4e-96b5-6c849c03423d 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 8168f4cc-39af-49bd-8b6e-a365f038bebd {http,https} \N \N {/s59-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b26ed3d4-5587-42ae-a6da-6123669164b4 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 8168f4cc-39af-49bd-8b6e-a365f038bebd {http,https} \N \N {/s59-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ec7d7a95-e5b7-42c8-8a0c-a933b5089804 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 8168f4cc-39af-49bd-8b6e-a365f038bebd {http,https} \N \N {/s59-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1c4b40eb-d910-4109-838b-d5a145b6005a 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 051898cd-71d2-457b-9ee8-c080908da498 {http,https} \N \N {/s60-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+01e02128-b620-49cf-bd2b-6ffca9f28c4c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 051898cd-71d2-457b-9ee8-c080908da498 {http,https} \N \N {/s60-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+62b48699-f419-4d31-9009-709cd966abcb 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 051898cd-71d2-457b-9ee8-c080908da498 {http,https} \N \N {/s60-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ddcffccb-96cd-4cc0-81b1-b1f1cdf09b58 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 051898cd-71d2-457b-9ee8-c080908da498 {http,https} \N \N {/s60-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be4c0681-1850-4750-b276-11f6c6ce83de 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cdb3688d-b5fc-421a-8c06-cb14fc6c5ff9 {http,https} \N \N {/s61-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+760b1b0a-a6d7-4138-bbe7-2da72748aaec 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cdb3688d-b5fc-421a-8c06-cb14fc6c5ff9 {http,https} \N \N {/s61-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a19f8cd4-458d-40ff-8919-80b80902fea6 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cdb3688d-b5fc-421a-8c06-cb14fc6c5ff9 {http,https} \N \N {/s61-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e8902d3c-6219-4029-adf8-fafb7e91ac2e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cdb3688d-b5fc-421a-8c06-cb14fc6c5ff9 {http,https} \N \N {/s61-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3f71841f-89f3-4fc7-bf7c-70c5c24e64f1 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cae8aca9-818b-450d-97a6-7ea08373e0cc {http,https} \N \N {/s62-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+26ce1726-fee5-4e7f-ace9-9b506a612843 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cae8aca9-818b-450d-97a6-7ea08373e0cc {http,https} \N \N {/s62-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+04d8e2e7-7e64-46d2-9fc8-8eb40f50feed 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cae8aca9-818b-450d-97a6-7ea08373e0cc {http,https} \N \N {/s62-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5fa7a59b-63dd-427d-a314-eb97ba59889c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N cae8aca9-818b-450d-97a6-7ea08373e0cc {http,https} \N \N {/s62-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+30f175e5-eb1e-48f2-a455-58d556b1c49d 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1b7c0f6a-9eab-428e-b979-5995a4ff6527 {http,https} \N \N {/s63-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+67909e1e-e8d3-494b-88a6-42dddb9cc70c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1b7c0f6a-9eab-428e-b979-5995a4ff6527 {http,https} \N \N {/s63-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+567df721-b470-4340-aaa7-45c6d4d8443a 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1b7c0f6a-9eab-428e-b979-5995a4ff6527 {http,https} \N \N {/s63-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0e7103e2-9878-405a-99c6-896c1fda9308 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1b7c0f6a-9eab-428e-b979-5995a4ff6527 {http,https} \N \N {/s63-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d0b57e6c-7080-4a2c-be92-b343f35b76c1 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 3e658a76-cb76-4be7-a15a-84d4883b472b {http,https} \N \N {/s64-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b0dedf00-dc34-4996-87d2-4c3dfc5c46d2 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 3e658a76-cb76-4be7-a15a-84d4883b472b {http,https} \N \N {/s64-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e5226a35-9d37-4e3d-a79c-e9f4b3014371 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 3e658a76-cb76-4be7-a15a-84d4883b472b {http,https} \N \N {/s64-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0e9a00d-e797-4a8c-a773-9567ef0487c7 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 3e658a76-cb76-4be7-a15a-84d4883b472b {http,https} \N \N {/s64-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6348b289-ccd1-40e7-83ee-9717654a861f 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 800121b2-3644-4ea0-8539-25d513acb472 {http,https} \N \N {/s65-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2b3c8d08-5826-40c8-bf4b-c9cd09627efe 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 800121b2-3644-4ea0-8539-25d513acb472 {http,https} \N \N {/s65-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+92f02e92-a089-490e-b8af-41a788a459a4 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 800121b2-3644-4ea0-8539-25d513acb472 {http,https} \N \N {/s65-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0c9f6955-7cbd-4bda-8738-4ee18fce587f 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 800121b2-3644-4ea0-8539-25d513acb472 {http,https} \N \N {/s65-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f4e93c81-d3b5-4007-9775-157c8c8c61ae 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 89b2af01-b55f-4425-844e-bc2dea397b93 {http,https} \N \N {/s66-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+12cfa8af-ef07-4bd0-aec4-6c17e9563fb1 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 89b2af01-b55f-4425-844e-bc2dea397b93 {http,https} \N \N {/s66-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+103a4113-2570-401a-9bff-456c18a6c41c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 89b2af01-b55f-4425-844e-bc2dea397b93 {http,https} \N \N {/s66-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d85f3777-3b23-45ac-9458-6533790f4813 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 89b2af01-b55f-4425-844e-bc2dea397b93 {http,https} \N \N {/s66-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3d6bc425-8bba-4a27-ad92-7f4676b167a5 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 34f521cb-53b9-4824-89b7-15459e96532f {http,https} \N \N {/s67-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+57b695be-5b45-4e9d-b96c-f82dee5c06ab 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 34f521cb-53b9-4824-89b7-15459e96532f {http,https} \N \N {/s67-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bb952eb2-a5e3-465a-837a-06908d777bef 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 34f521cb-53b9-4824-89b7-15459e96532f {http,https} \N \N {/s67-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+08636446-4863-4615-93a2-d88336303d9a 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 34f521cb-53b9-4824-89b7-15459e96532f {http,https} \N \N {/s67-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4ba55de6-96af-4854-8eea-af4f7eae005f 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 33a92a68-5e8d-487b-977e-89dd42a458bd {http,https} \N \N {/s68-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+638b369e-b27e-4be6-b139-8f747422453e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 33a92a68-5e8d-487b-977e-89dd42a458bd {http,https} \N \N {/s68-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6211773e-191e-43a2-b114-8de79c70d841 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 33a92a68-5e8d-487b-977e-89dd42a458bd {http,https} \N \N {/s68-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dee01448-e99a-4990-8f07-f187483c4a3c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 33a92a68-5e8d-487b-977e-89dd42a458bd {http,https} \N \N {/s68-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9e6312a9-762e-4442-82dd-404e5d0b1e24 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dbbe71cb-7ec1-4c43-804d-ef6a92721d90 {http,https} \N \N {/s69-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+793889bb-ad6d-45c5-ab09-d6170885350e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dbbe71cb-7ec1-4c43-804d-ef6a92721d90 {http,https} \N \N {/s69-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+792e6099-3c47-4d19-b97e-b7f1ad14b6b3 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dbbe71cb-7ec1-4c43-804d-ef6a92721d90 {http,https} \N \N {/s69-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+df9f4f76-306c-4243-843a-ce697957d909 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N dbbe71cb-7ec1-4c43-804d-ef6a92721d90 {http,https} \N \N {/s69-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c7379f6d-1aea-4c1e-9347-d0b3c4ac1a09 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 69a88ba4-e530-4723-b7c3-f739b92a5a66 {http,https} \N \N {/s70-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0473cdf4-8dd1-43cf-bb0e-24dd9133496b 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 69a88ba4-e530-4723-b7c3-f739b92a5a66 {http,https} \N \N {/s70-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+17e4085d-52ce-4825-98fd-63c6e389ef2a 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 69a88ba4-e530-4723-b7c3-f739b92a5a66 {http,https} \N \N {/s70-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+50ee2ef5-0eb9-449f-873a-3ffe3ca64478 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 69a88ba4-e530-4723-b7c3-f739b92a5a66 {http,https} \N \N {/s70-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+339e65d3-f2e4-4d6c-883f-089eb773b0b9 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 0d1eb445-8a10-49bb-952f-5eb35a8599d3 {http,https} \N \N {/s71-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b49dea8c-55fa-422f-bca3-aa3c93116e0b 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 0d1eb445-8a10-49bb-952f-5eb35a8599d3 {http,https} \N \N {/s71-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0e369db3-ea50-4d1f-b0a2-ed9209ccfc91 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 0d1eb445-8a10-49bb-952f-5eb35a8599d3 {http,https} \N \N {/s71-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9f5026b1-a5c7-47d8-b275-a777abdd13da 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 0d1eb445-8a10-49bb-952f-5eb35a8599d3 {http,https} \N \N {/s71-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+70cac125-433d-4ef7-8d95-d285cf4e0370 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a03dac5a-20dc-492d-b4db-732a79d4a30c {http,https} \N \N {/s72-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d84502db-755f-4301-9943-d140abfc00be 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a03dac5a-20dc-492d-b4db-732a79d4a30c {http,https} \N \N {/s72-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e08338f6-0985-495a-9f94-c05923658a7a 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a03dac5a-20dc-492d-b4db-732a79d4a30c {http,https} \N \N {/s72-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+abeb4a51-d15c-4f76-ab81-c66e67871626 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a03dac5a-20dc-492d-b4db-732a79d4a30c {http,https} \N \N {/s72-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+647e2caf-3b5c-46ab-85e8-a38cdd67a25b 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 291a0424-2ad1-47a6-a8b2-c63a037bf03c {http,https} \N \N {/s73-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+558e54d5-0c54-4fcf-84ee-da97751c4e48 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 291a0424-2ad1-47a6-a8b2-c63a037bf03c {http,https} \N \N {/s73-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3e2c67c4-03d2-49a3-b888-cb185c1fa600 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 291a0424-2ad1-47a6-a8b2-c63a037bf03c {http,https} \N \N {/s73-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2ea5cb4d-5e42-4d2f-84cd-abe9854e4697 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 291a0424-2ad1-47a6-a8b2-c63a037bf03c {http,https} \N \N {/s73-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4996e322-c97f-4aec-b788-c11ccaf9efd8 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4eb8a749-0bd2-47af-8fdc-4cf128bf0b66 {http,https} \N \N {/s74-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+81de2981-e03e-43ee-aed3-a244f12bee7c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4eb8a749-0bd2-47af-8fdc-4cf128bf0b66 {http,https} \N \N {/s74-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+019cf0ee-2cdb-4d65-8263-1a1f9c3c5f6e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4eb8a749-0bd2-47af-8fdc-4cf128bf0b66 {http,https} \N \N {/s74-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24ac0cea-3fe9-4873-b9a6-e050eff27d82 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 4eb8a749-0bd2-47af-8fdc-4cf128bf0b66 {http,https} \N \N {/s74-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4c80aa43-3d2b-46e7-9f26-0f56e776b06c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c398e6e1-2f3e-4897-912f-483c03ec6959 {http,https} \N \N {/s75-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1a8c8d53-ce1e-4b4b-9eeb-acacb1c5d70e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c398e6e1-2f3e-4897-912f-483c03ec6959 {http,https} \N \N {/s75-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29681c3f-0f05-4c3d-8f3f-2230f797811d 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c398e6e1-2f3e-4897-912f-483c03ec6959 {http,https} \N \N {/s75-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4245e97f-22dc-40d2-b922-780fd073f3ec 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c398e6e1-2f3e-4897-912f-483c03ec6959 {http,https} \N \N {/s75-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+757a1bfc-a735-4d45-9a50-7112f969ea15 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c544969b-0b53-43a7-a6a9-79e400d7b852 {http,https} \N \N {/s76-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f7d2f30-ad6f-4eb0-940a-b6d2f0c8877c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c544969b-0b53-43a7-a6a9-79e400d7b852 {http,https} \N \N {/s76-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e0ca802f-c54b-4a69-895b-9d5ddd1bf25c 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c544969b-0b53-43a7-a6a9-79e400d7b852 {http,https} \N \N {/s76-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ca7ec55c-2cb6-4689-bac0-c3c3f46abe9e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N c544969b-0b53-43a7-a6a9-79e400d7b852 {http,https} \N \N {/s76-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+07d18ff5-7c3a-43cf-8e73-0b61cdd9a867 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1dc10ac4-8720-49d0-9624-e2320ad83910 {http,https} \N \N {/s77-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b365a387-d043-4178-81fc-b30f32f082b6 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1dc10ac4-8720-49d0-9624-e2320ad83910 {http,https} \N \N {/s77-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3d56746a-4238-456d-9064-056d21decf91 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1dc10ac4-8720-49d0-9624-e2320ad83910 {http,https} \N \N {/s77-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+891dc0c9-4193-4952-87d8-ea6056b2ba88 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 1dc10ac4-8720-49d0-9624-e2320ad83910 {http,https} \N \N {/s77-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cbc1d656-4bfa-40bd-b40f-ef2b5af4d4f0 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 961eda07-6db4-41a9-b053-55f3d86feab9 {http,https} \N \N {/s78-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc2f8ad7-55e2-4ccb-9ec2-0dc5d8619482 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 961eda07-6db4-41a9-b053-55f3d86feab9 {http,https} \N \N {/s78-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7b040585-87c8-4559-883e-2c316faf3c65 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 961eda07-6db4-41a9-b053-55f3d86feab9 {http,https} \N \N {/s78-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2c30a266-bcae-43a2-9541-a291224a7049 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N 961eda07-6db4-41a9-b053-55f3d86feab9 {http,https} \N \N {/s78-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3b01e0e4-a2d4-49cf-910b-415c20e7f3cf 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a92dc0e0-3cd3-4c00-bfbd-1b9d849c617b {http,https} \N \N {/s79-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c5054caa-c60c-436a-a041-0be366e8d272 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a92dc0e0-3cd3-4c00-bfbd-1b9d849c617b {http,https} \N \N {/s79-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1419869c-88ee-495a-ba0f-379b5e0e9984 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a92dc0e0-3cd3-4c00-bfbd-1b9d849c617b {http,https} \N \N {/s79-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a4909080-0e69-4f7d-8d50-de3bfefae69e 2022-05-26 09:04:22+00 2022-05-26 09:04:22+00 \N a92dc0e0-3cd3-4c00-bfbd-1b9d849c617b {http,https} \N \N {/s79-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f5db0a03-9630-45ea-9996-e65fcf6d0b8a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6fc0c8de-dd47-4b2d-be48-acff77604738 {http,https} \N \N {/s80-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4a9d3ff9-c671-48e8-bfaf-28cc9bb82f7b 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6fc0c8de-dd47-4b2d-be48-acff77604738 {http,https} \N \N {/s80-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5b38a474-491d-471f-ba11-1b54ad9f1637 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6fc0c8de-dd47-4b2d-be48-acff77604738 {http,https} \N \N {/s80-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9ff12282-1ec8-49b2-b35f-426406bae7bc 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6fc0c8de-dd47-4b2d-be48-acff77604738 {http,https} \N \N {/s80-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8677f5a4-f5b3-4893-a2c2-5ce9bd4626dd 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c1477ea4-988e-40e5-b7a8-6fa4e688f36d {http,https} \N \N {/s81-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9ae59152-7021-4460-b166-ce819c7a078b 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c1477ea4-988e-40e5-b7a8-6fa4e688f36d {http,https} \N \N {/s81-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eb751574-5953-4b2b-8ff2-b946d3366caf 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c1477ea4-988e-40e5-b7a8-6fa4e688f36d {http,https} \N \N {/s81-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f781fee0-5d8d-485d-a425-49670bf46d9a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c1477ea4-988e-40e5-b7a8-6fa4e688f36d {http,https} \N \N {/s81-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0dce98c9-dffc-4657-bc2a-1ae1033dd2a7 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c0ac16b4-51b2-4388-a75c-99a6e8864567 {http,https} \N \N {/s82-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e6684904-4bee-472b-a960-9719d4fb3d09 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c0ac16b4-51b2-4388-a75c-99a6e8864567 {http,https} \N \N {/s82-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a21e5c1c-7b7a-40c7-a706-cfe47049969a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c0ac16b4-51b2-4388-a75c-99a6e8864567 {http,https} \N \N {/s82-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+36fea073-81cd-4283-956d-128f55a83899 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N c0ac16b4-51b2-4388-a75c-99a6e8864567 {http,https} \N \N {/s82-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+45f33f4c-8fa7-48f0-a831-b368bc51d06a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N b3490c56-2668-4cf8-ac26-9d3c38fb9ce6 {http,https} \N \N {/s83-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4b17145e-d390-400b-b142-7b8fe0682b5f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N b3490c56-2668-4cf8-ac26-9d3c38fb9ce6 {http,https} \N \N {/s83-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+defa59d1-6f2f-436d-a5c8-9cf13c193334 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N b3490c56-2668-4cf8-ac26-9d3c38fb9ce6 {http,https} \N \N {/s83-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e2f71888-ac65-4716-95cb-6c1999dacbae 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N b3490c56-2668-4cf8-ac26-9d3c38fb9ce6 {http,https} \N \N {/s83-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e28cbd79-6bf0-466a-8754-e6fc1ca61124 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6f607e1a-2baf-4f12-b0ed-270073df30c6 {http,https} \N \N {/s84-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+242ba16c-e255-499c-9908-7cf006340140 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6f607e1a-2baf-4f12-b0ed-270073df30c6 {http,https} \N \N {/s84-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29284033-0e0a-43c6-b82a-5446f0447cb7 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6f607e1a-2baf-4f12-b0ed-270073df30c6 {http,https} \N \N {/s84-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+62f01079-9db2-4e4a-ab3d-6235d0900e23 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 6f607e1a-2baf-4f12-b0ed-270073df30c6 {http,https} \N \N {/s84-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e87efb35-04cb-44e6-9bb3-30e76b5ec298 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4284966e-2ef5-45f7-b16c-faba6666c300 {http,https} \N \N {/s85-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+12a70bf9-d5d8-4402-8d22-b97d3fe6c8a4 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4284966e-2ef5-45f7-b16c-faba6666c300 {http,https} \N \N {/s85-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2594018c-1d96-4af3-af45-7eebc8d06515 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4284966e-2ef5-45f7-b16c-faba6666c300 {http,https} \N \N {/s85-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c7c39170-549b-4182-8ae6-13b8e73be911 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4284966e-2ef5-45f7-b16c-faba6666c300 {http,https} \N \N {/s85-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fc596999-1fc0-4a7b-a61b-14506c15e12d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0a3d005f-e8ae-46a0-bc92-0a4a8147fe3f {http,https} \N \N {/s86-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b5a95da1-841f-4653-b0de-9a405b6a5b99 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0a3d005f-e8ae-46a0-bc92-0a4a8147fe3f {http,https} \N \N {/s86-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3af242f4-3b4a-4cc8-8e49-fabcdd6d20d7 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0a3d005f-e8ae-46a0-bc92-0a4a8147fe3f {http,https} \N \N {/s86-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f808cfc-6eb5-4841-82bc-cb9945bab516 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0a3d005f-e8ae-46a0-bc92-0a4a8147fe3f {http,https} \N \N {/s86-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+35a595cc-d05e-4e4d-83b4-660e91cf6907 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f7039445-e8fa-44c0-ba30-4db609972643 {http,https} \N \N {/s87-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb93afbe-d5bc-4fae-995c-8b05e05f4a68 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f7039445-e8fa-44c0-ba30-4db609972643 {http,https} \N \N {/s87-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d8bbc254-7ec6-40fd-a93a-ad34a5c1b99d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f7039445-e8fa-44c0-ba30-4db609972643 {http,https} \N \N {/s87-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a6c4abac-9a5b-49e8-aa13-ca82f95de345 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f7039445-e8fa-44c0-ba30-4db609972643 {http,https} \N \N {/s87-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b3435e36-b1b8-4d10-be89-fc955bb56a12 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 10db8481-4fa8-4531-9e0c-fb20e642dc40 {http,https} \N \N {/s88-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+49e68f0e-8bb0-42e9-8e7a-a2e05821ff07 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 10db8481-4fa8-4531-9e0c-fb20e642dc40 {http,https} \N \N {/s88-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5d706489-1d36-4c5a-b451-1672965ae52d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 10db8481-4fa8-4531-9e0c-fb20e642dc40 {http,https} \N \N {/s88-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+986f5e98-8421-4e69-9045-88bdc41a6d09 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 10db8481-4fa8-4531-9e0c-fb20e642dc40 {http,https} \N \N {/s88-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0297b90-367a-4b03-b9ff-6d215458cbf4 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0069a9d9-459a-4efc-b5a2-c0ae786c92bd {http,https} \N \N {/s89-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2af7a506-b909-4ec1-868a-3f8b117483b1 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0069a9d9-459a-4efc-b5a2-c0ae786c92bd {http,https} \N \N {/s89-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63f3ce37-3f36-4b9b-8b81-e1ddb433539b 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0069a9d9-459a-4efc-b5a2-c0ae786c92bd {http,https} \N \N {/s89-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d22ddd42-4591-46d0-bddf-46fad1561fd7 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0069a9d9-459a-4efc-b5a2-c0ae786c92bd {http,https} \N \N {/s89-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+35d3cc52-4107-458f-ad8e-aee80dd3483e 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fa73881d-a74d-4349-8a9c-b2ae17b414fd {http,https} \N \N {/s90-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+678a2a21-fb5c-4b53-b9a3-5acc590e5e93 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fa73881d-a74d-4349-8a9c-b2ae17b414fd {http,https} \N \N {/s90-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+44162869-6884-47bc-9476-98c8c38ad9bf 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fa73881d-a74d-4349-8a9c-b2ae17b414fd {http,https} \N \N {/s90-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+716749cf-4ca9-4298-a603-7605970c733e 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fa73881d-a74d-4349-8a9c-b2ae17b414fd {http,https} \N \N {/s90-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4d75c19a-37a4-4664-b98d-2b7a81de89c6 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fea825b5-53e7-4d5e-b594-5e6d20822e27 {http,https} \N \N {/s91-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c81cf78d-87d0-4977-8496-4824784c28b8 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fea825b5-53e7-4d5e-b594-5e6d20822e27 {http,https} \N \N {/s91-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6b1b5631-cf02-4220-b8a7-6aeea37cf89f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fea825b5-53e7-4d5e-b594-5e6d20822e27 {http,https} \N \N {/s91-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cd28b502-199d-4fd7-bd0e-e343844f83cd 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N fea825b5-53e7-4d5e-b594-5e6d20822e27 {http,https} \N \N {/s91-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9dad893e-6c1b-49f6-bab2-f0f4d23aeeb9 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0f9df5d5-3dd4-4a0b-beef-5aed37af31c6 {http,https} \N \N {/s92-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+858e8ea3-ab8d-448f-8336-845f97b77242 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0f9df5d5-3dd4-4a0b-beef-5aed37af31c6 {http,https} \N \N {/s92-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83f1d1a3-11ef-4a49-8467-1ae7769cae4f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0f9df5d5-3dd4-4a0b-beef-5aed37af31c6 {http,https} \N \N {/s92-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83b72d29-4fc2-4454-af94-b05add1f612a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 0f9df5d5-3dd4-4a0b-beef-5aed37af31c6 {http,https} \N \N {/s92-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5e01aa1d-e5de-4429-a49c-867ba6d43c34 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7d839f08-fe27-44a8-bbea-abaea85e8ec4 {http,https} \N \N {/s93-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eac2c744-d694-4e53-8321-1bf5d2711ef9 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7d839f08-fe27-44a8-bbea-abaea85e8ec4 {http,https} \N \N {/s93-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ff25f866-172d-4eb3-a780-0f7b74779572 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7d839f08-fe27-44a8-bbea-abaea85e8ec4 {http,https} \N \N {/s93-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+96f720ad-4305-4dfa-a03d-650aeee8651d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7d839f08-fe27-44a8-bbea-abaea85e8ec4 {http,https} \N \N {/s93-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c3e8a3ac-10f2-4de2-b9cf-681379e6373e 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4e27c8d3-1b57-4837-a62e-7b7129f23b87 {http,https} \N \N {/s94-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4685cd6e-0dba-4249-ae0e-9deefb9952c5 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4e27c8d3-1b57-4837-a62e-7b7129f23b87 {http,https} \N \N {/s94-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bbbaacf1-310a-4b13-986c-14dbff6320e8 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4e27c8d3-1b57-4837-a62e-7b7129f23b87 {http,https} \N \N {/s94-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8be9c5cd-0b29-4750-8529-109f179754f6 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 4e27c8d3-1b57-4837-a62e-7b7129f23b87 {http,https} \N \N {/s94-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28b4f591-df0d-498e-92b8-9b97fae801a3 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 187a1bbe-8750-47fd-a693-eb832b67106f {http,https} \N \N {/s95-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f375807e-3ab9-4972-beac-86b454d9f9a1 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 187a1bbe-8750-47fd-a693-eb832b67106f {http,https} \N \N {/s95-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+293dd5ba-72cb-4f04-8c0a-3757b6fbab6b 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 187a1bbe-8750-47fd-a693-eb832b67106f {http,https} \N \N {/s95-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+61c03edb-0caa-48b0-a52e-2a462393cee3 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 187a1bbe-8750-47fd-a693-eb832b67106f {http,https} \N \N {/s95-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0e70b696-b717-4a41-b399-8ca2ff308a9c 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 97cac022-7f9a-4eb7-a600-3f99cbdf8484 {http,https} \N \N {/s96-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d3082908-2a66-42c6-9631-e1c0951f7866 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 97cac022-7f9a-4eb7-a600-3f99cbdf8484 {http,https} \N \N {/s96-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+61c692c6-67dc-46e9-b910-856cd7bcda12 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 97cac022-7f9a-4eb7-a600-3f99cbdf8484 {http,https} \N \N {/s96-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c6c9e4ec-1a34-4fbd-8879-a19cb1d70325 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 97cac022-7f9a-4eb7-a600-3f99cbdf8484 {http,https} \N \N {/s96-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+00014ccf-4ca8-4755-b0d2-8b92dc71920d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f731ee23-32fc-428e-858c-2451542ef358 {http,https} \N \N {/s97-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eb580aa6-8121-4a18-bb67-7cfdecde4b6f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f731ee23-32fc-428e-858c-2451542ef358 {http,https} \N \N {/s97-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+215e806d-f5bb-431a-8497-6d144090476c 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f731ee23-32fc-428e-858c-2451542ef358 {http,https} \N \N {/s97-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+99afea6a-684b-497d-a342-465f77de19f2 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N f731ee23-32fc-428e-858c-2451542ef358 {http,https} \N \N {/s97-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f9643224-8206-4dea-bf38-c0774296262a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7cdc1f2b-844d-44af-80ee-9ee8ce30ec3a {http,https} \N \N {/s98-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2fdd828a-3fef-4df8-b800-040dbaa54e4e 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7cdc1f2b-844d-44af-80ee-9ee8ce30ec3a {http,https} \N \N {/s98-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+09ba47c5-29d7-4741-9aaa-66edacca5e2a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7cdc1f2b-844d-44af-80ee-9ee8ce30ec3a {http,https} \N \N {/s98-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb992552-77ac-435a-afc0-5bc7e26d0165 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 7cdc1f2b-844d-44af-80ee-9ee8ce30ec3a {http,https} \N \N {/s98-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f93a1cf0-2ad4-4df5-a229-5c98139904da 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 786c4ca2-f7e2-497f-afe9-04a7d389cffb {http,https} \N \N {/s99-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63f416fb-0ffb-47d2-a206-5cee31b34c1b 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 786c4ca2-f7e2-497f-afe9-04a7d389cffb {http,https} \N \N {/s99-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9dfa1071-ab2b-41ba-b753-9cbefef656fb 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 786c4ca2-f7e2-497f-afe9-04a7d389cffb {http,https} \N \N {/s99-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6747376a-7cb0-406e-9f40-7797e1125a97 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 786c4ca2-f7e2-497f-afe9-04a7d389cffb {http,https} \N \N {/s99-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a4127491-d785-45fa-b64a-784acbf2a89c 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 327348b0-de35-47ef-a46b-292bf1a2ce91 {http,https} \N \N {/s100-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d67b5cb2-b0b5-4d77-924b-63bd7584d396 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 327348b0-de35-47ef-a46b-292bf1a2ce91 {http,https} \N \N {/s100-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6924c386-e398-46e5-8190-6074c7c7c690 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 327348b0-de35-47ef-a46b-292bf1a2ce91 {http,https} \N \N {/s100-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+527f67de-81f0-481c-96bf-a1c18272204d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 327348b0-de35-47ef-a46b-292bf1a2ce91 {http,https} \N \N {/s100-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+89f8dc6d-5186-4a5e-8a1b-ab664092a901 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 42231a53-eac6-41d4-906f-96a6007efd5c {http,https} \N \N {/s101-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5e1cf5ab-5814-4ba0-953d-e65c50359cc2 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 42231a53-eac6-41d4-906f-96a6007efd5c {http,https} \N \N {/s101-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+56c19a33-1a73-4938-a1cb-744cf850d87f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 42231a53-eac6-41d4-906f-96a6007efd5c {http,https} \N \N {/s101-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28cf63f8-14cc-4a5b-9075-d501074d9c0c 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 42231a53-eac6-41d4-906f-96a6007efd5c {http,https} \N \N {/s101-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+66247a44-9020-47eb-82ad-6c7a27a3b875 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2e5dce8d-7e56-4037-a53f-5363e78cfb67 {http,https} \N \N {/s102-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d7590ffa-8e4e-47c9-9cd0-b82b0245af60 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2e5dce8d-7e56-4037-a53f-5363e78cfb67 {http,https} \N \N {/s102-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0e9eebed-1078-4198-af13-1e4c61b53d85 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2e5dce8d-7e56-4037-a53f-5363e78cfb67 {http,https} \N \N {/s102-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ca7c895-8735-4846-af81-977f2e88e0c4 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2e5dce8d-7e56-4037-a53f-5363e78cfb67 {http,https} \N \N {/s102-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9ec2593f-35c3-4b02-a3e8-a76c2d11921f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 880c0dfc-3b35-4557-9f4f-20e450605453 {http,https} \N \N {/s103-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1271dbc2-9ae0-4586-b398-b13056fa66c9 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 880c0dfc-3b35-4557-9f4f-20e450605453 {http,https} \N \N {/s103-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e2d31a30-7159-48c9-8f2c-3550d00b4933 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 880c0dfc-3b35-4557-9f4f-20e450605453 {http,https} \N \N {/s103-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f7b5e9f4-70d7-40c2-9560-d0b942f078ab 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 880c0dfc-3b35-4557-9f4f-20e450605453 {http,https} \N \N {/s103-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+99cbb127-80e9-4413-b6d6-a3e2ca030a16 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2d1e40d6-8080-4cee-98b2-c64c3dfbeb70 {http,https} \N \N {/s104-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+57fa6077-4a63-4419-9f3d-8835aeee2b51 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2d1e40d6-8080-4cee-98b2-c64c3dfbeb70 {http,https} \N \N {/s104-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+843b3b55-37f7-4eaa-b3c2-16f82baf4eba 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2d1e40d6-8080-4cee-98b2-c64c3dfbeb70 {http,https} \N \N {/s104-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b56573dd-73d9-4fcf-b913-4cb34d99501f 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 2d1e40d6-8080-4cee-98b2-c64c3dfbeb70 {http,https} \N \N {/s104-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+99fa82d0-384b-49cb-a8a9-081ad2b78d96 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 92e0b48f-e57a-4b37-a150-ca88c81d14a3 {http,https} \N \N {/s105-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+da37c5ed-b9c5-4b50-ada0-f5bb20d979a0 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 92e0b48f-e57a-4b37-a150-ca88c81d14a3 {http,https} \N \N {/s105-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bf1f6c36-b4d2-4ee4-a30d-21b7e10fc921 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 92e0b48f-e57a-4b37-a150-ca88c81d14a3 {http,https} \N \N {/s105-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+71f366dd-fa90-4cca-8bb0-32a8044c1eae 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 92e0b48f-e57a-4b37-a150-ca88c81d14a3 {http,https} \N \N {/s105-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+96ea5adf-c1a8-4217-9831-ebef9e4bb447 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 837f896d-e596-4681-94af-74e1f8832cec {http,https} \N \N {/s106-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d51a47e0-df63-46dc-a58f-2a98da21fe1c 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 837f896d-e596-4681-94af-74e1f8832cec {http,https} \N \N {/s106-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2cf8e1a1-c838-45b3-8eba-73159a0e0718 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 837f896d-e596-4681-94af-74e1f8832cec {http,https} \N \N {/s106-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+092d64bd-9ad3-41c0-8aaf-a2259319ceeb 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 837f896d-e596-4681-94af-74e1f8832cec {http,https} \N \N {/s106-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+78e6a9d8-d4c6-442a-9a84-1f127076bb68 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N dfa8a1f7-4dba-4abe-b98d-11146dddf483 {http,https} \N \N {/s107-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+43beb0fa-c485-4296-b8cb-c8d135c6847a 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N dfa8a1f7-4dba-4abe-b98d-11146dddf483 {http,https} \N \N {/s107-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc74ff68-b16e-4ab5-b6d2-d8584c35d5be 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N dfa8a1f7-4dba-4abe-b98d-11146dddf483 {http,https} \N \N {/s107-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aa1981d7-2398-45a9-9215-26b5622c203d 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N dfa8a1f7-4dba-4abe-b98d-11146dddf483 {http,https} \N \N {/s107-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+645d75d2-fefb-4d51-a076-f4f56a705b14 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 87b83cd7-e97b-46e2-b8aa-cfc3f41df930 {http,https} \N \N {/s108-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+52afa8fe-7cd9-4f19-814f-f0a40ddffb48 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 87b83cd7-e97b-46e2-b8aa-cfc3f41df930 {http,https} \N \N {/s108-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+20613670-0d6c-4b52-bd82-29ab4700eda8 2022-05-26 09:04:23+00 2022-05-26 09:04:23+00 \N 87b83cd7-e97b-46e2-b8aa-cfc3f41df930 {http,https} \N \N {/s108-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fe336d75-96cc-4e8e-8923-a3f0952f7b5f 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 87b83cd7-e97b-46e2-b8aa-cfc3f41df930 {http,https} \N \N {/s108-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a4a47002-7ac0-4c25-b678-40db29d5ac21 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 090f6901-a7d3-42e6-94f4-69ff07632983 {http,https} \N \N {/s109-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+da5138ea-c2ed-47fb-9f59-b6f814700b6d 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 090f6901-a7d3-42e6-94f4-69ff07632983 {http,https} \N \N {/s109-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cf40b75a-8bcd-4858-acbc-e2751a0e7afa 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 090f6901-a7d3-42e6-94f4-69ff07632983 {http,https} \N \N {/s109-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e86288a-0c75-41da-8aa6-c6a59da62285 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 090f6901-a7d3-42e6-94f4-69ff07632983 {http,https} \N \N {/s109-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7290602b-fe3e-40b5-82bc-6b4059ed46e7 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f0c01e5e-139d-4458-a3f7-47c6f9eb59de {http,https} \N \N {/s110-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c20d930-7ae4-4e53-89d5-3813eddabb29 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f0c01e5e-139d-4458-a3f7-47c6f9eb59de {http,https} \N \N {/s110-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22814e4c-15c5-474d-867e-d8128914d1c2 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f0c01e5e-139d-4458-a3f7-47c6f9eb59de {http,https} \N \N {/s110-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ed36a390-d149-4c0a-8847-87d6b227dade 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f0c01e5e-139d-4458-a3f7-47c6f9eb59de {http,https} \N \N {/s110-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d5f28231-3ddd-48d8-809c-c06b7c0c16e1 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c1ad53a6-4115-441a-a162-5a27b3e5c01d {http,https} \N \N {/s111-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4b9a146a-30d3-4c69-b730-284d0f77caeb 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c1ad53a6-4115-441a-a162-5a27b3e5c01d {http,https} \N \N {/s111-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a27ff94-a4ca-4bc2-b6b7-b00a7cd28518 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c1ad53a6-4115-441a-a162-5a27b3e5c01d {http,https} \N \N {/s111-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7f4d261e-7897-498f-86cc-cbac60d7e739 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c1ad53a6-4115-441a-a162-5a27b3e5c01d {http,https} \N \N {/s111-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+95c42670-8b63-487e-b3fb-86806f894d0b 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6b12e083-97d5-4964-82c5-22bc95802ef0 {http,https} \N \N {/s112-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b72c9536-b5ac-4844-9e11-91371fac14a8 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6b12e083-97d5-4964-82c5-22bc95802ef0 {http,https} \N \N {/s112-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ec15c7b-a948-4967-9d83-e7fd54b5cb83 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6b12e083-97d5-4964-82c5-22bc95802ef0 {http,https} \N \N {/s112-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f79e102-51fd-4070-bc31-d88b340e810a 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6b12e083-97d5-4964-82c5-22bc95802ef0 {http,https} \N \N {/s112-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bde2c98c-5c0d-486f-a6b2-924f80e044f0 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75d7f4d4-c369-46cd-bf84-fb40784d4fe1 {http,https} \N \N {/s113-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83413b21-589d-408c-990c-c0b17838847f 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75d7f4d4-c369-46cd-bf84-fb40784d4fe1 {http,https} \N \N {/s113-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18a13c73-d50a-4d12-aad9-16cd0d3c8a40 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75d7f4d4-c369-46cd-bf84-fb40784d4fe1 {http,https} \N \N {/s113-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1f0e0456-c7ee-4af6-8b94-5b077ea64048 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75d7f4d4-c369-46cd-bf84-fb40784d4fe1 {http,https} \N \N {/s113-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+10664876-8b48-4c8c-a764-3c40b0be0bfc 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5e861b07-f18f-48b1-aa4d-e44f7ca06eb5 {http,https} \N \N {/s114-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ab17906f-1ee8-4064-817e-5f904bdcf0e1 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5e861b07-f18f-48b1-aa4d-e44f7ca06eb5 {http,https} \N \N {/s114-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+520dc7fc-65be-4c4b-b25d-fa3365e23289 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5e861b07-f18f-48b1-aa4d-e44f7ca06eb5 {http,https} \N \N {/s114-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bf18669d-d0a2-4cc6-a560-6b8c8f04889b 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 5e861b07-f18f-48b1-aa4d-e44f7ca06eb5 {http,https} \N \N {/s114-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+78209c49-5cbb-42c5-b57f-234f15c66764 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N dc67018b-ba17-48f8-962a-e39d4e96eff4 {http,https} \N \N {/s115-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2a24cacd-bf1a-4757-864e-a07112ddbd8b 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N dc67018b-ba17-48f8-962a-e39d4e96eff4 {http,https} \N \N {/s115-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aca61615-c28e-4eff-84d8-674a55d753fc 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N dc67018b-ba17-48f8-962a-e39d4e96eff4 {http,https} \N \N {/s115-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+570e8fe5-d94d-43a7-802a-8b899a5261aa 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N dc67018b-ba17-48f8-962a-e39d4e96eff4 {http,https} \N \N {/s115-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc879ce6-2110-4e92-a92b-beb92d473387 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N d025ea98-eb37-4e43-bddc-302f5d4ecee1 {http,https} \N \N {/s116-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1fa533ff-0362-4c74-a56d-cd413a28365a 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N d025ea98-eb37-4e43-bddc-302f5d4ecee1 {http,https} \N \N {/s116-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e7b0b95e-ab6b-46bb-832b-3c75bae4f5e7 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N d025ea98-eb37-4e43-bddc-302f5d4ecee1 {http,https} \N \N {/s116-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+38b19459-3053-4648-8877-89fbbc1f2c77 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N d025ea98-eb37-4e43-bddc-302f5d4ecee1 {http,https} \N \N {/s116-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7c7b4f75-d8c9-4a52-9338-f498326f5d50 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 34f418de-2a74-47b6-ac68-9099b4281763 {http,https} \N \N {/s117-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+badac910-0e73-4e2c-a1d7-73829c48e95d 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 34f418de-2a74-47b6-ac68-9099b4281763 {http,https} \N \N {/s117-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18a1b5ec-aa61-4385-9b30-f71c68b07e06 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 34f418de-2a74-47b6-ac68-9099b4281763 {http,https} \N \N {/s117-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b6b598c0-2a3a-4d12-ba70-187419437c50 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 34f418de-2a74-47b6-ac68-9099b4281763 {http,https} \N \N {/s117-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5bedca3e-46a2-4e94-993d-9e7b21e11042 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 81c2ba99-2238-48c5-9d7b-ee96f85ed0c5 {http,https} \N \N {/s118-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2edb719b-ec2b-461d-a93d-2758a5212afb 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 81c2ba99-2238-48c5-9d7b-ee96f85ed0c5 {http,https} \N \N {/s118-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ffa536c0-c83d-42c0-84e6-ada512e9dadf 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 81c2ba99-2238-48c5-9d7b-ee96f85ed0c5 {http,https} \N \N {/s118-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+48e43137-ac5c-4671-9905-2f9da67c9000 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 81c2ba99-2238-48c5-9d7b-ee96f85ed0c5 {http,https} \N \N {/s118-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1940e6e7-466d-4546-899d-5e33ed975d22 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N bebc02c6-4798-4c51-9c65-6ac83e7e2050 {http,https} \N \N {/s119-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c6523340-b914-46e7-a2e3-a69e5bffa403 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N bebc02c6-4798-4c51-9c65-6ac83e7e2050 {http,https} \N \N {/s119-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d93c99d0-e85a-49cf-89fa-6d87358a5b58 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N bebc02c6-4798-4c51-9c65-6ac83e7e2050 {http,https} \N \N {/s119-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+50f21b8f-9054-4c33-b309-20980545c572 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N bebc02c6-4798-4c51-9c65-6ac83e7e2050 {http,https} \N \N {/s119-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f2a3023-b047-4086-abd9-c5d97811124e 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 84579611-336d-4291-ba77-6907426203d0 {http,https} \N \N {/s120-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+92c01ded-c2bd-4eec-bfa8-b0531bdb0a73 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 84579611-336d-4291-ba77-6907426203d0 {http,https} \N \N {/s120-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e6ada7b-3292-4c2d-b14b-45ec885c1fd0 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 84579611-336d-4291-ba77-6907426203d0 {http,https} \N \N {/s120-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ac8b92ca-6a7a-4f7c-9b07-ffc7843880a2 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 84579611-336d-4291-ba77-6907426203d0 {http,https} \N \N {/s120-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5a2283a1-2697-4b8c-8acb-6a6f8173f681 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 03d2fc5d-582c-4f45-bce2-41f8a1e45f45 {http,https} \N \N {/s121-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f38f49b-fdc3-464e-90d8-02b15fe2ad31 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 03d2fc5d-582c-4f45-bce2-41f8a1e45f45 {http,https} \N \N {/s121-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e0fe610-4072-4177-9864-4a0db3492c86 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 03d2fc5d-582c-4f45-bce2-41f8a1e45f45 {http,https} \N \N {/s121-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8576e3ab-8c50-4928-a817-1807774fdf4f 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 03d2fc5d-582c-4f45-bce2-41f8a1e45f45 {http,https} \N \N {/s121-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b72e7a63-e228-46b7-94f1-3c51d14033de 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 8bd5e802-0de6-462c-89d8-8a3dc33743fc {http,https} \N \N {/s122-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5d4bcbaa-a58e-4130-b1a7-4724344b734f 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 8bd5e802-0de6-462c-89d8-8a3dc33743fc {http,https} \N \N {/s122-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7ed9986a-597c-4b54-879b-c03b8467e3ea 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 8bd5e802-0de6-462c-89d8-8a3dc33743fc {http,https} \N \N {/s122-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f4bda711-2f4b-4ef1-b4f6-51a0c9aaf551 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 8bd5e802-0de6-462c-89d8-8a3dc33743fc {http,https} \N \N {/s122-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e175c49d-b8c4-460f-a1c0-c8e5132fd117 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75a284e6-a2d0-4fa0-9210-d1dfbfe393cc {http,https} \N \N {/s123-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+13ee1365-a19c-46f8-bc06-edc10649ab5d 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75a284e6-a2d0-4fa0-9210-d1dfbfe393cc {http,https} \N \N {/s123-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c299e8f2-c906-41ef-a314-0d76bbbfa642 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75a284e6-a2d0-4fa0-9210-d1dfbfe393cc {http,https} \N \N {/s123-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cc1cda5a-e5bf-4d05-b24f-71c66834cd12 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 75a284e6-a2d0-4fa0-9210-d1dfbfe393cc {http,https} \N \N {/s123-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9c9c2674-9b08-4180-b780-af8b124b8713 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 9462d6ae-3811-488a-8f43-93afe7e8d6ed {http,https} \N \N {/s124-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+77e43a18-b2e5-4ad3-8cd2-fb5a0642051c 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 9462d6ae-3811-488a-8f43-93afe7e8d6ed {http,https} \N \N {/s124-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0586adfd-898e-48af-85a6-46d4e32ff94a 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 9462d6ae-3811-488a-8f43-93afe7e8d6ed {http,https} \N \N {/s124-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+48b5b353-d790-4cb1-928e-a0e5fc50ba43 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 9462d6ae-3811-488a-8f43-93afe7e8d6ed {http,https} \N \N {/s124-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+62b72daa-088a-46be-a912-a53dacacc40d 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6a8aa9d7-cefe-455e-8671-721e43cd0b96 {http,https} \N \N {/s125-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+66d8c4b8-c15a-4fa6-ab67-f93a052240e6 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6a8aa9d7-cefe-455e-8671-721e43cd0b96 {http,https} \N \N {/s125-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e9a334f5-9712-4d35-aa49-ee8f2a3c1c37 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6a8aa9d7-cefe-455e-8671-721e43cd0b96 {http,https} \N \N {/s125-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e42d8021-6e19-4e0a-88d9-0c3d4b4251ca 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 6a8aa9d7-cefe-455e-8671-721e43cd0b96 {http,https} \N \N {/s125-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e3c1eada-79a8-44e2-bf0d-83e0beb0d0d6 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 1a79fb8d-58e0-42d1-a2b2-a9f730a6d635 {http,https} \N \N {/s126-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31cfa842-fde0-4f62-a531-c4da23b56987 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 1a79fb8d-58e0-42d1-a2b2-a9f730a6d635 {http,https} \N \N {/s126-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+efc36e6b-b127-48f6-93bd-684d6946f011 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 1a79fb8d-58e0-42d1-a2b2-a9f730a6d635 {http,https} \N \N {/s126-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+134a7d77-61d9-4cc2-ac68-c467caffe9ef 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 1a79fb8d-58e0-42d1-a2b2-a9f730a6d635 {http,https} \N \N {/s126-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22c1c65f-6dde-45bd-b897-2bfccaba56db 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 693ae85e-2dcb-4bac-a88f-832ef036ec35 {http,https} \N \N {/s127-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+deda4b00-8afd-4da7-93c6-55f93d1a3940 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 693ae85e-2dcb-4bac-a88f-832ef036ec35 {http,https} \N \N {/s127-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+13ca9075-a2f4-4fa2-88b5-8b2678917cdd 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 693ae85e-2dcb-4bac-a88f-832ef036ec35 {http,https} \N \N {/s127-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+edc97298-b3f2-4609-b3de-abb7c1f2022b 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 693ae85e-2dcb-4bac-a88f-832ef036ec35 {http,https} \N \N {/s127-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+349f9c32-5218-4754-93ac-20861d67a844 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N cf55043c-e758-4007-9d0b-f29ce449b017 {http,https} \N \N {/s128-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+72eae599-7eac-4ae5-8552-6128a5a1dcc8 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N cf55043c-e758-4007-9d0b-f29ce449b017 {http,https} \N \N {/s128-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1e6e5c03-f26e-4952-8038-65542e6c946e 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N cf55043c-e758-4007-9d0b-f29ce449b017 {http,https} \N \N {/s128-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1be86f83-0192-4b54-9cec-f9afba9d64ce 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N cf55043c-e758-4007-9d0b-f29ce449b017 {http,https} \N \N {/s128-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+10a509e5-1987-4c99-97cc-ba61e91cb463 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N b0f369f5-47ca-4790-a7c6-f70ef9670801 {http,https} \N \N {/s129-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+706ae1e3-3733-472a-8fa1-d2c252d53640 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N b0f369f5-47ca-4790-a7c6-f70ef9670801 {http,https} \N \N {/s129-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d170ee14-5ddf-47c6-8b38-df0e8fc15ea6 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N b0f369f5-47ca-4790-a7c6-f70ef9670801 {http,https} \N \N {/s129-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+91e08902-d98f-49e6-9b6b-6662d77c9bd5 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N b0f369f5-47ca-4790-a7c6-f70ef9670801 {http,https} \N \N {/s129-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8eea92e4-0351-485f-a161-7076751c078d 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f54e8793-3010-4551-8a86-bc026fcdbd71 {http,https} \N \N {/s130-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cfa091ed-d262-4f27-8bbd-48febb2fd667 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f54e8793-3010-4551-8a86-bc026fcdbd71 {http,https} \N \N {/s130-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+55259e8b-9b33-4a05-bb76-413012af4a4a 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f54e8793-3010-4551-8a86-bc026fcdbd71 {http,https} \N \N {/s130-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6131c283-8f0f-4cde-a92a-0bb689946152 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N f54e8793-3010-4551-8a86-bc026fcdbd71 {http,https} \N \N {/s130-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bdd51639-d904-477c-ae5c-fecbab88bde7 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N eda8a272-adab-466a-b5c9-ba27137d2bc3 {http,https} \N \N {/s131-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+febbe7d3-b013-4150-a925-0953ad7d6dd8 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N eda8a272-adab-466a-b5c9-ba27137d2bc3 {http,https} \N \N {/s131-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+59154981-6e60-4829-b8e9-35028496621c 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N eda8a272-adab-466a-b5c9-ba27137d2bc3 {http,https} \N \N {/s131-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+84095394-8e55-4d27-9cd4-6bbe0c5b82d9 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N eda8a272-adab-466a-b5c9-ba27137d2bc3 {http,https} \N \N {/s131-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9ce4484-1583-4a42-af69-5a8e3b731675 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 78c825c8-abdd-4280-9da9-d3bf00e23f82 {http,https} \N \N {/s132-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8e14a515-e926-44e6-9b09-3cdcae5043be 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 78c825c8-abdd-4280-9da9-d3bf00e23f82 {http,https} \N \N {/s132-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e642a930-abc7-4fea-8262-142f23cca225 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 78c825c8-abdd-4280-9da9-d3bf00e23f82 {http,https} \N \N {/s132-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f07ce3c0-4022-4953-b6e8-93077f0ac5ec 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 78c825c8-abdd-4280-9da9-d3bf00e23f82 {http,https} \N \N {/s132-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+221463db-8b0c-4b4f-9074-c95726a8aee4 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c3dc6599-036f-46b8-a95e-8e5b6ef3a3f5 {http,https} \N \N {/s133-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fa564666-4866-4273-8a2e-9c2fe411e69f 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c3dc6599-036f-46b8-a95e-8e5b6ef3a3f5 {http,https} \N \N {/s133-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+42113b48-05fa-40a6-ac11-fd452ceaa4c2 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c3dc6599-036f-46b8-a95e-8e5b6ef3a3f5 {http,https} \N \N {/s133-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f48ba6a-3ec1-4019-8537-41672b494b7b 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c3dc6599-036f-46b8-a95e-8e5b6ef3a3f5 {http,https} \N \N {/s133-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc7dbea1-6fd5-4ae3-aa0d-ff0762ca4861 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 4372ca08-22e6-4a0e-8d13-f598ba86cf37 {http,https} \N \N {/s134-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2e6aa602-9eff-416c-a3c5-bf2e33818b5c 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 4372ca08-22e6-4a0e-8d13-f598ba86cf37 {http,https} \N \N {/s134-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4da38f5e-153c-40d6-bead-d476a3a94fa9 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 4372ca08-22e6-4a0e-8d13-f598ba86cf37 {http,https} \N \N {/s134-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d784d600-b813-4709-8100-46bc0d674810 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 4372ca08-22e6-4a0e-8d13-f598ba86cf37 {http,https} \N \N {/s134-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+332ac737-d32b-4f6c-bced-49a7e73d2aa3 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 0766430c-c266-489c-bc27-58df3fd10388 {http,https} \N \N {/s135-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0c29e82e-4079-4cc5-b87a-6555812349cf 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 0766430c-c266-489c-bc27-58df3fd10388 {http,https} \N \N {/s135-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+253636c0-8013-4d51-871f-01a78270352d 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 0766430c-c266-489c-bc27-58df3fd10388 {http,https} \N \N {/s135-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ed9b0cc8-adef-4cd1-be95-303b7d47d553 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N 0766430c-c266-489c-bc27-58df3fd10388 {http,https} \N \N {/s135-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c77769a9-0bb9-44aa-90c2-f0840c47f629 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c7167c55-60fb-45f7-b257-4acddb1d9119 {http,https} \N \N {/s136-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b54080f1-39c7-4446-8f78-ef814583a0e4 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c7167c55-60fb-45f7-b257-4acddb1d9119 {http,https} \N \N {/s136-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a68f5932-2632-44d1-a937-0734dba208e3 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c7167c55-60fb-45f7-b257-4acddb1d9119 {http,https} \N \N {/s136-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+40614334-e48d-433d-947c-64c0c5055aef 2022-05-26 09:04:24+00 2022-05-26 09:04:24+00 \N c7167c55-60fb-45f7-b257-4acddb1d9119 {http,https} \N \N {/s136-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c308cce9-e114-4e48-925e-94804505abdf 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 76b8797a-0ad8-4a9f-9fdf-561c79e481d9 {http,https} \N \N {/s137-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ec57a214-5299-4c0e-9de6-dc8df6fff285 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 76b8797a-0ad8-4a9f-9fdf-561c79e481d9 {http,https} \N \N {/s137-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb583546-40d6-418c-8552-fa944d2412bb 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 76b8797a-0ad8-4a9f-9fdf-561c79e481d9 {http,https} \N \N {/s137-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1952393c-d082-4d15-b2bc-29e2d7f82ed3 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 76b8797a-0ad8-4a9f-9fdf-561c79e481d9 {http,https} \N \N {/s137-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5c248012-76cb-453c-909b-d40632e801e1 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bad7c636-19ad-430e-8c49-6e4efddc4376 {http,https} \N \N {/s138-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fb2c93c5-42ee-4015-b968-df7c7e9c8b82 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bad7c636-19ad-430e-8c49-6e4efddc4376 {http,https} \N \N {/s138-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8ab89b41-6cfe-48b6-a3e5-367ecec10896 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bad7c636-19ad-430e-8c49-6e4efddc4376 {http,https} \N \N {/s138-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a2e0400-a685-4c85-abcc-b5ef1fdd7051 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bad7c636-19ad-430e-8c49-6e4efddc4376 {http,https} \N \N {/s138-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f6241fa-ab8a-4cf8-803e-552751cdbbdb 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N fd6fd9ca-1169-45ba-bb87-8b846a8d0d3e {http,https} \N \N {/s139-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2a8523fc-1001-4503-a12f-db41805792f8 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N fd6fd9ca-1169-45ba-bb87-8b846a8d0d3e {http,https} \N \N {/s139-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc54e31d-68da-46cc-b0da-84aea518e92e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N fd6fd9ca-1169-45ba-bb87-8b846a8d0d3e {http,https} \N \N {/s139-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+08814b9e-e844-4393-a4b8-802458c70eaf 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N fd6fd9ca-1169-45ba-bb87-8b846a8d0d3e {http,https} \N \N {/s139-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+952cad34-82e7-4474-b402-3d9b3467fba0 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N a2ee552e-0961-4036-8d1c-8ebd420f28ed {http,https} \N \N {/s140-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3f75d9ae-7607-4e84-9382-b80f2d70a99d 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N a2ee552e-0961-4036-8d1c-8ebd420f28ed {http,https} \N \N {/s140-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0517cf2c-98e8-41de-ae3b-56c2daee2859 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N a2ee552e-0961-4036-8d1c-8ebd420f28ed {http,https} \N \N {/s140-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fbde95fa-3633-41d1-beca-8df6f9f1b0ae 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N a2ee552e-0961-4036-8d1c-8ebd420f28ed {http,https} \N \N {/s140-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c04af6ae-707e-4f8e-8e03-d6b59d1ddb57 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 6fca3f1f-fa31-4c70-8059-aee7dd0d5be3 {http,https} \N \N {/s141-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+79657c82-6938-4449-9349-48ec8678e142 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 6fca3f1f-fa31-4c70-8059-aee7dd0d5be3 {http,https} \N \N {/s141-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+37381f66-6f01-4b17-824b-27896e93bd95 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 6fca3f1f-fa31-4c70-8059-aee7dd0d5be3 {http,https} \N \N {/s141-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0ee50621-2c9a-4945-b938-4a203e6ea199 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 6fca3f1f-fa31-4c70-8059-aee7dd0d5be3 {http,https} \N \N {/s141-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+80291ade-7bd3-42f8-8ea5-98a1355def09 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 70d03905-4002-4dc1-b3f9-336d25ee164e {http,https} \N \N {/s142-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+009ea757-f3ad-4302-8296-abe06be681f0 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 70d03905-4002-4dc1-b3f9-336d25ee164e {http,https} \N \N {/s142-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4b00370e-83a7-48e5-8e88-43685cde1dca 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 70d03905-4002-4dc1-b3f9-336d25ee164e {http,https} \N \N {/s142-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b6887d29-3015-4e8b-b486-02dc03fb70f5 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 70d03905-4002-4dc1-b3f9-336d25ee164e {http,https} \N \N {/s142-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+54b9278d-ea83-4814-ba00-fa11eb2e0183 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 4693dd6c-1d27-46df-b5be-259eda6ad3df {http,https} \N \N {/s143-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3a7fe796-5dd8-40fe-842d-d8a4750493c7 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 4693dd6c-1d27-46df-b5be-259eda6ad3df {http,https} \N \N {/s143-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a73b9f2-4758-4a32-9d2d-6186cbd37d06 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 4693dd6c-1d27-46df-b5be-259eda6ad3df {http,https} \N \N {/s143-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c40b1edc-e918-47ca-896d-2fe861a2b16d 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 4693dd6c-1d27-46df-b5be-259eda6ad3df {http,https} \N \N {/s143-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a9007af4-7294-4faf-99d1-ea26e4664eea 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 390c61c3-b91b-44d0-9132-d629f3f7f2c2 {http,https} \N \N {/s144-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8390994d-f65b-486b-b331-d6233c27975d 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 390c61c3-b91b-44d0-9132-d629f3f7f2c2 {http,https} \N \N {/s144-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+286457da-3d3d-442a-a47e-eddc90f94fae 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 390c61c3-b91b-44d0-9132-d629f3f7f2c2 {http,https} \N \N {/s144-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2bb38fd-11c0-4302-bc73-9f2b92bfdb7e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 390c61c3-b91b-44d0-9132-d629f3f7f2c2 {http,https} \N \N {/s144-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+799f1236-6939-49dc-9559-ce456182edfe 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N addbf9ae-c319-4a46-831b-a2c71204cfdc {http,https} \N \N {/s145-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+afa4a841-ac7e-479d-8cfb-6ee4f3e7576c 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N addbf9ae-c319-4a46-831b-a2c71204cfdc {http,https} \N \N {/s145-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+48d3420a-0715-417a-bd0e-595428ee8552 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N addbf9ae-c319-4a46-831b-a2c71204cfdc {http,https} \N \N {/s145-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1e3c0494-c573-4202-802e-16c020bd1dcc 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N addbf9ae-c319-4a46-831b-a2c71204cfdc {http,https} \N \N {/s145-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+71d5e006-1d1b-45d3-ab77-767bbc08dacf 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d59261e7-93ca-464a-b84d-cc9c64e2d649 {http,https} \N \N {/s146-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+40d37028-4253-4d09-a7d4-1d9afb2f80f5 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d59261e7-93ca-464a-b84d-cc9c64e2d649 {http,https} \N \N {/s146-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5fa958da-4c0b-4ff0-921e-2d4425c096e2 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d59261e7-93ca-464a-b84d-cc9c64e2d649 {http,https} \N \N {/s146-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+87f8e3b3-db11-4fb6-897e-3bcf78d1d2f2 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d59261e7-93ca-464a-b84d-cc9c64e2d649 {http,https} \N \N {/s146-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d55f55bb-699e-4e16-ac97-197e8f7f4a24 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 37262d9e-1dd7-4314-9a5a-d289c7479be0 {http,https} \N \N {/s147-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ec1563f8-689b-4621-b57f-89f5fabb6b8a 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 37262d9e-1dd7-4314-9a5a-d289c7479be0 {http,https} \N \N {/s147-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b2ade045-55bf-438b-b0e2-f499953aa888 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 37262d9e-1dd7-4314-9a5a-d289c7479be0 {http,https} \N \N {/s147-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8c8b26e7-b443-4738-82f2-3695cd656943 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 37262d9e-1dd7-4314-9a5a-d289c7479be0 {http,https} \N \N {/s147-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+20a06da8-c6b3-4250-8d30-8bcabb5d97d9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d3ec5e93-e9e3-4fd4-a27b-6af1e300aa4b {http,https} \N \N {/s148-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4ceeb28c-8cac-4f52-8a6d-400716ad0cfb 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d3ec5e93-e9e3-4fd4-a27b-6af1e300aa4b {http,https} \N \N {/s148-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+10b33ab3-84ff-4c07-961c-8baf666ebf7f 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d3ec5e93-e9e3-4fd4-a27b-6af1e300aa4b {http,https} \N \N {/s148-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+76636d5b-a12e-4fe9-a09b-c98ecdad1743 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N d3ec5e93-e9e3-4fd4-a27b-6af1e300aa4b {http,https} \N \N {/s148-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+09b43683-f7ac-480f-b8df-4d99f6a5703b 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 0cdb0d81-1c8a-49b4-b5aa-50b627e298c6 {http,https} \N \N {/s149-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ea17964f-4682-47be-8580-4e94210d34ec 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 0cdb0d81-1c8a-49b4-b5aa-50b627e298c6 {http,https} \N \N {/s149-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e82f3a93-209d-4e7c-aec5-3874747b2b8a 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 0cdb0d81-1c8a-49b4-b5aa-50b627e298c6 {http,https} \N \N {/s149-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+69784499-8f2a-4fcc-9fe6-e0ab42202ef6 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 0cdb0d81-1c8a-49b4-b5aa-50b627e298c6 {http,https} \N \N {/s149-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+85dd27b7-3399-4ab0-8ec7-d2e397ea301b 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e987b7a-1d92-49e3-ad2f-362501d07bf9 {http,https} \N \N {/s150-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9f001c3-3cdb-4a5f-997d-3a7b00022131 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e987b7a-1d92-49e3-ad2f-362501d07bf9 {http,https} \N \N {/s150-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+39c52891-9c51-4f8d-85bf-9604c3f49c22 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e987b7a-1d92-49e3-ad2f-362501d07bf9 {http,https} \N \N {/s150-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9b34cd4b-03f7-4911-8326-52e6b1156649 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e987b7a-1d92-49e3-ad2f-362501d07bf9 {http,https} \N \N {/s150-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af5092d3-7538-4c67-a03a-e13d86f94516 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 98193422-6ec1-4767-8568-e34555d37244 {http,https} \N \N {/s151-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f990e621-c712-4904-8d2a-7f0f97c4c3d0 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 98193422-6ec1-4767-8568-e34555d37244 {http,https} \N \N {/s151-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+735fede1-62ad-4693-a8c9-aa88ed3e3bc0 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 98193422-6ec1-4767-8568-e34555d37244 {http,https} \N \N {/s151-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+98a8d34c-8127-469a-a53f-930fe4864220 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 98193422-6ec1-4767-8568-e34555d37244 {http,https} \N \N {/s151-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d240fa9b-a666-4967-9e28-d757193dd92d 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 23c5d21a-6ff6-4f87-950b-3189611df400 {http,https} \N \N {/s152-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cee33038-b02b-401c-b30c-ea12d9e6cb5b 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 23c5d21a-6ff6-4f87-950b-3189611df400 {http,https} \N \N {/s152-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e7664be5-15b5-4459-863a-9a57aeabd8db 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 23c5d21a-6ff6-4f87-950b-3189611df400 {http,https} \N \N {/s152-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c7300262-fb86-4140-9dd8-541f90ba1602 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 23c5d21a-6ff6-4f87-950b-3189611df400 {http,https} \N \N {/s152-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7a83033b-385b-4e01-90ea-acc959fae024 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 61b20f0c-ad75-46c5-bdb1-c9ee4db679eb {http,https} \N \N {/s153-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc96baa4-77a2-456d-85da-1e09359806a2 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 61b20f0c-ad75-46c5-bdb1-c9ee4db679eb {http,https} \N \N {/s153-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+35faf989-ccc4-4d00-88da-a30a1726bf76 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 61b20f0c-ad75-46c5-bdb1-c9ee4db679eb {http,https} \N \N {/s153-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aadd4d64-4895-45e8-850a-5df9123186d3 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 61b20f0c-ad75-46c5-bdb1-c9ee4db679eb {http,https} \N \N {/s153-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+43b90307-3f64-4595-9c39-7e96c80a03ec 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N f658e233-91f5-4e42-a97f-43303defe86d {http,https} \N \N {/s154-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f6fe2815-3819-40fa-8901-4baf0fc1c4a5 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N f658e233-91f5-4e42-a97f-43303defe86d {http,https} \N \N {/s154-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cc0a9449-df5d-44fe-a9d3-7332f4787c05 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N f658e233-91f5-4e42-a97f-43303defe86d {http,https} \N \N {/s154-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dfae0345-b3d0-4ce1-bafd-39bffa1ad3ea 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N f658e233-91f5-4e42-a97f-43303defe86d {http,https} \N \N {/s154-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+49206548-9d47-43f6-aa41-d8fccc9032a3 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bf2c91f2-cfdd-4f0a-bb05-0433141ad9ce {http,https} \N \N {/s155-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2b088891-7e35-4485-ad96-e1b450341308 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bf2c91f2-cfdd-4f0a-bb05-0433141ad9ce {http,https} \N \N {/s155-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dfc48b47-1ab1-4253-af03-2be8b4070ab2 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bf2c91f2-cfdd-4f0a-bb05-0433141ad9ce {http,https} \N \N {/s155-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f5cfbdc5-4203-4ce9-8d60-2441dfa6f6ea 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N bf2c91f2-cfdd-4f0a-bb05-0433141ad9ce {http,https} \N \N {/s155-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d529b339-f52e-4cde-a88c-fe21ca1edbb9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 44e7d282-81cf-4f35-b20d-289a41d57da9 {http,https} \N \N {/s156-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b1858bb9-c701-41ab-8faf-ef7abdc3f2af 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 44e7d282-81cf-4f35-b20d-289a41d57da9 {http,https} \N \N {/s156-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+34d86e9c-51f8-4de3-b44f-6a91904649d2 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 44e7d282-81cf-4f35-b20d-289a41d57da9 {http,https} \N \N {/s156-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83dd3ef4-3da3-42d3-98ff-83f6f00e18ae 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 44e7d282-81cf-4f35-b20d-289a41d57da9 {http,https} \N \N {/s156-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+87989a69-9c8a-4037-9fea-680cc4fd282b 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e9458db-1f76-4728-bf68-8f100dcb5e04 {http,https} \N \N {/s157-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0f42d0c4-09bf-4799-a550-d7bd5de071cf 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e9458db-1f76-4728-bf68-8f100dcb5e04 {http,https} \N \N {/s157-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+67a0134f-95ac-4aea-a181-e16091b3261b 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e9458db-1f76-4728-bf68-8f100dcb5e04 {http,https} \N \N {/s157-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be0fe9db-b3a3-4221-a3a0-e3d4e9183d56 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5e9458db-1f76-4728-bf68-8f100dcb5e04 {http,https} \N \N {/s157-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22d86719-08cd-4b0b-9e00-f9957f27dde2 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5cf7efb5-6ce3-4bfa-9b9c-69615c0424c3 {http,https} \N \N {/s158-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2fe55a66-ab3e-4816-8a2d-4f3f992bc8d7 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5cf7efb5-6ce3-4bfa-9b9c-69615c0424c3 {http,https} \N \N {/s158-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eabeed58-c2e9-4516-b141-2e55494094f4 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5cf7efb5-6ce3-4bfa-9b9c-69615c0424c3 {http,https} \N \N {/s158-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c29be95e-602c-461e-9836-2eaf64373ae0 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 5cf7efb5-6ce3-4bfa-9b9c-69615c0424c3 {http,https} \N \N {/s158-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e2e495a6-8e59-41bb-91c0-3c9336f2d28e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N e601de5f-ad58-4d48-83b7-bc0e20cadd7e {http,https} \N \N {/s159-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b2c400a2-57a3-4756-a5a5-20c57fc6da35 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N e601de5f-ad58-4d48-83b7-bc0e20cadd7e {http,https} \N \N {/s159-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c70e2d23-3f67-4bad-8c2b-0ae0bf15b8d9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N e601de5f-ad58-4d48-83b7-bc0e20cadd7e {http,https} \N \N {/s159-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fd0b32f7-c191-46c2-82df-54ed7eea9ada 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N e601de5f-ad58-4d48-83b7-bc0e20cadd7e {http,https} \N \N {/s159-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eb4d3228-d924-463b-91ec-d7c92d472bc9 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 3995380e-ac1c-4133-a6e1-65a2b355a121 {http,https} \N \N {/s160-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+daad247c-b556-4547-b6ff-76c3489e0c7d 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 3995380e-ac1c-4133-a6e1-65a2b355a121 {http,https} \N \N {/s160-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f454e59-d967-46f5-95cd-37a6e8363121 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 3995380e-ac1c-4133-a6e1-65a2b355a121 {http,https} \N \N {/s160-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ddd7d394-ee2a-4812-9cce-9397b487698e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 3995380e-ac1c-4133-a6e1-65a2b355a121 {http,https} \N \N {/s160-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4a5efd5a-f47f-4ec8-9c73-59657da79ea1 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 109dabd3-4d13-40ea-b6f4-2a94d74c7f6c {http,https} \N \N {/s161-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2b21d645-cd05-4ae9-9072-b5b343826646 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 109dabd3-4d13-40ea-b6f4-2a94d74c7f6c {http,https} \N \N {/s161-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d71ea753-3fe6-4582-85af-02c13ec4f25f 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 109dabd3-4d13-40ea-b6f4-2a94d74c7f6c {http,https} \N \N {/s161-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dcc781be-61d7-488f-8a54-39b32aca478b 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 109dabd3-4d13-40ea-b6f4-2a94d74c7f6c {http,https} \N \N {/s161-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+79528e1b-fa40-4dfe-a02d-67c5681b347a 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 502c5b41-66bf-4383-918a-badfea2d25c7 {http,https} \N \N {/s162-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f763ec59-ab8e-465a-acb1-9d9c6cb7a607 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 502c5b41-66bf-4383-918a-badfea2d25c7 {http,https} \N \N {/s162-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7f1d5485-afa9-4f7c-97a6-709cc21b906a 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 502c5b41-66bf-4383-918a-badfea2d25c7 {http,https} \N \N {/s162-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ffe74437-4a70-40f0-be0e-5b389c7ae2f0 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 502c5b41-66bf-4383-918a-badfea2d25c7 {http,https} \N \N {/s162-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fd14267c-b276-4cac-bc09-6a95fff7540e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 9557d7a1-d82f-4fab-a4c1-59b705f29b2e {http,https} \N \N {/s163-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+04c7a8b9-a0a2-4fc9-b61e-c9722e7d2367 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 9557d7a1-d82f-4fab-a4c1-59b705f29b2e {http,https} \N \N {/s163-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e86a838-8e98-40d7-96ef-62e4248a68b3 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 9557d7a1-d82f-4fab-a4c1-59b705f29b2e {http,https} \N \N {/s163-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5074512e-c1e0-4c3c-b79a-368b0a3ce696 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 9557d7a1-d82f-4fab-a4c1-59b705f29b2e {http,https} \N \N {/s163-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a92a46d7-e383-4199-80a1-65ab84ed38e7 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N cefbb83a-2d32-4aba-83e1-1ad7811849e9 {http,https} \N \N {/s164-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f325ec0c-73df-4b78-a4c3-a34006513067 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N cefbb83a-2d32-4aba-83e1-1ad7811849e9 {http,https} \N \N {/s164-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f4154d0-78ce-4ff2-bf50-03a4fb272e4f 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N cefbb83a-2d32-4aba-83e1-1ad7811849e9 {http,https} \N \N {/s164-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+72544d66-cec7-476c-af59-f1af6974176e 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N cefbb83a-2d32-4aba-83e1-1ad7811849e9 {http,https} \N \N {/s164-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be535d03-73d3-471e-aed6-8833ae34a2ae 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 24fbd204-d7a7-4d11-9109-a73e52f718b1 {http,https} \N \N {/s165-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc95d9db-2f13-464d-a318-99d242a2bb52 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 24fbd204-d7a7-4d11-9109-a73e52f718b1 {http,https} \N \N {/s165-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18b7158f-dedf-48ea-85b3-147c47351fcd 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 24fbd204-d7a7-4d11-9109-a73e52f718b1 {http,https} \N \N {/s165-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b9bd8aa8-6682-47d1-85a6-57723ba8e341 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N 24fbd204-d7a7-4d11-9109-a73e52f718b1 {http,https} \N \N {/s165-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+93e68fcf-c0b5-4f1b-9605-da6389ab6621 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N ef9b8d4d-3e83-4353-a80e-426e5fc7cbb9 {http,https} \N \N {/s166-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+51266dc4-3bdf-415f-b1ae-f3842cbe5dee 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N ef9b8d4d-3e83-4353-a80e-426e5fc7cbb9 {http,https} \N \N {/s166-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f306910-0c7b-4bfb-8cc5-4e4280adcfa6 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N ef9b8d4d-3e83-4353-a80e-426e5fc7cbb9 {http,https} \N \N {/s166-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6eb78f5c-80c0-4492-b352-055da84d6a98 2022-05-26 09:04:25+00 2022-05-26 09:04:25+00 \N ef9b8d4d-3e83-4353-a80e-426e5fc7cbb9 {http,https} \N \N {/s166-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19a74a8f-9328-4e67-be6e-3d296866251e 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd6e4a2a-b1f5-4fdf-bb0d-6e9918275bd6 {http,https} \N \N {/s167-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28590603-cb60-45a8-835f-bfc5232380c5 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd6e4a2a-b1f5-4fdf-bb0d-6e9918275bd6 {http,https} \N \N {/s167-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3a7417a0-1ba7-47db-913e-ca211871ddba 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd6e4a2a-b1f5-4fdf-bb0d-6e9918275bd6 {http,https} \N \N {/s167-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e51ced59-2ced-4656-966f-584a9a4e488a 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd6e4a2a-b1f5-4fdf-bb0d-6e9918275bd6 {http,https} \N \N {/s167-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e50002ab-e446-4061-93f7-68d7c2cfa4d5 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a39c21f4-1588-473b-b5f0-ca58437f5670 {http,https} \N \N {/s168-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+471db396-7e15-4da7-8991-73ab2ad29ea4 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a39c21f4-1588-473b-b5f0-ca58437f5670 {http,https} \N \N {/s168-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2277f88f-da72-4c75-851d-9b444121c708 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a39c21f4-1588-473b-b5f0-ca58437f5670 {http,https} \N \N {/s168-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1e6ab643-c8e7-4bfd-8b7f-fc838a15afb4 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a39c21f4-1588-473b-b5f0-ca58437f5670 {http,https} \N \N {/s168-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f6d11d3-2fa2-4101-86f5-e2c7f169f5ff 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N cd7ff4b6-0461-43d7-89d4-00df67b34598 {http,https} \N \N {/s169-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+87d2868f-44db-445d-a98a-7c3ee3502eee 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N cd7ff4b6-0461-43d7-89d4-00df67b34598 {http,https} \N \N {/s169-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2171b9be-1957-4eb2-aafb-b201eecc0199 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N cd7ff4b6-0461-43d7-89d4-00df67b34598 {http,https} \N \N {/s169-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9b8b29f-1044-490c-8227-546e7c524de9 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N cd7ff4b6-0461-43d7-89d4-00df67b34598 {http,https} \N \N {/s169-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+014a55eb-f1f5-42b5-9fd5-c1e7a06e8bad 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d46890a2-26b2-4d3c-860d-f54cc24b7663 {http,https} \N \N {/s170-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+04902f25-a16f-47d8-8870-10ceb0fdc8bc 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d46890a2-26b2-4d3c-860d-f54cc24b7663 {http,https} \N \N {/s170-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18a21895-85e8-4b21-b594-750a5352ba3e 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d46890a2-26b2-4d3c-860d-f54cc24b7663 {http,https} \N \N {/s170-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+261c98c5-f53c-400d-8562-8a917211812c 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d46890a2-26b2-4d3c-860d-f54cc24b7663 {http,https} \N \N {/s170-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cd4fadc3-d86e-4ed2-b0a0-5eac3256d265 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 4d17db21-c723-4052-9a5f-d704fd01862f {http,https} \N \N {/s171-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d5a00454-610d-4098-a872-15d2a01b85a8 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 4d17db21-c723-4052-9a5f-d704fd01862f {http,https} \N \N {/s171-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af223b5b-d885-4784-924b-8a4c97bb2b2a 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 4d17db21-c723-4052-9a5f-d704fd01862f {http,https} \N \N {/s171-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c0388b6e-65f0-412c-96ad-2b507eaf725e 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 4d17db21-c723-4052-9a5f-d704fd01862f {http,https} \N \N {/s171-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ff1879e3-337a-44ca-8f95-851aebf97a03 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a9c1b4cf-9457-4010-a9b8-4f5236dcc5ce {http,https} \N \N {/s172-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+33dbfde5-d6b8-45c4-a42c-7eb99cfe74e5 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a9c1b4cf-9457-4010-a9b8-4f5236dcc5ce {http,https} \N \N {/s172-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+30c0bec9-12fe-4055-9a90-29ad4855670d 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a9c1b4cf-9457-4010-a9b8-4f5236dcc5ce {http,https} \N \N {/s172-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+37cb8256-042c-4890-ac10-3e8a255c9d48 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a9c1b4cf-9457-4010-a9b8-4f5236dcc5ce {http,https} \N \N {/s172-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7c07beaa-fa8f-4840-8b08-d11391de882a 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N e79cb133-66ba-406a-895d-559eddf73902 {http,https} \N \N {/s173-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7c78deff-8eb1-4f60-b5e7-2bbabeca3fdc 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N e79cb133-66ba-406a-895d-559eddf73902 {http,https} \N \N {/s173-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+265650a8-af3a-4fcf-8c43-45d2c91e7fa8 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N e79cb133-66ba-406a-895d-559eddf73902 {http,https} \N \N {/s173-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc457997-7b4a-4959-a96d-2a73aa411470 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N e79cb133-66ba-406a-895d-559eddf73902 {http,https} \N \N {/s173-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e7355947-c821-4cca-a485-e44c90ec50ab 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 8b99e7b2-ccdf-4cb9-b185-e3cde9ec9af7 {http,https} \N \N {/s174-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+06f8adbc-0a97-429f-a3b8-ee9a9feddbc7 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 8b99e7b2-ccdf-4cb9-b185-e3cde9ec9af7 {http,https} \N \N {/s174-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b4d627bb-b68e-4a92-be3e-c3fe220cf533 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 8b99e7b2-ccdf-4cb9-b185-e3cde9ec9af7 {http,https} \N \N {/s174-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9cf4e435-0e53-4223-8c95-38ec63479fbd 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 8b99e7b2-ccdf-4cb9-b185-e3cde9ec9af7 {http,https} \N \N {/s174-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+40948daf-3e7d-4adb-9aa1-83f20e11979c 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d807dd5e-21de-4d30-823e-41d98b76bf8e {http,https} \N \N {/s175-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c6cd578b-ad55-4f6e-b2fe-4ea1f40cfb21 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d807dd5e-21de-4d30-823e-41d98b76bf8e {http,https} \N \N {/s175-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cc34b095-cf47-4f04-8b42-fff44d04ab50 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d807dd5e-21de-4d30-823e-41d98b76bf8e {http,https} \N \N {/s175-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0642f66b-a15c-4c78-8937-1b035448c2e6 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d807dd5e-21de-4d30-823e-41d98b76bf8e {http,https} \N \N {/s175-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8c5829a6-6859-4831-bb61-b8ed82e74d1c 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 00284c22-d742-4a15-9a67-4bb4dcd90d8f {http,https} \N \N {/s176-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b4ca032f-79e6-4092-aab3-9382b2bf1052 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 00284c22-d742-4a15-9a67-4bb4dcd90d8f {http,https} \N \N {/s176-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b52bf36b-7703-47e3-ba86-03adf2ca98bd 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 00284c22-d742-4a15-9a67-4bb4dcd90d8f {http,https} \N \N {/s176-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0ea7b271-e1e4-46f7-955a-36f62ab6e960 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 00284c22-d742-4a15-9a67-4bb4dcd90d8f {http,https} \N \N {/s176-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1f26d35e-560f-49f9-b5e0-9ee0504e49b3 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 751853be-1e25-490e-a6ef-9417a6b540ef {http,https} \N \N {/s177-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+657dc03f-22d6-4e30-9a53-a66246406012 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 751853be-1e25-490e-a6ef-9417a6b540ef {http,https} \N \N {/s177-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+664d362d-e68d-48ac-ab93-79e806f3865c 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 751853be-1e25-490e-a6ef-9417a6b540ef {http,https} \N \N {/s177-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+180ac050-1a3c-405e-880f-0be43d342e65 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 751853be-1e25-490e-a6ef-9417a6b540ef {http,https} \N \N {/s177-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3bc4438-9c03-4bd3-a817-2faba58a55a3 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N f73bf090-0d18-40e8-b186-7fc9e91e62d1 {http,https} \N \N {/s178-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+abc7b6b5-d944-4ba7-aeb5-7fab62c8bdac 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N f73bf090-0d18-40e8-b186-7fc9e91e62d1 {http,https} \N \N {/s178-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ae8e4b9-adab-4512-80c8-4277c7eb37a3 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N f73bf090-0d18-40e8-b186-7fc9e91e62d1 {http,https} \N \N {/s178-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2c55697c-20fc-48e9-b4db-3c462f62fb5f 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N f73bf090-0d18-40e8-b186-7fc9e91e62d1 {http,https} \N \N {/s178-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+91069e9f-1303-4a9d-aa2a-93db4d7f111f 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 12042bab-a587-44e7-881d-2315a7305c39 {http,https} \N \N {/s179-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+281664fa-5496-474b-8fde-5f587ce458a8 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 12042bab-a587-44e7-881d-2315a7305c39 {http,https} \N \N {/s179-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3a29ce38-4b03-48b5-93b4-d2b06a9b5acc 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 12042bab-a587-44e7-881d-2315a7305c39 {http,https} \N \N {/s179-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8481ad3f-469b-4d1d-bf37-5072d3a3c24c 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 12042bab-a587-44e7-881d-2315a7305c39 {http,https} \N \N {/s179-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ea144262-7bb7-4796-a5bb-2f5072ec79ec 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 9b0c19f6-6ab2-4119-8a6f-37e8f15cdd98 {http,https} \N \N {/s180-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d80c53dc-5d1c-43da-b9bb-acc96d018c65 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 9b0c19f6-6ab2-4119-8a6f-37e8f15cdd98 {http,https} \N \N {/s180-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bea9c68b-aa00-4ead-9a62-c39d8b90271f 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 9b0c19f6-6ab2-4119-8a6f-37e8f15cdd98 {http,https} \N \N {/s180-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5a0df2fb-4699-4cd5-969d-0496de8dd583 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 9b0c19f6-6ab2-4119-8a6f-37e8f15cdd98 {http,https} \N \N {/s180-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cbdd7c1b-7934-4a48-a084-1b4e85f4e816 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d76ebd2e-5ee7-4810-864b-3a12440faca9 {http,https} \N \N {/s181-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9a829cb-f1ea-4112-be04-bcdfc24331a9 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d76ebd2e-5ee7-4810-864b-3a12440faca9 {http,https} \N \N {/s181-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a5a86801-54b0-48b3-ba22-a417173689cf 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d76ebd2e-5ee7-4810-864b-3a12440faca9 {http,https} \N \N {/s181-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+71f19cd6-ad7a-426d-bc0e-d77f624526ac 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N d76ebd2e-5ee7-4810-864b-3a12440faca9 {http,https} \N \N {/s181-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+32317f4f-f3a0-4809-8b51-24efb7379e43 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd3ca0d9-03ac-4021-8de2-08321ccb3277 {http,https} \N \N {/s182-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a846c0e2-87a5-446d-8138-c11efa369837 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd3ca0d9-03ac-4021-8de2-08321ccb3277 {http,https} \N \N {/s182-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a271e44d-c12d-49bb-971f-487597b32292 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd3ca0d9-03ac-4021-8de2-08321ccb3277 {http,https} \N \N {/s182-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+07ee9f76-3f50-4a4f-8b6e-871e8918ec9d 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N bd3ca0d9-03ac-4021-8de2-08321ccb3277 {http,https} \N \N {/s182-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ff672f37-19fc-49ef-9a17-bce8296072f0 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 528428e4-3f06-482d-8b4b-65b51c3bb653 {http,https} \N \N {/s183-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b30a35ef-48a7-48da-9ce3-9fe6e79c7dbf 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 528428e4-3f06-482d-8b4b-65b51c3bb653 {http,https} \N \N {/s183-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9592dfea-488a-4db5-95f4-bfba492f7eaa 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 528428e4-3f06-482d-8b4b-65b51c3bb653 {http,https} \N \N {/s183-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d6da54cb-b86d-46b4-a37d-7d20671a5c68 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 528428e4-3f06-482d-8b4b-65b51c3bb653 {http,https} \N \N {/s183-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63879c78-1dfc-40f1-bc58-5c1528acec16 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 73e663c8-0f96-4908-a02c-5c7eea81e327 {http,https} \N \N {/s184-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+94eb27f6-061d-45ab-949c-e2c4eee3f996 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 73e663c8-0f96-4908-a02c-5c7eea81e327 {http,https} \N \N {/s184-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7dcffda6-19ce-4db7-be50-9e5ffdd06661 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 73e663c8-0f96-4908-a02c-5c7eea81e327 {http,https} \N \N {/s184-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+071657de-ef68-4006-9974-ce8a5744886f 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 73e663c8-0f96-4908-a02c-5c7eea81e327 {http,https} \N \N {/s184-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+84d47d85-6298-4b1d-ab66-b732ab72c59d 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 2c40d9e2-469a-4c7a-9bcf-61552994e02e {http,https} \N \N {/s185-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+011ae483-0c29-42b3-915c-b8b422ce71b4 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 2c40d9e2-469a-4c7a-9bcf-61552994e02e {http,https} \N \N {/s185-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19c28169-42fa-4251-9828-7ce4d4b90f80 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 2c40d9e2-469a-4c7a-9bcf-61552994e02e {http,https} \N \N {/s185-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+94fafc99-fd1b-4bfc-899f-2333c776da12 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 2c40d9e2-469a-4c7a-9bcf-61552994e02e {http,https} \N \N {/s185-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f4a6e100-d1ff-4c04-b2f7-948703eadc4a 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 3e2fe25a-fc33-4a1e-a1f1-a60ac070e341 {http,https} \N \N {/s186-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1ccd126a-5a5d-4597-9c5c-16c5f1699781 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 3e2fe25a-fc33-4a1e-a1f1-a60ac070e341 {http,https} \N \N {/s186-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7737eda7-b57b-40f9-8026-001a216ea04e 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 3e2fe25a-fc33-4a1e-a1f1-a60ac070e341 {http,https} \N \N {/s186-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+85ba2b4b-f82b-4ac1-b91c-38b4ebe28d71 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 3e2fe25a-fc33-4a1e-a1f1-a60ac070e341 {http,https} \N \N {/s186-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2c8f7fe9-7eff-40e1-a8a3-3fa14bcf8d53 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a344e177-1f6e-4753-8404-a3fbd716a992 {http,https} \N \N {/s187-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7e4a7d82-b633-40dd-92b3-41d66e40fea1 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a344e177-1f6e-4753-8404-a3fbd716a992 {http,https} \N \N {/s187-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bca31da5-6c38-485a-a87d-37e374a26c9a 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a344e177-1f6e-4753-8404-a3fbd716a992 {http,https} \N \N {/s187-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+587a1fad-4cff-4059-8212-56014add501a 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N a344e177-1f6e-4753-8404-a3fbd716a992 {http,https} \N \N {/s187-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ddcbfca7-d79e-463a-8fe5-2d6c25e0bdc6 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N ababbb85-337f-4aba-9922-41daf23c2865 {http,https} \N \N {/s188-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c228af42-ba0d-4f22-a07b-e4a8319754fa 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N ababbb85-337f-4aba-9922-41daf23c2865 {http,https} \N \N {/s188-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ff9eca3c-c9ea-4876-a3b4-44d810c831b3 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N ababbb85-337f-4aba-9922-41daf23c2865 {http,https} \N \N {/s188-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+56438a1c-a5a9-444b-ba64-119dac6590b3 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N ababbb85-337f-4aba-9922-41daf23c2865 {http,https} \N \N {/s188-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+265035f5-2008-491e-9063-14b21b7fd598 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 1b075615-d2ce-4b5c-997d-729c664dc4f4 {http,https} \N \N {/s189-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b1f60ac9-cd3b-4008-8cd8-0b301fefaf14 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 1b075615-d2ce-4b5c-997d-729c664dc4f4 {http,https} \N \N {/s189-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ed245d94-3876-46e7-998d-347a6325b963 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 1b075615-d2ce-4b5c-997d-729c664dc4f4 {http,https} \N \N {/s189-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9e32fcb8-5877-458e-8f61-c375f7195da1 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 1b075615-d2ce-4b5c-997d-729c664dc4f4 {http,https} \N \N {/s189-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a9a189b0-ae27-4917-9492-011195b606d0 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N fe3e3c81-0f6c-4f7b-82d7-06022c1613b6 {http,https} \N \N {/s190-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+06f8930d-390b-4688-b733-eec262c2143b 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N fe3e3c81-0f6c-4f7b-82d7-06022c1613b6 {http,https} \N \N {/s190-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f7559e30-e6a1-4220-97e1-0d3e4d70edb7 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N fe3e3c81-0f6c-4f7b-82d7-06022c1613b6 {http,https} \N \N {/s190-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af56a77a-2cfd-4b6a-80dc-cbe9761fa839 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N fe3e3c81-0f6c-4f7b-82d7-06022c1613b6 {http,https} \N \N {/s190-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bf5f5fc9-2078-4b72-9a43-d8878340d3e5 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 54d95a23-896b-40b4-b93a-dfe4b4083a23 {http,https} \N \N {/s191-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29cff1a4-2725-40cb-98d1-cc0802bf63eb 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 54d95a23-896b-40b4-b93a-dfe4b4083a23 {http,https} \N \N {/s191-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a87bba57-0a9f-41cb-955d-e74ef7f882c5 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 54d95a23-896b-40b4-b93a-dfe4b4083a23 {http,https} \N \N {/s191-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3283a9a8-c19d-4950-9f72-9cd852a13f46 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 54d95a23-896b-40b4-b93a-dfe4b4083a23 {http,https} \N \N {/s191-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7fbb876e-75ec-4c0d-af98-c70ce26b513e 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 92af388d-d0f3-41a9-ad5f-ed90b03de869 {http,https} \N \N {/s192-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+759463d0-28af-4458-bea0-b04db67add1a 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 92af388d-d0f3-41a9-ad5f-ed90b03de869 {http,https} \N \N {/s192-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bbf3f83e-b4d4-4ad2-822b-88e8f0748df8 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 92af388d-d0f3-41a9-ad5f-ed90b03de869 {http,https} \N \N {/s192-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+71c67e7c-51b8-45d7-85a9-dbf8e9bc0a45 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 92af388d-d0f3-41a9-ad5f-ed90b03de869 {http,https} \N \N {/s192-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+53d373d4-2629-4241-a039-d1fdd751ab28 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5a61733d-2684-4d4a-9d35-bf785b7c07c2 {http,https} \N \N {/s193-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a8831701-cbd8-416f-93bc-287126315593 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5a61733d-2684-4d4a-9d35-bf785b7c07c2 {http,https} \N \N {/s193-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+44bfe0fd-07eb-4585-949c-e226c244e9d5 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5a61733d-2684-4d4a-9d35-bf785b7c07c2 {http,https} \N \N {/s193-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+46a2ea6f-6729-4318-8816-8f65e25a3cd2 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N 5a61733d-2684-4d4a-9d35-bf785b7c07c2 {http,https} \N \N {/s193-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8842606e-ccfc-4331-bff9-0d59d34ee387 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N ece058ba-4c37-48de-a640-d7b889c4fb6c {http,https} \N \N {/s194-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e3ac1e1e-1407-4df7-8436-18402735747d 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N ece058ba-4c37-48de-a640-d7b889c4fb6c {http,https} \N \N {/s194-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+94a377f9-7bd0-4634-b305-63b7e88f9ca5 2022-05-26 09:04:26+00 2022-05-26 09:04:26+00 \N ece058ba-4c37-48de-a640-d7b889c4fb6c {http,https} \N \N {/s194-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bb9b5ed3-d6c3-4cdb-9e5a-f28032574224 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ece058ba-4c37-48de-a640-d7b889c4fb6c {http,https} \N \N {/s194-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+788fc63b-5d13-41ca-8f13-87282675b88b 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c2c49d74-23c3-4ce3-a9e5-f0ede3967097 {http,https} \N \N {/s195-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+784e0624-6b13-4699-a26d-96cddfe8851c 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c2c49d74-23c3-4ce3-a9e5-f0ede3967097 {http,https} \N \N {/s195-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+209e20f0-4ea4-48f0-b275-80d6e3d88483 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c2c49d74-23c3-4ce3-a9e5-f0ede3967097 {http,https} \N \N {/s195-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a37f4e35-cac6-49d3-a0a2-c2b58f77278d 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c2c49d74-23c3-4ce3-a9e5-f0ede3967097 {http,https} \N \N {/s195-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+27c7886f-0847-4165-bbdd-601871847f68 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N fbdc551b-4550-4528-a74d-a595aa492b51 {http,https} \N \N {/s196-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de454194-9c07-4879-a465-3e194fcf4341 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N fbdc551b-4550-4528-a74d-a595aa492b51 {http,https} \N \N {/s196-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+252a3a99-c46f-4875-904e-dd82aca1777e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N fbdc551b-4550-4528-a74d-a595aa492b51 {http,https} \N \N {/s196-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d96919d-8d0e-405a-b1a2-c3d02b4b56aa 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N fbdc551b-4550-4528-a74d-a595aa492b51 {http,https} \N \N {/s196-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8fb42864-5606-43c9-b041-0273ea529965 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 92c2bcd2-bb73-4339-aaf1-8b552ceb0106 {http,https} \N \N {/s197-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7ff05871-59c1-46a4-8595-84f2bb305465 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 92c2bcd2-bb73-4339-aaf1-8b552ceb0106 {http,https} \N \N {/s197-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1884b6a1-611a-42e3-9fbe-eea1b8ca4fe4 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 92c2bcd2-bb73-4339-aaf1-8b552ceb0106 {http,https} \N \N {/s197-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9f15af83-4089-4944-bc15-a18687e442d5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 92c2bcd2-bb73-4339-aaf1-8b552ceb0106 {http,https} \N \N {/s197-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e0788586-00b1-490b-8b44-736e8db27981 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c60849dc-5675-492f-8bab-5d8cb3626823 {http,https} \N \N {/s198-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a198fe7-4cd4-4546-83f2-2b4e1e2e6ca2 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c60849dc-5675-492f-8bab-5d8cb3626823 {http,https} \N \N {/s198-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29cdcb0e-dd9c-40a5-8b57-e198c5a98f39 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c60849dc-5675-492f-8bab-5d8cb3626823 {http,https} \N \N {/s198-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9247fff8-ca66-434f-a300-e4e7db0f47c1 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N c60849dc-5675-492f-8bab-5d8cb3626823 {http,https} \N \N {/s198-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8941a60b-adeb-418d-87cb-e25d2bde5da1 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1d6aa622-24ef-4888-a080-ba20e5c89316 {http,https} \N \N {/s199-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3e8c7fc4-3828-499e-84c6-585279a856d8 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1d6aa622-24ef-4888-a080-ba20e5c89316 {http,https} \N \N {/s199-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4b9bb24-57dd-4609-b6e7-3bbf84573a6c 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1d6aa622-24ef-4888-a080-ba20e5c89316 {http,https} \N \N {/s199-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+81b2991f-886a-49ef-acb6-2e18ff7b836f 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1d6aa622-24ef-4888-a080-ba20e5c89316 {http,https} \N \N {/s199-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c410bd56-3558-45bb-9421-c80bc680bc18 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 204833b7-0070-4b55-9583-1df64dc7ab2a {http,https} \N \N {/s200-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+04f736a8-d0cf-4f12-959e-8051346306a6 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 204833b7-0070-4b55-9583-1df64dc7ab2a {http,https} \N \N {/s200-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+355ab472-684c-4dad-a464-14d223d5cf9a 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 204833b7-0070-4b55-9583-1df64dc7ab2a {http,https} \N \N {/s200-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+71b18877-0e77-46e1-831f-4145d44cce18 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 204833b7-0070-4b55-9583-1df64dc7ab2a {http,https} \N \N {/s200-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+508d3ec2-4700-4bc2-8e30-cf5b9989b37d 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2cebb659-d522-4e02-9ba6-90e09ced208c {http,https} \N \N {/s201-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b9db9172-8b7e-481c-91c5-2bba6b5592a5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2cebb659-d522-4e02-9ba6-90e09ced208c {http,https} \N \N {/s201-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+34bbdbd6-2558-4ba5-9cf6-1c43f7347358 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2cebb659-d522-4e02-9ba6-90e09ced208c {http,https} \N \N {/s201-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bf0b9b7b-d3dc-421d-aae1-ea3bc0e4f4b2 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2cebb659-d522-4e02-9ba6-90e09ced208c {http,https} \N \N {/s201-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+221c3634-abac-4c45-92e3-9cc676ab4485 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8fd65cbb-d37c-45ad-95ba-f5bb0acf87e0 {http,https} \N \N {/s202-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f18721a4-6297-4f5e-841f-69e90f94bbf1 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8fd65cbb-d37c-45ad-95ba-f5bb0acf87e0 {http,https} \N \N {/s202-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2e66ed55-4275-401e-94b3-f9d0a4e0ed0d 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8fd65cbb-d37c-45ad-95ba-f5bb0acf87e0 {http,https} \N \N {/s202-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+df1ac559-4d7d-473e-beac-eb48e6672278 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8fd65cbb-d37c-45ad-95ba-f5bb0acf87e0 {http,https} \N \N {/s202-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2b4fec1a-e43b-4ef7-bbfc-ae8c7bf57f67 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 310fe133-a807-45dc-9dd1-6a6b1fe1d07d {http,https} \N \N {/s203-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e434321d-4292-4f93-b34c-0f4a65322831 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 310fe133-a807-45dc-9dd1-6a6b1fe1d07d {http,https} \N \N {/s203-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eee19ea7-e3d3-4785-99a7-e59599e9a72a 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 310fe133-a807-45dc-9dd1-6a6b1fe1d07d {http,https} \N \N {/s203-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b0b4320f-15f5-4837-bf08-fdb852b5335c 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 310fe133-a807-45dc-9dd1-6a6b1fe1d07d {http,https} \N \N {/s203-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+198a559c-3922-4174-9f67-0cbcfced40a6 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f7df66fb-1d8f-46dc-b569-de1b63a0344b {http,https} \N \N {/s204-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d0b5c8f1-bb54-466c-bf6e-3862cdb19dfb 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f7df66fb-1d8f-46dc-b569-de1b63a0344b {http,https} \N \N {/s204-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+419939ca-5f75-4831-b957-74321322646a 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f7df66fb-1d8f-46dc-b569-de1b63a0344b {http,https} \N \N {/s204-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7611e12a-366a-42d6-9616-4c067bf76546 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f7df66fb-1d8f-46dc-b569-de1b63a0344b {http,https} \N \N {/s204-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fa1818d1-d11d-467d-88f0-b2824668b25c 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b75d1f70-93f2-4de0-9bb4-7a1fae40e29b {http,https} \N \N {/s205-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0532bb48-00cf-41a9-b651-5e10eb087bfc 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b75d1f70-93f2-4de0-9bb4-7a1fae40e29b {http,https} \N \N {/s205-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5120d4f7-8e38-4a65-9ef3-6f9492483e14 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b75d1f70-93f2-4de0-9bb4-7a1fae40e29b {http,https} \N \N {/s205-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d328af8a-b84f-4a6e-b35b-63a2e9b8dee5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b75d1f70-93f2-4de0-9bb4-7a1fae40e29b {http,https} \N \N {/s205-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5248f2f3-878b-482a-9626-670f56b6417e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N cde580a3-81d5-4cef-9858-f99a1f629422 {http,https} \N \N {/s206-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c237d2b2-8d0a-4f76-a6e0-0bc79d1eb7f6 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N cde580a3-81d5-4cef-9858-f99a1f629422 {http,https} \N \N {/s206-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9451c770-3558-4e7c-a73a-42fda3b13dbe 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N cde580a3-81d5-4cef-9858-f99a1f629422 {http,https} \N \N {/s206-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+01b6ecaa-932d-4b76-bd6b-d33ee791221e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N cde580a3-81d5-4cef-9858-f99a1f629422 {http,https} \N \N {/s206-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+227f7690-1b6f-48ed-9ba0-8de2210cf564 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ebc496df-a1c7-4046-bf99-45778c2de1c6 {http,https} \N \N {/s207-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5e941f0c-f542-4aea-b2dc-9d793f6a0080 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ebc496df-a1c7-4046-bf99-45778c2de1c6 {http,https} \N \N {/s207-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af6e9d14-8189-4b98-88a6-03c57eab6be4 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ebc496df-a1c7-4046-bf99-45778c2de1c6 {http,https} \N \N {/s207-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c156047f-6a96-4e2c-ba7f-0fa8b892c5be 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ebc496df-a1c7-4046-bf99-45778c2de1c6 {http,https} \N \N {/s207-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+03b3939d-8f6e-4df2-93d4-5c6944ffab39 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2a2d78fd-a19a-4a2c-80c1-816deb18c823 {http,https} \N \N {/s208-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1cb4051d-77e3-4292-babb-d994125c4f27 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2a2d78fd-a19a-4a2c-80c1-816deb18c823 {http,https} \N \N {/s208-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8c41b214-4ff1-4a2c-8729-9443b477ea14 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2a2d78fd-a19a-4a2c-80c1-816deb18c823 {http,https} \N \N {/s208-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9baf5a7d-d09e-4f9a-b03c-aba6c414f36e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 2a2d78fd-a19a-4a2c-80c1-816deb18c823 {http,https} \N \N {/s208-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+02ef066e-e9c3-4693-9b6c-5b877fee6859 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 88c9d8c2-1bfd-4b33-81c7-7d77866b2d7e {http,https} \N \N {/s209-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+045c6995-14d4-490c-9532-63b01ada6787 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 88c9d8c2-1bfd-4b33-81c7-7d77866b2d7e {http,https} \N \N {/s209-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f204c88-b044-44f6-bf6b-4e486b5ad64d 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 88c9d8c2-1bfd-4b33-81c7-7d77866b2d7e {http,https} \N \N {/s209-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+99d40389-5494-417b-95df-71b26c369402 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 88c9d8c2-1bfd-4b33-81c7-7d77866b2d7e {http,https} \N \N {/s209-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+56477f27-4d1c-4ea8-87b3-d34a1a408239 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 0eb52ec4-f6fc-4c6d-ac31-e07b84f7e17e {http,https} \N \N {/s210-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+60a83f05-8969-4ddd-959f-ba125750c7d8 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 0eb52ec4-f6fc-4c6d-ac31-e07b84f7e17e {http,https} \N \N {/s210-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0c3a00ab-5c5a-4091-b7f8-747d119fdbfa 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 0eb52ec4-f6fc-4c6d-ac31-e07b84f7e17e {http,https} \N \N {/s210-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+138df44c-a087-49fc-ac27-30dec071a3a5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 0eb52ec4-f6fc-4c6d-ac31-e07b84f7e17e {http,https} \N \N {/s210-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a9405b4-8b56-4562-a669-efdaa3131af8 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1c255589-3ec2-42b8-b722-32c1f9ad2510 {http,https} \N \N {/s211-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e3dbee91-2b1e-4732-ba78-a6721f1e80d5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1c255589-3ec2-42b8-b722-32c1f9ad2510 {http,https} \N \N {/s211-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+afe847ed-9bf3-4dc9-8afa-7a65c51a26af 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1c255589-3ec2-42b8-b722-32c1f9ad2510 {http,https} \N \N {/s211-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5c10847d-e99a-4683-b950-92c6adb1dee4 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 1c255589-3ec2-42b8-b722-32c1f9ad2510 {http,https} \N \N {/s211-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f8d705dc-146b-42aa-9e42-e391a7a7c1b9 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b5af350e-6e66-40e4-8333-e0595f756e83 {http,https} \N \N {/s212-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4eacd6c5-8fbc-4a2e-9fe3-bc0bee4517ee 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b5af350e-6e66-40e4-8333-e0595f756e83 {http,https} \N \N {/s212-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c99a2b48-2556-4179-8acd-06f427d86e43 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b5af350e-6e66-40e4-8333-e0595f756e83 {http,https} \N \N {/s212-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f45c9e1c-abad-4f81-910d-69ccfc347d0e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N b5af350e-6e66-40e4-8333-e0595f756e83 {http,https} \N \N {/s212-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+04626a0e-3830-4297-a445-7da2ac7bae9c 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 607a67a8-1ab1-4c96-869d-71ffc14a90cb {http,https} \N \N {/s213-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a82dbd91-76dd-471b-b6e1-9ba77984d481 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 607a67a8-1ab1-4c96-869d-71ffc14a90cb {http,https} \N \N {/s213-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dd52ccb1-ffee-4d4f-8794-ddd1c9b04c0e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 607a67a8-1ab1-4c96-869d-71ffc14a90cb {http,https} \N \N {/s213-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d59bec56-631e-4870-9053-b9aa1a8c3b16 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 607a67a8-1ab1-4c96-869d-71ffc14a90cb {http,https} \N \N {/s213-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0f5a7ee7-75c6-4055-a7c8-ea70e80ee487 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 97657a2e-8286-4638-b42b-d8f1418f68f3 {http,https} \N \N {/s214-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8ffd06db-9ca7-4071-b267-4c6ca1f217f2 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 97657a2e-8286-4638-b42b-d8f1418f68f3 {http,https} \N \N {/s214-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+33f9f90b-363e-433e-b018-74a09ff8821b 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 97657a2e-8286-4638-b42b-d8f1418f68f3 {http,https} \N \N {/s214-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+948637b6-f3ba-4e1e-a3b4-7c9023a99eb2 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 97657a2e-8286-4638-b42b-d8f1418f68f3 {http,https} \N \N {/s214-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24d84b7d-c0ac-4043-9ba5-fe93f73fb4b3 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8ebbdaa1-2ede-459c-8f20-9eaf6c4c5e34 {http,https} \N \N {/s215-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fa315997-a402-42bb-8bc8-a015c33a4ebc 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8ebbdaa1-2ede-459c-8f20-9eaf6c4c5e34 {http,https} \N \N {/s215-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a71db8e6-7adc-4672-9fa4-8c663e9ae8d5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8ebbdaa1-2ede-459c-8f20-9eaf6c4c5e34 {http,https} \N \N {/s215-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+07fa01fd-7fda-4e48-a74e-857515e2bb0a 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8ebbdaa1-2ede-459c-8f20-9eaf6c4c5e34 {http,https} \N \N {/s215-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+859bbe89-f301-40a6-b751-af71121364c9 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N dc47a6ab-1456-4e60-95d2-50b7251072be {http,https} \N \N {/s216-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+356a976d-9ca3-4dbf-b0b0-e87fb26df24d 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N dc47a6ab-1456-4e60-95d2-50b7251072be {http,https} \N \N {/s216-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+64839bb8-fcd2-4105-aa56-d779f4e37544 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N dc47a6ab-1456-4e60-95d2-50b7251072be {http,https} \N \N {/s216-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de160398-b693-49e3-8b9b-85112666f1b9 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N dc47a6ab-1456-4e60-95d2-50b7251072be {http,https} \N \N {/s216-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19ce1881-c412-4267-921a-d2cc78f8e695 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 17157627-0993-4a53-ac67-5dc31565a022 {http,https} \N \N {/s217-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cd8596e2-38e3-4c93-95e2-76d31e2a995e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 17157627-0993-4a53-ac67-5dc31565a022 {http,https} \N \N {/s217-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+886c5da0-c197-4b27-bc70-74f3b0aa087e 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 17157627-0993-4a53-ac67-5dc31565a022 {http,https} \N \N {/s217-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+620f3ede-bbc9-4123-ae29-132e9f45708b 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 17157627-0993-4a53-ac67-5dc31565a022 {http,https} \N \N {/s217-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c97c962e-854c-480b-8f91-9d8d00240165 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8456d2fa-f8ee-44c4-b062-376c225c6ad9 {http,https} \N \N {/s218-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fba47ef2-1fc3-4519-a0e5-1ac9ada2ccae 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8456d2fa-f8ee-44c4-b062-376c225c6ad9 {http,https} \N \N {/s218-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9a8fa17-af14-4a3d-968b-eb1280b461f5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8456d2fa-f8ee-44c4-b062-376c225c6ad9 {http,https} \N \N {/s218-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a49368a3-9a05-4ded-9cc5-7c609d3581e7 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 8456d2fa-f8ee-44c4-b062-376c225c6ad9 {http,https} \N \N {/s218-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+035bc257-8cb8-4883-9e3f-0e675ddd6f15 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 289e1e86-7c79-4686-910d-91d138398782 {http,https} \N \N {/s219-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ee288452-127e-4b81-8235-f459a73ad52d 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 289e1e86-7c79-4686-910d-91d138398782 {http,https} \N \N {/s219-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3d1b9b5c-855f-439b-b1e5-39879b7f1109 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 289e1e86-7c79-4686-910d-91d138398782 {http,https} \N \N {/s219-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f2d98f5-9841-46e9-a1e9-9de85a177404 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 289e1e86-7c79-4686-910d-91d138398782 {http,https} \N \N {/s219-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+45b52dc9-6a5b-419f-9aa4-c9799954814c 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ef250969-68ff-4fc9-a9f9-46f776374937 {http,https} \N \N {/s220-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d33e0b54-65db-4f26-9287-df3b8f6b25cb 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ef250969-68ff-4fc9-a9f9-46f776374937 {http,https} \N \N {/s220-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22192499-69e4-4fec-b815-19d0a1794f55 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ef250969-68ff-4fc9-a9f9-46f776374937 {http,https} \N \N {/s220-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b72fc0df-17ac-4c2d-a6ad-849b01b1aa12 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N ef250969-68ff-4fc9-a9f9-46f776374937 {http,https} \N \N {/s220-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb513101-6911-4457-a34a-a11810450c3b 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f75fa431-1d5b-4a84-adc9-f2ab778755f2 {http,https} \N \N {/s221-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e76689cf-cd5d-4c76-9a6f-ff0e6ecb40d5 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f75fa431-1d5b-4a84-adc9-f2ab778755f2 {http,https} \N \N {/s221-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d2a69105-f34a-4d03-8700-029974e4dd23 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f75fa431-1d5b-4a84-adc9-f2ab778755f2 {http,https} \N \N {/s221-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a44ab04-86a3-434f-acf5-b6742310bff6 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N f75fa431-1d5b-4a84-adc9-f2ab778755f2 {http,https} \N \N {/s221-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+605e87c1-c4b3-46c8-8a26-eaf2466a3cbc 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 395b99d4-38f4-4268-9cd0-fa6e0f2cff94 {http,https} \N \N {/s222-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e638a649-e228-448e-a43d-bb01b9595a31 2022-05-26 09:04:27+00 2022-05-26 09:04:27+00 \N 395b99d4-38f4-4268-9cd0-fa6e0f2cff94 {http,https} \N \N {/s222-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8abbf9d5-609c-42ba-9d3e-e9c465da782b 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 395b99d4-38f4-4268-9cd0-fa6e0f2cff94 {http,https} \N \N {/s222-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+644a2486-77b8-4909-a320-0b0f64f1e602 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 395b99d4-38f4-4268-9cd0-fa6e0f2cff94 {http,https} \N \N {/s222-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3eac023b-f444-4746-b50d-3cd01d728004 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N fd296ad3-4272-4acb-8246-1853ba56f38c {http,https} \N \N {/s223-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0db4c5f7-9e77-4d76-83e2-21dcbcdbcc96 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N fd296ad3-4272-4acb-8246-1853ba56f38c {http,https} \N \N {/s223-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a4c419e2-919f-40c1-aba8-0cfa522e276e 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N fd296ad3-4272-4acb-8246-1853ba56f38c {http,https} \N \N {/s223-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a93825b8-bd1d-413c-92cb-2abcaa4d0926 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N fd296ad3-4272-4acb-8246-1853ba56f38c {http,https} \N \N {/s223-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+db0adc4a-7dfe-43a4-9e74-8cbc772e8230 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 2128d33e-4e88-442c-a077-753f5bc3cfb1 {http,https} \N \N {/s224-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5fe30601-1403-452c-9b72-56d974767951 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 2128d33e-4e88-442c-a077-753f5bc3cfb1 {http,https} \N \N {/s224-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+90c8e8fc-d744-45ec-81b7-f26c60c7623d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 2128d33e-4e88-442c-a077-753f5bc3cfb1 {http,https} \N \N {/s224-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2528c78-e84e-4da8-a289-955767c7328b 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 2128d33e-4e88-442c-a077-753f5bc3cfb1 {http,https} \N \N {/s224-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c8dcbad3-f9e4-49f2-9fae-9c0cec332879 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 0e047d1b-5481-4e2e-949c-8bb2dcf9e5e9 {http,https} \N \N {/s225-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+957737e1-6569-4650-9fa7-834d2ece5bec 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 0e047d1b-5481-4e2e-949c-8bb2dcf9e5e9 {http,https} \N \N {/s225-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+86b3c74e-1c47-41e8-9b5a-6ea637769538 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 0e047d1b-5481-4e2e-949c-8bb2dcf9e5e9 {http,https} \N \N {/s225-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ddca249b-defc-47f3-acad-0f0a7e4f8617 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 0e047d1b-5481-4e2e-949c-8bb2dcf9e5e9 {http,https} \N \N {/s225-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+79ae0d64-ab90-4e9a-882e-859056d79538 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b3a256a3-3d0f-4a67-9518-dda233dab2a4 {http,https} \N \N {/s226-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2f9858d-cf8e-4b4a-a5d9-a33908ef5530 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b3a256a3-3d0f-4a67-9518-dda233dab2a4 {http,https} \N \N {/s226-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8b26c801-e3d2-4692-b594-4b69485f4ca8 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b3a256a3-3d0f-4a67-9518-dda233dab2a4 {http,https} \N \N {/s226-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eab207bd-b43b-416a-a95f-78dd707a4579 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b3a256a3-3d0f-4a67-9518-dda233dab2a4 {http,https} \N \N {/s226-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63ab9266-e6de-4b6c-8ec4-9dc035752e64 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75b76bb1-fcd9-4b1d-8a07-9c89e323838d {http,https} \N \N {/s227-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d76b3e9b-33a8-4d3e-800a-f1df30437669 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75b76bb1-fcd9-4b1d-8a07-9c89e323838d {http,https} \N \N {/s227-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+07efcc32-c3f6-4860-8753-a8a8646a0f72 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75b76bb1-fcd9-4b1d-8a07-9c89e323838d {http,https} \N \N {/s227-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e9e6a941-3daf-43bf-b592-1501baed5fb2 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75b76bb1-fcd9-4b1d-8a07-9c89e323838d {http,https} \N \N {/s227-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6880c3fa-0d24-44cd-a886-e9f9c4c58cea 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b9fd2d19-6d98-409c-822c-b53d23fc6bf4 {http,https} \N \N {/s228-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+95efeae4-1f31-4155-ba77-829f06379af1 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b9fd2d19-6d98-409c-822c-b53d23fc6bf4 {http,https} \N \N {/s228-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2544fd60-0054-42cc-8d70-dc6ec403f38c 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b9fd2d19-6d98-409c-822c-b53d23fc6bf4 {http,https} \N \N {/s228-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3033fd15-db84-4505-b9c8-5aee47497024 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b9fd2d19-6d98-409c-822c-b53d23fc6bf4 {http,https} \N \N {/s228-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dbcc9362-249a-4b74-911f-73931014f6d7 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 999a382f-59db-47a3-95e5-3c7c387e519c {http,https} \N \N {/s229-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f6c39d90-718a-4aab-817c-f808b0bebb48 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 999a382f-59db-47a3-95e5-3c7c387e519c {http,https} \N \N {/s229-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+03107345-1338-46fc-a73f-62d1d7c3b36a 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 999a382f-59db-47a3-95e5-3c7c387e519c {http,https} \N \N {/s229-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+47c87273-2924-47c6-9090-888d86b7dc81 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 999a382f-59db-47a3-95e5-3c7c387e519c {http,https} \N \N {/s229-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dee03211-607a-47f4-809a-ca7b1121acc3 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 12475fba-736b-41ef-b7c9-91f0ab42706f {http,https} \N \N {/s230-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+961a0c1c-f59b-403c-9f09-dfbe43e72f2b 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 12475fba-736b-41ef-b7c9-91f0ab42706f {http,https} \N \N {/s230-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+452ed169-607d-4df7-b01a-e7d299bf7fae 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 12475fba-736b-41ef-b7c9-91f0ab42706f {http,https} \N \N {/s230-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+88587098-6e3c-4f1f-8b78-b3ca286d6b86 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 12475fba-736b-41ef-b7c9-91f0ab42706f {http,https} \N \N {/s230-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c319290e-5fe8-4104-8ec6-4844c9518e89 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 991a0eb0-d11a-40c7-9c0c-69134e425825 {http,https} \N \N {/s231-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9b08a36d-6d73-47c0-8c08-84d9ef630b71 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 991a0eb0-d11a-40c7-9c0c-69134e425825 {http,https} \N \N {/s231-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9c3381de-39d6-4656-83b2-e363a0674564 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 991a0eb0-d11a-40c7-9c0c-69134e425825 {http,https} \N \N {/s231-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9d3c2d9a-377f-49f3-bd84-825c82b54b2a 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 991a0eb0-d11a-40c7-9c0c-69134e425825 {http,https} \N \N {/s231-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fbd49e46-42c2-42fb-8138-5e1f99b76838 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N a8911c95-832e-49cd-bbbf-adf393a69d28 {http,https} \N \N {/s232-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8d978335-6bb7-49b9-8fa7-fc28c5306d4d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N a8911c95-832e-49cd-bbbf-adf393a69d28 {http,https} \N \N {/s232-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+93d89a25-7e8f-49fc-ab7c-ba3d9900cdfe 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N a8911c95-832e-49cd-bbbf-adf393a69d28 {http,https} \N \N {/s232-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7ad486db-d9fc-4e93-b90f-9aad1ffca8c2 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N a8911c95-832e-49cd-bbbf-adf393a69d28 {http,https} \N \N {/s232-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6232efcc-cf9c-4faa-bdc0-1165995f180e 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 05d5816d-797f-4329-8693-6864ba16fa00 {http,https} \N \N {/s233-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+db2796a2-5b9f-44e4-b4e6-e1b650eac133 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 05d5816d-797f-4329-8693-6864ba16fa00 {http,https} \N \N {/s233-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9aeccec9-69c0-4095-b109-03c37c0f4102 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 05d5816d-797f-4329-8693-6864ba16fa00 {http,https} \N \N {/s233-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+601e944e-4e5b-49e8-8431-5d5a9ffbd2ef 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 05d5816d-797f-4329-8693-6864ba16fa00 {http,https} \N \N {/s233-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f02a8d6a-4494-49b4-8db7-58aa2c068de2 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b198788c-dabc-4723-aaeb-258b242f5bf7 {http,https} \N \N {/s234-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aebdeb27-1aa7-4b9c-b324-eb1444df50c8 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b198788c-dabc-4723-aaeb-258b242f5bf7 {http,https} \N \N {/s234-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+645f09bf-9e69-487d-a15f-d9b5602a100d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b198788c-dabc-4723-aaeb-258b242f5bf7 {http,https} \N \N {/s234-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e8fdd5e7-3d0f-4205-9984-194647b7815e 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N b198788c-dabc-4723-aaeb-258b242f5bf7 {http,https} \N \N {/s234-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c5748793-1bd0-4bc1-8a0b-a2addb5a8bcc 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N f827a7cb-3a5d-49dd-b15b-4a6a05c8f76c {http,https} \N \N {/s235-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+76ef03e5-c78c-45e2-a406-178b5b77a723 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N f827a7cb-3a5d-49dd-b15b-4a6a05c8f76c {http,https} \N \N {/s235-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f95ab1b-95bf-4eac-ba04-d19db0f79ae0 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N f827a7cb-3a5d-49dd-b15b-4a6a05c8f76c {http,https} \N \N {/s235-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83395d2e-05e3-4ff8-9d10-5597651975cb 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N f827a7cb-3a5d-49dd-b15b-4a6a05c8f76c {http,https} \N \N {/s235-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+990b02bb-1105-4c02-948c-5277b3423853 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 37142dfa-010c-4d0b-ae54-3285c60e177c {http,https} \N \N {/s236-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+75a4132e-b33a-4b75-bea9-66d59b6b8df1 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 37142dfa-010c-4d0b-ae54-3285c60e177c {http,https} \N \N {/s236-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+62907511-18be-4e6c-add5-baa3d4830809 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 37142dfa-010c-4d0b-ae54-3285c60e177c {http,https} \N \N {/s236-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c77aa53-ceb7-4e37-828f-39721d97fc9d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 37142dfa-010c-4d0b-ae54-3285c60e177c {http,https} \N \N {/s236-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0bf19a48-2fa5-49b8-96e1-f096f1121522 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 82375487-c356-468a-9a2a-3999121b401e {http,https} \N \N {/s237-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fff7df69-dfb4-49f3-a312-4ffc17f98e40 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 82375487-c356-468a-9a2a-3999121b401e {http,https} \N \N {/s237-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fa5a1367-d124-42a6-acf6-1efce4ac2338 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 82375487-c356-468a-9a2a-3999121b401e {http,https} \N \N {/s237-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f1913020-f42a-4fc2-83b0-d4d837548747 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 82375487-c356-468a-9a2a-3999121b401e {http,https} \N \N {/s237-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2638b337-18c2-4e96-be07-b6e989aed671 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N d15f0c0a-bce7-427d-8da1-07928f5d415b {http,https} \N \N {/s238-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d6fd3ac-73cc-4a10-bf8c-ab03ac940276 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N d15f0c0a-bce7-427d-8da1-07928f5d415b {http,https} \N \N {/s238-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a5150d0e-1090-427c-9b20-3d452576fc06 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N d15f0c0a-bce7-427d-8da1-07928f5d415b {http,https} \N \N {/s238-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+56be2967-2351-4c26-8a3e-eee4ef98a8e3 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N d15f0c0a-bce7-427d-8da1-07928f5d415b {http,https} \N \N {/s238-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7dd824b1-39f8-49a2-9509-3e2bbf05ee7e 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 24e96d1e-b429-4a11-8fd1-ec0688531b53 {http,https} \N \N {/s239-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e0de3211-d6ad-4a8c-9087-c5ceb3c42505 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 24e96d1e-b429-4a11-8fd1-ec0688531b53 {http,https} \N \N {/s239-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24f8052d-ffbc-4074-b2c6-b08699b78f44 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 24e96d1e-b429-4a11-8fd1-ec0688531b53 {http,https} \N \N {/s239-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a1c79a06-a91a-4334-82a3-f8982eaa59b4 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 24e96d1e-b429-4a11-8fd1-ec0688531b53 {http,https} \N \N {/s239-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+74bd9573-fdd0-44ef-961b-49f4e5720753 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N eea2568d-e01a-4936-a539-01988a96bda8 {http,https} \N \N {/s240-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b05b9ae2-5cc1-480e-9174-2e9459ec9846 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N eea2568d-e01a-4936-a539-01988a96bda8 {http,https} \N \N {/s240-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ff61997e-911f-4c69-b5e9-50438b72a263 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N eea2568d-e01a-4936-a539-01988a96bda8 {http,https} \N \N {/s240-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fb9ec4e2-4a04-4823-b8e7-f8ac42962fcd 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N eea2568d-e01a-4936-a539-01988a96bda8 {http,https} \N \N {/s240-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7612fda4-4889-4103-869b-77ccd865e086 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N aea5c9f3-3582-4705-be7d-88c291890572 {http,https} \N \N {/s241-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1789af00-c255-47ef-a66b-9610d239b0da 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N aea5c9f3-3582-4705-be7d-88c291890572 {http,https} \N \N {/s241-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+81100e16-0857-4023-93e8-b81d2a458027 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N aea5c9f3-3582-4705-be7d-88c291890572 {http,https} \N \N {/s241-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+da641f38-12be-45b6-a4ad-fdfcd3557b8d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N aea5c9f3-3582-4705-be7d-88c291890572 {http,https} \N \N {/s241-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8ec1ae96-b063-4a14-8d70-620ad207fe3d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 062ddf91-5330-4185-877a-f8cdc29b5580 {http,https} \N \N {/s242-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4859932-4381-43d5-ba26-356a34bae53e 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 062ddf91-5330-4185-877a-f8cdc29b5580 {http,https} \N \N {/s242-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4b70afd1-9913-44d0-9494-378d60c001b1 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 062ddf91-5330-4185-877a-f8cdc29b5580 {http,https} \N \N {/s242-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4ffcdbc7-1716-4302-8f04-8b4cef55f3ee 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 062ddf91-5330-4185-877a-f8cdc29b5580 {http,https} \N \N {/s242-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4fb8c46c-c343-4b80-8bc9-848d3d4cb24f 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 839c749b-aebf-46d3-b72b-ce58fb730dbe {http,https} \N \N {/s243-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+60cf7fdb-7492-4b8f-b2c2-70e2b6773095 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 839c749b-aebf-46d3-b72b-ce58fb730dbe {http,https} \N \N {/s243-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d5ccbc2b-75c9-401d-961b-0b0f0133f634 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 839c749b-aebf-46d3-b72b-ce58fb730dbe {http,https} \N \N {/s243-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5a2b31f4-b9c9-4137-804a-4847c23e0666 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 839c749b-aebf-46d3-b72b-ce58fb730dbe {http,https} \N \N {/s243-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+74c5ebda-098f-4ecd-9798-ed8ad5e5e9e6 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75fa1631-c22b-4234-b8e0-0e6a79d24963 {http,https} \N \N {/s244-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+86b23491-f7ea-43a0-99ee-689d43bcea35 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75fa1631-c22b-4234-b8e0-0e6a79d24963 {http,https} \N \N {/s244-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f70e67ff-9a01-46ad-8c86-4cece7c0c106 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75fa1631-c22b-4234-b8e0-0e6a79d24963 {http,https} \N \N {/s244-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af0bbd28-93b2-4307-932f-085be3944d7e 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 75fa1631-c22b-4234-b8e0-0e6a79d24963 {http,https} \N \N {/s244-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c26123d9-0316-4ed7-949f-adb9184ccc2d 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 56e78f0a-a314-4f02-865a-ccfd68eaa009 {http,https} \N \N {/s245-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4da8744-6ba4-438b-91ef-9509f195b114 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 56e78f0a-a314-4f02-865a-ccfd68eaa009 {http,https} \N \N {/s245-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+141912a4-28bb-4e85-bcd1-6af70ca57811 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 56e78f0a-a314-4f02-865a-ccfd68eaa009 {http,https} \N \N {/s245-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+35839bab-88c3-40c1-94e2-4e661a5c706c 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 56e78f0a-a314-4f02-865a-ccfd68eaa009 {http,https} \N \N {/s245-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9196182e-0c1a-495f-b6b6-b3da1974c5d1 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 11b2be65-4a17-48f2-8a23-3c377c31b8bb {http,https} \N \N {/s246-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+00d42217-ca42-43d6-a053-82dfc08fb7f0 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 11b2be65-4a17-48f2-8a23-3c377c31b8bb {http,https} \N \N {/s246-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e77e0202-6a47-41a1-99f0-eac197f7c818 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 11b2be65-4a17-48f2-8a23-3c377c31b8bb {http,https} \N \N {/s246-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0cc09072-39ef-4e3a-a8a7-4862247f40a7 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 11b2be65-4a17-48f2-8a23-3c377c31b8bb {http,https} \N \N {/s246-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2a518dd7-8340-4650-9bb4-1597f43e7a13 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 8497dff1-9e4d-4a60-b7ba-d4c8ff11af87 {http,https} \N \N {/s247-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3234090b-adb9-4881-bab1-428e85a2d33c 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 8497dff1-9e4d-4a60-b7ba-d4c8ff11af87 {http,https} \N \N {/s247-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fbfd5159-8f5a-4289-a63c-0bd42283801f 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 8497dff1-9e4d-4a60-b7ba-d4c8ff11af87 {http,https} \N \N {/s247-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0ec7d5b4-4b0b-425e-af57-8ad87f484c63 2022-05-26 09:04:28+00 2022-05-26 09:04:28+00 \N 8497dff1-9e4d-4a60-b7ba-d4c8ff11af87 {http,https} \N \N {/s247-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ea527d94-9918-41c2-a18f-fd8a891a596e 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 712a182e-b50a-4efb-a0f0-ca4fe894e577 {http,https} \N \N {/s248-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+348fd434-de19-4323-ab49-a34c9e97d29c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 712a182e-b50a-4efb-a0f0-ca4fe894e577 {http,https} \N \N {/s248-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+396a55b0-2278-4c11-82f3-3dbe12c1fa6c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 712a182e-b50a-4efb-a0f0-ca4fe894e577 {http,https} \N \N {/s248-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ff22c081-47e7-41bb-abb4-06608ba68931 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 712a182e-b50a-4efb-a0f0-ca4fe894e577 {http,https} \N \N {/s248-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5978de24-382d-4d97-8239-b9ce82c800bc 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N ab44cae8-8ac0-41f1-9671-d07d69bb4ad2 {http,https} \N \N {/s249-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+209680d5-f5ef-444b-a5a4-c41e9103c156 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N ab44cae8-8ac0-41f1-9671-d07d69bb4ad2 {http,https} \N \N {/s249-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c5502c81-af38-48d9-b723-abded1a99819 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N ab44cae8-8ac0-41f1-9671-d07d69bb4ad2 {http,https} \N \N {/s249-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eed10aa7-274d-4019-87ce-3faa9f610358 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N ab44cae8-8ac0-41f1-9671-d07d69bb4ad2 {http,https} \N \N {/s249-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ab583423-fbf6-409b-ba71-9913ef7b7559 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 86074cab-06f4-425d-b52a-7ba8958f3778 {http,https} \N \N {/s250-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+907c4250-e472-4128-9aec-54d695b1eaeb 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 86074cab-06f4-425d-b52a-7ba8958f3778 {http,https} \N \N {/s250-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f419d80c-3261-4ab7-a86c-b5ba9f07144c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 86074cab-06f4-425d-b52a-7ba8958f3778 {http,https} \N \N {/s250-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e0dbcfc1-3bf1-49f2-8646-7257b80d5bc0 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 86074cab-06f4-425d-b52a-7ba8958f3778 {http,https} \N \N {/s250-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+98feec91-b2f0-46c6-a3af-f846d3e655e6 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3342939c-cfcb-437b-9ba9-ba20845e2183 {http,https} \N \N {/s251-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9400a5c7-b5c5-47d7-ab57-1b94f5ac7a6a 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3342939c-cfcb-437b-9ba9-ba20845e2183 {http,https} \N \N {/s251-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dd14486c-840d-41e6-992f-41957c1d12fe 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3342939c-cfcb-437b-9ba9-ba20845e2183 {http,https} \N \N {/s251-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6fc2a12a-7513-49f8-b4e0-54214e094ac0 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3342939c-cfcb-437b-9ba9-ba20845e2183 {http,https} \N \N {/s251-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8b3e6e32-3f4e-4f64-a4a1-d6bd36322ccb 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N be8251f2-6fd1-4823-8bf1-bc8c7fcd04be {http,https} \N \N {/s252-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c95c793a-34a4-4f68-9d06-2218e24c482a 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N be8251f2-6fd1-4823-8bf1-bc8c7fcd04be {http,https} \N \N {/s252-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cf8b1a5a-8cf6-4046-b5d5-7f39cdf7b5f8 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N be8251f2-6fd1-4823-8bf1-bc8c7fcd04be {http,https} \N \N {/s252-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e7e735ef-8851-4914-8680-27bd81a04bde 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N be8251f2-6fd1-4823-8bf1-bc8c7fcd04be {http,https} \N \N {/s252-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ba861cca-1947-49d9-be61-489badcf3a55 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3d42dc37-596d-4996-8f00-b3c2fb6de270 {http,https} \N \N {/s253-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b42a4d96-7214-434a-a90f-334d33da57e5 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3d42dc37-596d-4996-8f00-b3c2fb6de270 {http,https} \N \N {/s253-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f16e4e16-e084-4578-aaa5-f94fadd501c1 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3d42dc37-596d-4996-8f00-b3c2fb6de270 {http,https} \N \N {/s253-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0d4e535-9ad6-488b-8e78-5134a476735c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 3d42dc37-596d-4996-8f00-b3c2fb6de270 {http,https} \N \N {/s253-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+37cca1b2-1d03-442c-a8dd-5384f083cb53 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 704f1d16-e489-41d3-8a88-ee2c5b9b603f {http,https} \N \N {/s254-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4f92532-84d6-43ad-ab14-8dbcc7cde10d 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 704f1d16-e489-41d3-8a88-ee2c5b9b603f {http,https} \N \N {/s254-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3907184e-5ca9-43b1-aa66-9067eaf30c85 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 704f1d16-e489-41d3-8a88-ee2c5b9b603f {http,https} \N \N {/s254-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+15b2956d-8a48-439a-8990-e5e3fc06f403 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 704f1d16-e489-41d3-8a88-ee2c5b9b603f {http,https} \N \N {/s254-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b598a8c8-b596-469a-bff9-3525463f70eb 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N de8247fa-8178-495c-9fdb-111b5ae55037 {http,https} \N \N {/s255-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0197fdce-600f-4d72-b8fe-e780bb59dc0c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N de8247fa-8178-495c-9fdb-111b5ae55037 {http,https} \N \N {/s255-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3b4ca02-ad86-40fa-abaf-726711527b72 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N de8247fa-8178-495c-9fdb-111b5ae55037 {http,https} \N \N {/s255-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4d74bb2f-97ef-439c-a5ee-22d0dcdcebf1 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N de8247fa-8178-495c-9fdb-111b5ae55037 {http,https} \N \N {/s255-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+96b79441-2684-402f-be0e-1b36f14ca501 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9a548e20-7aef-4cbc-b959-e1680c595689 {http,https} \N \N {/s256-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+47288119-664e-4a3d-91de-5cf2989e28fa 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9a548e20-7aef-4cbc-b959-e1680c595689 {http,https} \N \N {/s256-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+25c97166-1b72-4f15-aea6-d2727a79dabb 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9a548e20-7aef-4cbc-b959-e1680c595689 {http,https} \N \N {/s256-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6e2e11cf-0c8d-4080-b7a9-1f28c90c2dab 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9a548e20-7aef-4cbc-b959-e1680c595689 {http,https} \N \N {/s256-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fbd3a495-78e9-4175-8237-71793cfbb606 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 6d28de77-2ca4-4bb6-bc60-cd631380e860 {http,https} \N \N {/s257-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e5ae2c28-dfc5-496d-906d-7e2efc8095d0 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 6d28de77-2ca4-4bb6-bc60-cd631380e860 {http,https} \N \N {/s257-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+09c5f01c-c719-4109-954e-edaa0eb2e4fd 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 6d28de77-2ca4-4bb6-bc60-cd631380e860 {http,https} \N \N {/s257-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f431b40-da54-4986-aa34-099cccb0d1e4 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 6d28de77-2ca4-4bb6-bc60-cd631380e860 {http,https} \N \N {/s257-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6811b6b5-b2e5-4a76-b398-bdcff56d7f22 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9630e957-6d21-4127-b724-dc7be3e201c1 {http,https} \N \N {/s258-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c35cc644-49cd-4594-8de6-9a806674660c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9630e957-6d21-4127-b724-dc7be3e201c1 {http,https} \N \N {/s258-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+530b68b4-7e22-41f0-837d-809dced43422 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9630e957-6d21-4127-b724-dc7be3e201c1 {http,https} \N \N {/s258-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b2534c0d-fdb5-42c1-b908-4520e385cdbf 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 9630e957-6d21-4127-b724-dc7be3e201c1 {http,https} \N \N {/s258-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7e3aa4c5-571b-4972-828e-fa399be86501 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 439b1ab5-f5d1-4fce-b52d-b2beca2c2d6b {http,https} \N \N {/s259-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c908e9b4-8935-4f19-afd5-090326fde382 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 439b1ab5-f5d1-4fce-b52d-b2beca2c2d6b {http,https} \N \N {/s259-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+158f7d7d-a0bc-4b85-a502-8b7ad0b56eb7 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 439b1ab5-f5d1-4fce-b52d-b2beca2c2d6b {http,https} \N \N {/s259-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e55e8a17-2f7b-469a-ac79-6bd192f221de 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 439b1ab5-f5d1-4fce-b52d-b2beca2c2d6b {http,https} \N \N {/s259-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ed05f0e0-9eed-42e8-ad60-06a678b81458 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N c385836e-5c56-47a7-b3d8-2388d62b077c {http,https} \N \N {/s260-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7b2f74ba-fdc6-4f85-8e8a-983bc873478f 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N c385836e-5c56-47a7-b3d8-2388d62b077c {http,https} \N \N {/s260-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d22c9fdf-ecd5-4d4f-85b0-3ca66aaf33d9 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N c385836e-5c56-47a7-b3d8-2388d62b077c {http,https} \N \N {/s260-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+462c16fa-1946-47a9-b089-c5cc2d79ad8a 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N c385836e-5c56-47a7-b3d8-2388d62b077c {http,https} \N \N {/s260-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+824cfe79-b762-45b9-bcb1-9ba5ef3b48a5 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5e375f63-692a-4416-a031-72323da9262b {http,https} \N \N {/s261-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a850e086-415a-43d4-be5b-e4e38d8c8943 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5e375f63-692a-4416-a031-72323da9262b {http,https} \N \N {/s261-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3799dd5c-abfd-4e56-95fd-9c86b2991c2a 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5e375f63-692a-4416-a031-72323da9262b {http,https} \N \N {/s261-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+847adc5b-670d-49ec-ad2c-d52cfc908eb3 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5e375f63-692a-4416-a031-72323da9262b {http,https} \N \N {/s261-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c0af9b6f-2469-4a72-bd62-d2ba3d4e8dc4 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 15ae2d93-8e77-49a2-a00b-1f8c7bf6b5a4 {http,https} \N \N {/s262-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+02f33d77-8e08-4483-9290-84c8f9819d92 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 15ae2d93-8e77-49a2-a00b-1f8c7bf6b5a4 {http,https} \N \N {/s262-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+49c09e7f-5c33-4261-9641-c13a1b7e188c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 15ae2d93-8e77-49a2-a00b-1f8c7bf6b5a4 {http,https} \N \N {/s262-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6fe90468-23d8-439e-9adb-020fc2bca272 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 15ae2d93-8e77-49a2-a00b-1f8c7bf6b5a4 {http,https} \N \N {/s262-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0a84aada-558e-4917-a4f7-fa4c6af88c9b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b4045684-2ff9-4810-a1ca-9bd3993f7cd4 {http,https} \N \N {/s263-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+744eee8f-0e52-49cb-9561-e32f76762b2b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b4045684-2ff9-4810-a1ca-9bd3993f7cd4 {http,https} \N \N {/s263-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d8422887-12e7-401d-90a4-ba0f7c72d3c1 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b4045684-2ff9-4810-a1ca-9bd3993f7cd4 {http,https} \N \N {/s263-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5321323b-2aff-4b1d-a684-6b09daaf580d 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b4045684-2ff9-4810-a1ca-9bd3993f7cd4 {http,https} \N \N {/s263-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a55abe57-70a6-454b-b1d9-122fb86ec968 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 75d178df-1223-4f56-80b4-1bea51adfc97 {http,https} \N \N {/s264-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3b34a202-fa58-4444-bbb3-5940062b1cb6 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 75d178df-1223-4f56-80b4-1bea51adfc97 {http,https} \N \N {/s264-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+39e5eb6c-15f1-4381-88ff-52938c020ec4 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 75d178df-1223-4f56-80b4-1bea51adfc97 {http,https} \N \N {/s264-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1a80d0b3-e96f-48f6-bb94-f455498bdc7d 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 75d178df-1223-4f56-80b4-1bea51adfc97 {http,https} \N \N {/s264-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8b6916bb-cf39-4aba-9b32-5f9142dc4726 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b44e03a1-22f5-4443-ba10-921c56788bfe {http,https} \N \N {/s265-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8bc591fa-c2ed-49e1-898e-91fcf8d94cf7 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b44e03a1-22f5-4443-ba10-921c56788bfe {http,https} \N \N {/s265-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8cd3fb93-8500-4e7e-9da6-3bbcbc933be7 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b44e03a1-22f5-4443-ba10-921c56788bfe {http,https} \N \N {/s265-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3fab8b54-49fe-4951-9497-2fbf94093ac1 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N b44e03a1-22f5-4443-ba10-921c56788bfe {http,https} \N \N {/s265-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9309d452-40ea-4d41-bba6-81931aa7543c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 8577c35b-106c-418c-8b93-90decb06af58 {http,https} \N \N {/s266-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+889ac2e8-ebb9-42e0-b6f1-2ef895622fce 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 8577c35b-106c-418c-8b93-90decb06af58 {http,https} \N \N {/s266-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5c1de002-cf5a-4158-a95d-bd945093c7d8 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 8577c35b-106c-418c-8b93-90decb06af58 {http,https} \N \N {/s266-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+02b5a25d-09ad-4749-b513-4c46f628e7ff 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 8577c35b-106c-418c-8b93-90decb06af58 {http,https} \N \N {/s266-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+052bf264-63f0-4397-82a6-11e8094fa966 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 18b21a7d-7f74-48b1-b9db-9ffa2db7d904 {http,https} \N \N {/s267-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3220acdb-f816-43e7-b1dc-ff4fa95662d5 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 18b21a7d-7f74-48b1-b9db-9ffa2db7d904 {http,https} \N \N {/s267-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b3d2e5e1-b160-4da5-bd5f-c6a9a05d05cf 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 18b21a7d-7f74-48b1-b9db-9ffa2db7d904 {http,https} \N \N {/s267-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4533df68-786c-487a-9a0b-f5c2d022c6ba 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 18b21a7d-7f74-48b1-b9db-9ffa2db7d904 {http,https} \N \N {/s267-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+43a993ea-426b-43f7-a5c4-5b97b6717a14 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 62f8d892-76fb-4ef9-9b66-b0b81564bce5 {http,https} \N \N {/s268-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0ae6aca5-83ef-4006-9617-f8483bfeedc3 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 62f8d892-76fb-4ef9-9b66-b0b81564bce5 {http,https} \N \N {/s268-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+09583471-7a23-4a2b-b279-51fbfb8abd61 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 62f8d892-76fb-4ef9-9b66-b0b81564bce5 {http,https} \N \N {/s268-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c58d1ab1-a910-402b-aaf3-9b29b1794850 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 62f8d892-76fb-4ef9-9b66-b0b81564bce5 {http,https} \N \N {/s268-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5387a4b2-e8c3-4816-97bc-c7c848cd6dc2 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 08da3a9d-5fdf-47a8-be8f-ce287d2f2914 {http,https} \N \N {/s269-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b6491fbf-c90a-40cc-97a7-74ca4f088960 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 08da3a9d-5fdf-47a8-be8f-ce287d2f2914 {http,https} \N \N {/s269-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+76091a4f-6f33-41b6-8087-ca0e7911ad9f 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 08da3a9d-5fdf-47a8-be8f-ce287d2f2914 {http,https} \N \N {/s269-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f21744bf-3172-4cbe-9a5b-90b3dc3de89f 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 08da3a9d-5fdf-47a8-be8f-ce287d2f2914 {http,https} \N \N {/s269-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+43fee4de-6c96-4e33-8aeb-94f9fa66257b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N e6ff5e56-255d-440d-81df-a452a2072297 {http,https} \N \N {/s270-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+90f51228-c787-46bb-aead-6e6414ae2bc1 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N e6ff5e56-255d-440d-81df-a452a2072297 {http,https} \N \N {/s270-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+61153c6f-6bed-4d51-9f78-3ceab4b5d196 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N e6ff5e56-255d-440d-81df-a452a2072297 {http,https} \N \N {/s270-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+45a72cc0-9e6d-42d9-8d2d-21fb0c847140 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N e6ff5e56-255d-440d-81df-a452a2072297 {http,https} \N \N {/s270-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24ff427e-0332-49fa-8206-784da4ba5b08 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5d13ade8-944a-46a1-89db-e6707760f27a {http,https} \N \N {/s271-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22ff64e4-97f3-4eec-bba5-53e51f4f883b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5d13ade8-944a-46a1-89db-e6707760f27a {http,https} \N \N {/s271-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7e421a8c-8875-4594-b600-9ac94d893106 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5d13ade8-944a-46a1-89db-e6707760f27a {http,https} \N \N {/s271-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a1d24aee-f6ba-45fb-959e-57bedffa0b46 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 5d13ade8-944a-46a1-89db-e6707760f27a {http,https} \N \N {/s271-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4f824f7d-885e-42ba-9038-b4c65a7be458 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 783e864e-f9f2-410b-ae7e-f083694fd114 {http,https} \N \N {/s272-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a6c54709-dbe3-4b18-bd44-d7e8b5182d2b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 783e864e-f9f2-410b-ae7e-f083694fd114 {http,https} \N \N {/s272-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+803cf53a-4016-4648-9f0a-2f274b40093c 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 783e864e-f9f2-410b-ae7e-f083694fd114 {http,https} \N \N {/s272-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e178bef8-4f8d-47c0-bb07-ef94f4c3348b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 783e864e-f9f2-410b-ae7e-f083694fd114 {http,https} \N \N {/s272-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9148b8d2-133c-4808-8c0c-71545df3008d 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N dd29a63e-9bd9-4a46-99a2-bb4de34b390d {http,https} \N \N {/s273-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f0df146-c486-4a7c-832c-a0c5cdf656bc 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N dd29a63e-9bd9-4a46-99a2-bb4de34b390d {http,https} \N \N {/s273-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5ab69c7c-3c0f-4f0d-9100-726bf887f09f 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N dd29a63e-9bd9-4a46-99a2-bb4de34b390d {http,https} \N \N {/s273-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+01b9bbe7-7748-40ae-b2ea-9e4f641a52bb 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N dd29a63e-9bd9-4a46-99a2-bb4de34b390d {http,https} \N \N {/s273-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2c068758-6596-4aa6-8d5c-2c1461ea6b63 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N d308ba72-8ccb-4b74-bc09-c3ea91561b47 {http,https} \N \N {/s274-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be96003d-565e-4bb8-bad7-a497fe5e2e51 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N d308ba72-8ccb-4b74-bc09-c3ea91561b47 {http,https} \N \N {/s274-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+99c4664d-2e5c-4c46-9dda-4f05ef8b6e5b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N d308ba72-8ccb-4b74-bc09-c3ea91561b47 {http,https} \N \N {/s274-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7a4b03bc-df94-4d3e-8d22-a078a6539271 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N d308ba72-8ccb-4b74-bc09-c3ea91561b47 {http,https} \N \N {/s274-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7dfafca3-ad07-479a-a5ff-0ea8d931a5e8 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N bb545b0f-69e5-4dbe-8b3a-8d692e9f0465 {http,https} \N \N {/s275-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fdb5b185-b8f4-4a36-b8d1-1ee1b7ea4852 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N bb545b0f-69e5-4dbe-8b3a-8d692e9f0465 {http,https} \N \N {/s275-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9150a4ac-5b0d-40ad-aa34-5e282fa8b6f0 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N bb545b0f-69e5-4dbe-8b3a-8d692e9f0465 {http,https} \N \N {/s275-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+78a2798c-1ccc-4af8-aca8-f64dcbcf83f1 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N bb545b0f-69e5-4dbe-8b3a-8d692e9f0465 {http,https} \N \N {/s275-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9c5116d1-6f48-4666-890c-6652ade62b3b 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 09688798-b181-4282-9b47-4ea11cbed88f {http,https} \N \N {/s276-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7f4f9605-4c50-45f6-b4aa-f0376e44e6e2 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 09688798-b181-4282-9b47-4ea11cbed88f {http,https} \N \N {/s276-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a04d56c4-b5a9-4c33-8da6-d144a43d32e5 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 09688798-b181-4282-9b47-4ea11cbed88f {http,https} \N \N {/s276-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a71d07e-24ce-4435-9354-8da15daf1a6d 2022-05-26 09:04:29+00 2022-05-26 09:04:29+00 \N 09688798-b181-4282-9b47-4ea11cbed88f {http,https} \N \N {/s276-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c8587ba4-265a-477a-bad9-3bc338c6a86e 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f2f31531-6e81-4e47-8ee5-21db84a28cae {http,https} \N \N {/s277-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24855e5d-ff47-4287-adc3-6f63a3549733 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f2f31531-6e81-4e47-8ee5-21db84a28cae {http,https} \N \N {/s277-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6e3daae6-384f-4ed9-9a52-9c18db969354 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f2f31531-6e81-4e47-8ee5-21db84a28cae {http,https} \N \N {/s277-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+32435b98-a760-4f16-97e6-7561d91cb280 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f2f31531-6e81-4e47-8ee5-21db84a28cae {http,https} \N \N {/s277-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7002e942-31fc-4778-b412-47e49c6e3d70 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5718da07-3088-41a8-a8e9-56d83309d49f {http,https} \N \N {/s278-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+09e78d3a-45c5-474a-9ff6-b3b95211b3a4 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5718da07-3088-41a8-a8e9-56d83309d49f {http,https} \N \N {/s278-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+70adbf34-eda8-445a-9448-10b5100b9890 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5718da07-3088-41a8-a8e9-56d83309d49f {http,https} \N \N {/s278-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dd3ce252-9cd4-4435-abd7-43de11e0b22a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5718da07-3088-41a8-a8e9-56d83309d49f {http,https} \N \N {/s278-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24427c56-ec45-4ead-b0a0-b4e05cc8d653 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 858587ef-4507-470b-bf83-53d9d428607d {http,https} \N \N {/s279-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19214a79-a957-467d-981d-31cd3685febb 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 858587ef-4507-470b-bf83-53d9d428607d {http,https} \N \N {/s279-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+256168e2-8de7-4530-88d7-8f54e2d548d6 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 858587ef-4507-470b-bf83-53d9d428607d {http,https} \N \N {/s279-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f7c42535-085e-4731-9f29-13c9c033a3c6 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 858587ef-4507-470b-bf83-53d9d428607d {http,https} \N \N {/s279-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cc809221-dad1-4357-9525-b99a233008d9 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e838f443-11b9-47d3-952c-b29d32c47d99 {http,https} \N \N {/s280-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+90af6eaa-2435-4719-8f0c-a6072fda1ee8 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e838f443-11b9-47d3-952c-b29d32c47d99 {http,https} \N \N {/s280-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5bd96850-5f1b-47c5-9d47-970da35bb2af 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e838f443-11b9-47d3-952c-b29d32c47d99 {http,https} \N \N {/s280-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19fb4a2a-cf09-44dc-8430-85afaba6be53 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e838f443-11b9-47d3-952c-b29d32c47d99 {http,https} \N \N {/s280-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0ad8ebfd-5c52-458d-870a-f7e38ef47b22 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 3c00d6b0-b98a-4e77-a9e8-3255963487ca {http,https} \N \N {/s281-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5c8e93f6-0b19-4a01-a418-5db63980174f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 3c00d6b0-b98a-4e77-a9e8-3255963487ca {http,https} \N \N {/s281-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5801a3ce-c020-4a20-a858-d9fb576ec08e 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 3c00d6b0-b98a-4e77-a9e8-3255963487ca {http,https} \N \N {/s281-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d089c304-1bad-4a90-ab0a-f7cd9ce7e317 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 3c00d6b0-b98a-4e77-a9e8-3255963487ca {http,https} \N \N {/s281-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cc4ae031-e11a-44fe-b1c2-7ec6107639a4 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 7968fa6f-3fce-4d76-98b7-ac7e1abd5f3b {http,https} \N \N {/s282-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4567a08d-a922-42bb-a9ea-a6c143e09108 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 7968fa6f-3fce-4d76-98b7-ac7e1abd5f3b {http,https} \N \N {/s282-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b08a9de6-f0a7-482d-9ca7-f7942a3d5289 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 7968fa6f-3fce-4d76-98b7-ac7e1abd5f3b {http,https} \N \N {/s282-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e16a4ba7-c2b9-4bcc-a47b-373bd9e00aa9 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 7968fa6f-3fce-4d76-98b7-ac7e1abd5f3b {http,https} \N \N {/s282-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29dc0430-7190-492b-ac0e-f54fd1a2571e 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0215b396-4130-4073-8c0b-a994e36641fc {http,https} \N \N {/s283-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+55693b37-b38e-421a-8491-89233a1a6d31 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0215b396-4130-4073-8c0b-a994e36641fc {http,https} \N \N {/s283-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+deb4cd60-2671-4143-a1c9-fef0b689b14f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0215b396-4130-4073-8c0b-a994e36641fc {http,https} \N \N {/s283-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c3069bf3-a702-4577-b07e-3fcefaa8bb22 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0215b396-4130-4073-8c0b-a994e36641fc {http,https} \N \N {/s283-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+80197ab5-5266-421d-8472-f2ccfa566226 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 053a5358-18e8-401d-8eae-709cae78044b {http,https} \N \N {/s284-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0b74243e-23ff-41af-acbe-fbed49ceafdf 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 053a5358-18e8-401d-8eae-709cae78044b {http,https} \N \N {/s284-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8df7a1a5-1896-4c92-9090-37deb9413e0c 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 053a5358-18e8-401d-8eae-709cae78044b {http,https} \N \N {/s284-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4ff1b4c-3f5c-49cc-bfec-000f1c21f00a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 053a5358-18e8-401d-8eae-709cae78044b {http,https} \N \N {/s284-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f4a829e-3f63-471c-b46e-a58623a1291a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 645d937e-50e6-428b-a66b-b940faa02f28 {http,https} \N \N {/s285-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b6132914-ca25-4d59-ba21-2730b87f2aae 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 645d937e-50e6-428b-a66b-b940faa02f28 {http,https} \N \N {/s285-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+906b22be-2177-4fc4-a490-b61a79320e75 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 645d937e-50e6-428b-a66b-b940faa02f28 {http,https} \N \N {/s285-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f47b12f0-1a61-4bb2-a50a-d3ac3b34160f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 645d937e-50e6-428b-a66b-b940faa02f28 {http,https} \N \N {/s285-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ffc3c83f-3318-4311-99c5-8901687e1c72 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 19fa1c11-2031-49e3-8242-33a1fc7aeb18 {http,https} \N \N {/s286-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+39a060df-8013-4e5b-9309-36d901a5c48c 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 19fa1c11-2031-49e3-8242-33a1fc7aeb18 {http,https} \N \N {/s286-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+550cc2f4-a1fd-4462-96dd-2dc76b84961a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 19fa1c11-2031-49e3-8242-33a1fc7aeb18 {http,https} \N \N {/s286-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+54b1193f-3c7d-4a44-a181-d6261c68416d 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 19fa1c11-2031-49e3-8242-33a1fc7aeb18 {http,https} \N \N {/s286-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f6165dfc-6c2a-4563-85b4-3b2cff47f855 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 9832ee7f-74e0-4e0b-8897-44cfd8c7892a {http,https} \N \N {/s287-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+80bce374-42f7-4fe6-9a94-719816681ff1 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 9832ee7f-74e0-4e0b-8897-44cfd8c7892a {http,https} \N \N {/s287-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+82d780da-9228-4204-9682-36a12419dc16 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 9832ee7f-74e0-4e0b-8897-44cfd8c7892a {http,https} \N \N {/s287-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f4fac863-5143-4f04-9919-6426d950b22d 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 9832ee7f-74e0-4e0b-8897-44cfd8c7892a {http,https} \N \N {/s287-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c762421f-dc86-472e-ace2-5491e03e5d02 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0a5d0d3b-055c-4338-b19e-1fd4d196234a {http,https} \N \N {/s288-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+33e9ec41-f5ea-46df-9ec6-eb16e3f19eba 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0a5d0d3b-055c-4338-b19e-1fd4d196234a {http,https} \N \N {/s288-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d78a3acd-0653-4f05-a338-e2e38275b01f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0a5d0d3b-055c-4338-b19e-1fd4d196234a {http,https} \N \N {/s288-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0e9ad80a-cac1-43a0-b76d-92bd926edb89 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0a5d0d3b-055c-4338-b19e-1fd4d196234a {http,https} \N \N {/s288-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0702cf7d-f724-451a-8c99-a227f4a6f5e6 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 70fae9ae-8e2b-4fe7-8c2d-3c50cf88dbac {http,https} \N \N {/s289-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ee2d5b43-ec16-40e1-a0ec-b6d7e5ce8b78 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 70fae9ae-8e2b-4fe7-8c2d-3c50cf88dbac {http,https} \N \N {/s289-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5fc724a6-8c41-4d84-acbc-ab8ac58761d5 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 70fae9ae-8e2b-4fe7-8c2d-3c50cf88dbac {http,https} \N \N {/s289-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+849c6b50-03cc-4dcb-b809-e5f8873594e9 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 70fae9ae-8e2b-4fe7-8c2d-3c50cf88dbac {http,https} \N \N {/s289-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c3896e85-8096-4b89-ae83-b1eb037fc659 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 554fa44c-d64b-4501-84f6-8543e0ac1c42 {http,https} \N \N {/s290-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+64efc957-dc79-4892-bf93-08ac8dd7bbd3 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 554fa44c-d64b-4501-84f6-8543e0ac1c42 {http,https} \N \N {/s290-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c8b4f33c-c286-4080-bd26-d78dbb6b9604 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 554fa44c-d64b-4501-84f6-8543e0ac1c42 {http,https} \N \N {/s290-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cf84d710-4034-4f8f-9332-c27a23728e25 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 554fa44c-d64b-4501-84f6-8543e0ac1c42 {http,https} \N \N {/s290-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8e3ba10b-291c-4adf-a209-1511e4ca9a8f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N ff177547-b49b-4e7e-b3d9-f99ba78df0db {http,https} \N \N {/s291-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+59e68c8c-1693-441d-90fd-c9163e2acd9a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N ff177547-b49b-4e7e-b3d9-f99ba78df0db {http,https} \N \N {/s291-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+800b1149-8225-41cb-82e1-1cc4746dfac8 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N ff177547-b49b-4e7e-b3d9-f99ba78df0db {http,https} \N \N {/s291-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+543cb191-333c-4f0c-a5dc-0491916a81a9 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N ff177547-b49b-4e7e-b3d9-f99ba78df0db {http,https} \N \N {/s291-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+108314e6-e3d1-4bdb-9f32-3163cebbf5f4 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 76217b97-af15-44da-8565-39546305a786 {http,https} \N \N {/s292-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+661143eb-9b31-4c34-88c9-8200c5dfbd1f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 76217b97-af15-44da-8565-39546305a786 {http,https} \N \N {/s292-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1703ab0a-7da4-4665-ae26-cda38a06ddb6 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 76217b97-af15-44da-8565-39546305a786 {http,https} \N \N {/s292-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a22d25cc-1114-4f3a-a285-3caa4f7c1c4b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 76217b97-af15-44da-8565-39546305a786 {http,https} \N \N {/s292-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+52760e3c-9b52-4bfe-9c33-2648bc1890d1 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5f70b4d9-fcd2-4a6b-b5d5-57f603a2d936 {http,https} \N \N {/s293-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4a293abf-5d48-46b2-86f0-4c95be79be65 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5f70b4d9-fcd2-4a6b-b5d5-57f603a2d936 {http,https} \N \N {/s293-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7de8476d-620c-4d0c-835b-20673d10340b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5f70b4d9-fcd2-4a6b-b5d5-57f603a2d936 {http,https} \N \N {/s293-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+340bcd96-9ae3-4e84-b2c0-f145b9d30f7e 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 5f70b4d9-fcd2-4a6b-b5d5-57f603a2d936 {http,https} \N \N {/s293-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8133ed27-39bb-4eee-8bbc-910e77fcc5e2 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N cddf8c8a-8e68-45c7-a771-d5d2d8aca8f5 {http,https} \N \N {/s294-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c6baa05c-e9e7-4f9e-9a80-19ff337bc72b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N cddf8c8a-8e68-45c7-a771-d5d2d8aca8f5 {http,https} \N \N {/s294-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fffea5bd-246a-4cae-bbbf-496f68c32872 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N cddf8c8a-8e68-45c7-a771-d5d2d8aca8f5 {http,https} \N \N {/s294-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bb097e25-2ac2-4309-8f1d-3660da95aa2c 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N cddf8c8a-8e68-45c7-a771-d5d2d8aca8f5 {http,https} \N \N {/s294-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b5bdc259-237e-4a60-bbda-fe70889b5d6c 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f1e1ff63-b396-4ed6-9305-d4d045a2e9a7 {http,https} \N \N {/s295-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+298774f4-ddcb-4667-a502-d7f5969eff3e 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f1e1ff63-b396-4ed6-9305-d4d045a2e9a7 {http,https} \N \N {/s295-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+92d7bb01-afe4-41cb-acc3-b0e553669f84 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f1e1ff63-b396-4ed6-9305-d4d045a2e9a7 {http,https} \N \N {/s295-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+decd2289-e746-4792-9d58-ab34081fb1fe 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N f1e1ff63-b396-4ed6-9305-d4d045a2e9a7 {http,https} \N \N {/s295-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6c887363-c580-49ec-bbb8-89328640a7f7 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 22fa79c7-1a20-4b96-afbb-cac2c2c22706 {http,https} \N \N {/s296-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+da6360e8-ff98-4d8b-b008-0fc3e7676466 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 22fa79c7-1a20-4b96-afbb-cac2c2c22706 {http,https} \N \N {/s296-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fcbd76a8-cf2c-42a6-9b97-4b1f9f9d461a 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 22fa79c7-1a20-4b96-afbb-cac2c2c22706 {http,https} \N \N {/s296-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8db17f64-a079-4e82-9fbe-2908b771d6dd 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 22fa79c7-1a20-4b96-afbb-cac2c2c22706 {http,https} \N \N {/s296-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb7fc10f-a7f8-408e-8aa5-6fe29c2f7f83 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N dc31ed76-081d-4ae2-b4d3-c249a4348842 {http,https} \N \N {/s297-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+830d11fc-f539-4581-95ff-b5bc36d0771c 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N dc31ed76-081d-4ae2-b4d3-c249a4348842 {http,https} \N \N {/s297-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e351acf-98e3-45e3-9786-c6fb719ca7c2 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N dc31ed76-081d-4ae2-b4d3-c249a4348842 {http,https} \N \N {/s297-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+27b055be-d510-4d88-b119-e576273fb9e5 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N dc31ed76-081d-4ae2-b4d3-c249a4348842 {http,https} \N \N {/s297-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f4af7fd-dc45-4a09-aeb1-af0e3c20ea91 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 6331cb28-6a75-45e7-9d9d-7225d0996e0f {http,https} \N \N {/s298-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eea50a61-12a9-41e2-92b0-a294e830df8b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 6331cb28-6a75-45e7-9d9d-7225d0996e0f {http,https} \N \N {/s298-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cecb910c-ced0-4ed2-b726-e09de4370d33 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 6331cb28-6a75-45e7-9d9d-7225d0996e0f {http,https} \N \N {/s298-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0770314d-25f6-4226-b66b-64e2b9088793 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 6331cb28-6a75-45e7-9d9d-7225d0996e0f {http,https} \N \N {/s298-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+96d99bd3-b8b8-4e6b-9e3c-65bba71819f9 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N d9a841c6-6bf4-4cd6-921c-f38e9f772cb0 {http,https} \N \N {/s299-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c47c5c78-11dd-45c5-825b-afc89d4d19b1 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N d9a841c6-6bf4-4cd6-921c-f38e9f772cb0 {http,https} \N \N {/s299-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8e5d4e58-0ee9-4ab1-9768-641774ba20bd 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N d9a841c6-6bf4-4cd6-921c-f38e9f772cb0 {http,https} \N \N {/s299-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b6f97875-7d88-4499-9965-a700fb1821ce 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N d9a841c6-6bf4-4cd6-921c-f38e9f772cb0 {http,https} \N \N {/s299-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3031ee2c-3cbf-4eb5-982d-54ef84e30031 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 49b9e591-2b39-4cca-b0ad-94880347cb6e {http,https} \N \N {/s300-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31e86c57-baa0-4709-83ed-a486ce4ecf6f 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 49b9e591-2b39-4cca-b0ad-94880347cb6e {http,https} \N \N {/s300-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+56f299a5-8df3-4c31-ab8e-5c9a0512f325 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 49b9e591-2b39-4cca-b0ad-94880347cb6e {http,https} \N \N {/s300-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e72a3c50-d2b3-4d63-a4de-b8d280e3fffa 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 49b9e591-2b39-4cca-b0ad-94880347cb6e {http,https} \N \N {/s300-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+539ab917-81ee-46ca-9f90-3cb110bcebd7 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 50d5126f-ed18-4022-a93a-3fee8b5a2a61 {http,https} \N \N {/s301-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2d08cf1-a499-48b4-af7f-56c1ab22d28b 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 50d5126f-ed18-4022-a93a-3fee8b5a2a61 {http,https} \N \N {/s301-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be46c66d-667c-4832-8b7e-2d2145ffe5e3 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 50d5126f-ed18-4022-a93a-3fee8b5a2a61 {http,https} \N \N {/s301-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+57033331-e8db-4919-bd23-2c289503ed70 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 50d5126f-ed18-4022-a93a-3fee8b5a2a61 {http,https} \N \N {/s301-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cbdd3bf7-2a83-4358-bb6b-31848887868d 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e1e1f82a-936b-49d0-8d28-ebab1f134a1b {http,https} \N \N {/s302-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+25c8e254-9fdc-4d75-b57e-f0120d3b144e 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e1e1f82a-936b-49d0-8d28-ebab1f134a1b {http,https} \N \N {/s302-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+55c08559-fd0b-414f-8b9c-a8ac6047b405 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e1e1f82a-936b-49d0-8d28-ebab1f134a1b {http,https} \N \N {/s302-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+479f54bd-2893-41d2-910d-c8bda2e94242 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N e1e1f82a-936b-49d0-8d28-ebab1f134a1b {http,https} \N \N {/s302-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e45c75a8-657a-47dc-adb3-55926af9c3b2 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N b5815188-d327-4734-ad11-6bd6459b38a4 {http,https} \N \N {/s303-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a0da43c6-ce4d-4513-897e-61fa95f64d8d 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N b5815188-d327-4734-ad11-6bd6459b38a4 {http,https} \N \N {/s303-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+72924912-c284-4596-83c5-c303451001a4 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N b5815188-d327-4734-ad11-6bd6459b38a4 {http,https} \N \N {/s303-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aff8a5c9-cb02-4c1b-a86c-07ebd6e0bdfd 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N b5815188-d327-4734-ad11-6bd6459b38a4 {http,https} \N \N {/s303-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+14813123-4ed3-4b6e-91db-f1b5ac038a73 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0808e339-4431-4419-8c80-0bd658eb351a {http,https} \N \N {/s304-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+741feecc-e331-42aa-a661-8e5ed487ee62 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0808e339-4431-4419-8c80-0bd658eb351a {http,https} \N \N {/s304-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+248aa6cc-0725-44da-9dbb-4b7c5850d634 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0808e339-4431-4419-8c80-0bd658eb351a {http,https} \N \N {/s304-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+12946059-37ad-4979-8272-354cf58d5617 2022-05-26 09:04:30+00 2022-05-26 09:04:30+00 \N 0808e339-4431-4419-8c80-0bd658eb351a {http,https} \N \N {/s304-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c31e50a3-ec4f-4a24-a968-525dbb636fa3 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8e7cf859-20b8-46cf-a515-89cff33cbaf3 {http,https} \N \N {/s305-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f24e9f9b-3d61-4cb2-9d02-d158ec53d880 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8e7cf859-20b8-46cf-a515-89cff33cbaf3 {http,https} \N \N {/s305-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+07a39fd9-7a46-4b38-936a-2fd9762aa789 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8e7cf859-20b8-46cf-a515-89cff33cbaf3 {http,https} \N \N {/s305-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c8b3744-685d-484e-af02-c1ad1eb3556a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8e7cf859-20b8-46cf-a515-89cff33cbaf3 {http,https} \N \N {/s305-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3414b762-ca82-403e-aaa3-8249c2ecf248 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 876e891f-4820-4e1d-96d5-d86cb4ecedc1 {http,https} \N \N {/s306-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+79d62324-4aa7-42d7-a4ae-03379f54844c 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 876e891f-4820-4e1d-96d5-d86cb4ecedc1 {http,https} \N \N {/s306-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4c306453-1d74-4983-a358-50f6ab589901 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 876e891f-4820-4e1d-96d5-d86cb4ecedc1 {http,https} \N \N {/s306-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1545b9ce-91da-4760-82c0-21daf92b82fd 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 876e891f-4820-4e1d-96d5-d86cb4ecedc1 {http,https} \N \N {/s306-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e9a04683-e583-4767-b401-be4b21716993 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 84c6bde5-724f-4beb-b1c0-16f07b948029 {http,https} \N \N {/s307-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29486f34-fe2d-42ea-ae8e-997eec09d113 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 84c6bde5-724f-4beb-b1c0-16f07b948029 {http,https} \N \N {/s307-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0dd87c7-c38f-4f5d-bf09-840a303d8c5a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 84c6bde5-724f-4beb-b1c0-16f07b948029 {http,https} \N \N {/s307-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2edb7b00-f7dd-47d4-941e-f2ad940eafda 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 84c6bde5-724f-4beb-b1c0-16f07b948029 {http,https} \N \N {/s307-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+097b64d5-e821-402f-841b-6193a92adbc2 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f612ff85-e276-47b3-a33a-63499962253d {http,https} \N \N {/s308-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+58cc4cf6-04fb-40f0-9e5a-2dbf033e935b 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f612ff85-e276-47b3-a33a-63499962253d {http,https} \N \N {/s308-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+00d5dc17-89b3-4060-b289-517b17d16a12 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f612ff85-e276-47b3-a33a-63499962253d {http,https} \N \N {/s308-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+11a89492-7e21-469d-990d-6f6e5a0da418 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f612ff85-e276-47b3-a33a-63499962253d {http,https} \N \N {/s308-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+868da3e1-521e-4a2d-b4ba-74aa35e5e67a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0e58f9e2-049c-413c-9053-520742687a6e {http,https} \N \N {/s309-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4f233cfb-63f9-41f6-a15d-c26c0000d759 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0e58f9e2-049c-413c-9053-520742687a6e {http,https} \N \N {/s309-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+32f2826c-4afd-40f1-b5a2-858053a33cc7 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0e58f9e2-049c-413c-9053-520742687a6e {http,https} \N \N {/s309-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a85d4c37-8534-4331-a60b-986ea8b76ef2 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0e58f9e2-049c-413c-9053-520742687a6e {http,https} \N \N {/s309-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+99efc0da-21fb-4849-81c5-306cd0387caf 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 82a6fb35-6254-4f5b-8aa7-c0472632af47 {http,https} \N \N {/s310-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dfcc93dd-3dcd-4f2e-81f3-087bde70a6b5 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 82a6fb35-6254-4f5b-8aa7-c0472632af47 {http,https} \N \N {/s310-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b77ed2e4-f97b-45b4-b228-9aacf868f9bb 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 82a6fb35-6254-4f5b-8aa7-c0472632af47 {http,https} \N \N {/s310-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29fdf619-528e-4511-a46c-2109bab3a761 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 82a6fb35-6254-4f5b-8aa7-c0472632af47 {http,https} \N \N {/s310-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5303abb3-dbf4-4a19-a26c-ef9e7182b975 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 258d783d-9e92-48d2-ace4-861cb00df9b7 {http,https} \N \N {/s311-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2b021031-bb05-4c39-8405-fabc1b056cfe 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 258d783d-9e92-48d2-ace4-861cb00df9b7 {http,https} \N \N {/s311-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+420b4aac-5fe1-42af-8293-b3e9994ec2d8 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 258d783d-9e92-48d2-ace4-861cb00df9b7 {http,https} \N \N {/s311-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2355e36d-d82c-4a31-824e-186affeef2c8 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 258d783d-9e92-48d2-ace4-861cb00df9b7 {http,https} \N \N {/s311-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+048c4888-dc42-424b-803b-251a79f0827a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N bd5dcc38-1fc4-49c0-80e2-f26fa6a49a9f {http,https} \N \N {/s312-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+676716b3-b615-4e49-9571-fc2ccd13937a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N bd5dcc38-1fc4-49c0-80e2-f26fa6a49a9f {http,https} \N \N {/s312-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ab6f70c-6e28-4e24-934b-4bc0c4f30be1 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N bd5dcc38-1fc4-49c0-80e2-f26fa6a49a9f {http,https} \N \N {/s312-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c01b7bce-2012-4680-a2c6-cb979ac95931 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N bd5dcc38-1fc4-49c0-80e2-f26fa6a49a9f {http,https} \N \N {/s312-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e32e7206-4b81-433f-818f-3d47b31edd31 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 1e5ab1ef-87e3-4ebc-92e9-ec9c0f7aaa9f {http,https} \N \N {/s313-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9f23478-4aec-495c-8d12-c69f7d7987f6 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 1e5ab1ef-87e3-4ebc-92e9-ec9c0f7aaa9f {http,https} \N \N {/s313-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6b0a7fcb-9f01-4179-b691-0b1479481014 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 1e5ab1ef-87e3-4ebc-92e9-ec9c0f7aaa9f {http,https} \N \N {/s313-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e5642783-b3f2-4220-b24b-711595a92acf 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 1e5ab1ef-87e3-4ebc-92e9-ec9c0f7aaa9f {http,https} \N \N {/s313-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18d225b8-c01d-4f2f-8edd-fb3c26e305da 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5e35d3e9-49a9-4976-a638-4e6764ccd426 {http,https} \N \N {/s314-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2cd01762-1180-4c1c-871b-651aeb203c3c 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5e35d3e9-49a9-4976-a638-4e6764ccd426 {http,https} \N \N {/s314-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+73d9575e-ac4d-4c46-8b12-d1f2958f2cdf 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5e35d3e9-49a9-4976-a638-4e6764ccd426 {http,https} \N \N {/s314-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bb5174a5-5337-4a6a-9e57-70a14ce2682f 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5e35d3e9-49a9-4976-a638-4e6764ccd426 {http,https} \N \N {/s314-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+03b928eb-3a70-4949-8811-07129921837a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 7bab5fa6-6191-49b8-9c7e-8addeb144e8a {http,https} \N \N {/s315-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+36140aad-79a9-4198-8007-c5c94f31ecdd 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 7bab5fa6-6191-49b8-9c7e-8addeb144e8a {http,https} \N \N {/s315-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31e9dc47-a7ac-451e-bfdd-fd4e3491fdda 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 7bab5fa6-6191-49b8-9c7e-8addeb144e8a {http,https} \N \N {/s315-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d9c548e4-288c-4ecf-b9cd-73652e6e689b 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 7bab5fa6-6191-49b8-9c7e-8addeb144e8a {http,https} \N \N {/s315-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4424a33d-98da-4246-9ccb-200ff9f62ce3 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 9bd52aa4-7158-4d06-81f2-a10f99e33f08 {http,https} \N \N {/s316-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5661013c-e421-43c6-ab2e-ae64587f46e2 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 9bd52aa4-7158-4d06-81f2-a10f99e33f08 {http,https} \N \N {/s316-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+39e23428-ae1f-4cf7-bb56-ce6f4f08defc 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 9bd52aa4-7158-4d06-81f2-a10f99e33f08 {http,https} \N \N {/s316-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+82da3fbd-0483-41f8-af41-fd3f4c87d071 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 9bd52aa4-7158-4d06-81f2-a10f99e33f08 {http,https} \N \N {/s316-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f1543a8c-08aa-4c3a-bde9-c1cd187e0779 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N b26027f8-6fc2-46c7-aef7-d9cd67fbffe3 {http,https} \N \N {/s317-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+793df1e0-6ab6-4fe9-907c-d18863bbeccf 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N b26027f8-6fc2-46c7-aef7-d9cd67fbffe3 {http,https} \N \N {/s317-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+437f872b-bd08-43f5-b957-169c2148f932 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N b26027f8-6fc2-46c7-aef7-d9cd67fbffe3 {http,https} \N \N {/s317-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a228df4-32da-4fd7-9093-984ddf1a3c70 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N b26027f8-6fc2-46c7-aef7-d9cd67fbffe3 {http,https} \N \N {/s317-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a2121b71-4355-49f9-9102-95339015122d 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c00f7722-3c3f-498d-9808-cd4a86007958 {http,https} \N \N {/s318-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8c9b468b-2bdb-4700-b0e1-f798138e79e7 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c00f7722-3c3f-498d-9808-cd4a86007958 {http,https} \N \N {/s318-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3fe8c5d-8307-4885-8654-abcbf4817871 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c00f7722-3c3f-498d-9808-cd4a86007958 {http,https} \N \N {/s318-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ba06f51b-4793-408d-8695-3382f4fe7ee1 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c00f7722-3c3f-498d-9808-cd4a86007958 {http,https} \N \N {/s318-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cde5fa67-134f-46b8-93dc-aba56caee17e 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c512e792-661f-4223-bc9d-6a9c059a4a09 {http,https} \N \N {/s319-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1150a88b-b145-42d6-8d45-06d7f0afbcfe 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c512e792-661f-4223-bc9d-6a9c059a4a09 {http,https} \N \N {/s319-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a7ab5648-327f-4203-a4df-5d3c99d5ad19 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c512e792-661f-4223-bc9d-6a9c059a4a09 {http,https} \N \N {/s319-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc17decd-87f7-47ce-b199-6639f4995f01 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N c512e792-661f-4223-bc9d-6a9c059a4a09 {http,https} \N \N {/s319-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b3ee9bb9-f6ec-4e45-a09d-19e3dd69a786 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5f154afd-4a66-4d1a-be2a-15354ad499fa {http,https} \N \N {/s320-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+79f14f9b-ffeb-48ef-8827-6e5c1822e974 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5f154afd-4a66-4d1a-be2a-15354ad499fa {http,https} \N \N {/s320-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63c8682f-c030-4621-ae98-85a669e33b8c 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5f154afd-4a66-4d1a-be2a-15354ad499fa {http,https} \N \N {/s320-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ce713b63-fae7-4384-a7c8-305a3bfea60a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5f154afd-4a66-4d1a-be2a-15354ad499fa {http,https} \N \N {/s320-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d8d2ebe1-78c7-40d3-8077-90adbc27feb3 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6226f972-df24-4f54-a21d-e90352622724 {http,https} \N \N {/s321-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0317094-0e83-474b-843f-9870f893c2fb 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6226f972-df24-4f54-a21d-e90352622724 {http,https} \N \N {/s321-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1c79b425-d3be-482b-9bfa-33f6952d3dd1 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6226f972-df24-4f54-a21d-e90352622724 {http,https} \N \N {/s321-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c72a5c27-f8ab-4b26-82b4-2229aa4e9fdd 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6226f972-df24-4f54-a21d-e90352622724 {http,https} \N \N {/s321-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+66f98d94-be19-48bb-9922-c987e915554a 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6337f622-dad3-40f7-9a25-acd776963042 {http,https} \N \N {/s322-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc871827-aa4c-4ad2-89c1-3b6109cf4899 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6337f622-dad3-40f7-9a25-acd776963042 {http,https} \N \N {/s322-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+97d92c9e-7903-4d72-8896-466e0e4072ae 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6337f622-dad3-40f7-9a25-acd776963042 {http,https} \N \N {/s322-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e1b25673-e1a1-45a3-95f5-5b65085e0a54 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6337f622-dad3-40f7-9a25-acd776963042 {http,https} \N \N {/s322-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+04de7c11-54f1-4c5d-9383-d9e8f6b44fb1 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f60b096f-1249-4270-80eb-b451330fc934 {http,https} \N \N {/s323-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d318c2c-335b-4327-a803-bd2d3990809c 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f60b096f-1249-4270-80eb-b451330fc934 {http,https} \N \N {/s323-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2d7326f-8b77-4aaa-ade9-c32fa392c14b 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f60b096f-1249-4270-80eb-b451330fc934 {http,https} \N \N {/s323-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3639b575-8aae-4dbe-8b59-d28cfa657bf6 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N f60b096f-1249-4270-80eb-b451330fc934 {http,https} \N \N {/s323-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+198d8756-5382-46bc-bbd0-47e5ad06bc52 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6f477457-1329-4c51-b556-9ab27a341116 {http,https} \N \N {/s324-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1ddd25d8-8b51-47ed-9d18-4aa3464b354e 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6f477457-1329-4c51-b556-9ab27a341116 {http,https} \N \N {/s324-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7f513acc-043e-4c75-a0b2-69fe81b8b812 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6f477457-1329-4c51-b556-9ab27a341116 {http,https} \N \N {/s324-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18508143-177a-40da-a5c8-09ecef14a2a5 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 6f477457-1329-4c51-b556-9ab27a341116 {http,https} \N \N {/s324-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a6d3ff8-ae12-4a16-85ce-6100a247d772 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ba259465-73c0-4035-af03-083de17865cd {http,https} \N \N {/s325-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+40227b2c-3f97-4011-b988-221639bf3d48 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ba259465-73c0-4035-af03-083de17865cd {http,https} \N \N {/s325-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3af767f5-9621-4b5f-ac21-0c73acfe9745 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ba259465-73c0-4035-af03-083de17865cd {http,https} \N \N {/s325-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+adda8361-8dca-47de-89e6-e91a4656b4cc 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ba259465-73c0-4035-af03-083de17865cd {http,https} \N \N {/s325-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f67126dc-9d64-4783-9ce4-8362e27ed727 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ad7ba3c6-8d4c-4f5e-9c8b-58b6b7bc2b42 {http,https} \N \N {/s326-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c5a88724-319f-4343-8f85-7309da59a872 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ad7ba3c6-8d4c-4f5e-9c8b-58b6b7bc2b42 {http,https} \N \N {/s326-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1649bdcd-4ac7-4f3f-92b9-f0f66eb2f86f 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ad7ba3c6-8d4c-4f5e-9c8b-58b6b7bc2b42 {http,https} \N \N {/s326-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a92886db-a118-44a4-9f2d-7ba57b0b2738 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N ad7ba3c6-8d4c-4f5e-9c8b-58b6b7bc2b42 {http,https} \N \N {/s326-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+750bdcc4-274b-457d-9168-39a6bc928198 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N a3caefa8-c914-44c0-ab20-e5420eef9025 {http,https} \N \N {/s327-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de3129b4-0c83-4f00-aa2d-7f8287abce50 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N a3caefa8-c914-44c0-ab20-e5420eef9025 {http,https} \N \N {/s327-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+10ef3ef9-6413-44e5-9aef-9291d3e840fe 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N a3caefa8-c914-44c0-ab20-e5420eef9025 {http,https} \N \N {/s327-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+503c8713-668f-4a2d-9f94-9a46e3b5967c 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N a3caefa8-c914-44c0-ab20-e5420eef9025 {http,https} \N \N {/s327-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d6cba0ec-6b78-4d44-9559-01cef7091a1d 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N dadc0a91-472d-4792-9b8e-d573a52b9056 {http,https} \N \N {/s328-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fc7c8f9b-b54b-441e-9887-dcb2b9a695d7 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N dadc0a91-472d-4792-9b8e-d573a52b9056 {http,https} \N \N {/s328-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+58c681ca-8422-4499-89ae-24420f7b29ca 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N dadc0a91-472d-4792-9b8e-d573a52b9056 {http,https} \N \N {/s328-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7f7bdd6c-b21d-4c17-88d5-9ace430f23aa 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N dadc0a91-472d-4792-9b8e-d573a52b9056 {http,https} \N \N {/s328-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dd4fea37-feb9-48f9-9f2c-93f35cffac45 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8b00c8a1-b680-492a-87eb-350ca72bc616 {http,https} \N \N {/s329-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+754ea9fd-6de2-4197-b05f-71ceb322da23 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8b00c8a1-b680-492a-87eb-350ca72bc616 {http,https} \N \N {/s329-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2ec5d03e-977a-413c-8383-337a5d5f246d 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8b00c8a1-b680-492a-87eb-350ca72bc616 {http,https} \N \N {/s329-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f77dddbc-7ae4-46f2-8aa9-c97d2ab68ac6 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 8b00c8a1-b680-492a-87eb-350ca72bc616 {http,https} \N \N {/s329-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+14e35303-2a3a-4356-9396-088d64a291de 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 24fe112c-a8ae-4ee0-9abf-b5d8a8a61f65 {http,https} \N \N {/s330-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+507f239e-efd7-431f-a9cb-6536507e50bb 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 24fe112c-a8ae-4ee0-9abf-b5d8a8a61f65 {http,https} \N \N {/s330-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+febd9dd3-9ed7-4033-b773-f55a43662a35 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 24fe112c-a8ae-4ee0-9abf-b5d8a8a61f65 {http,https} \N \N {/s330-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eac29fc8-3b05-4e07-93ac-d4949d5f3530 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 24fe112c-a8ae-4ee0-9abf-b5d8a8a61f65 {http,https} \N \N {/s330-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f5a74f0f-cd5e-4bfe-ba82-f5b9e13ecef3 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 33da5233-b9f0-4d03-964e-10a619eaa459 {http,https} \N \N {/s331-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f9c9cff-5f6f-4cd6-b5f2-1ec0e618500d 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 33da5233-b9f0-4d03-964e-10a619eaa459 {http,https} \N \N {/s331-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ccadb9e5-aea4-494a-88f4-e8ecce7d784d 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 33da5233-b9f0-4d03-964e-10a619eaa459 {http,https} \N \N {/s331-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dec88f5c-fcd5-4f43-aae3-4bfa0c7594ce 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 33da5233-b9f0-4d03-964e-10a619eaa459 {http,https} \N \N {/s331-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6324fd00-fa16-49f1-ba13-00debc458046 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0158712b-2d90-482a-8ca0-5c4dfdf19d42 {http,https} \N \N {/s332-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cb240526-52a4-494d-a42d-6a6a69940187 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0158712b-2d90-482a-8ca0-5c4dfdf19d42 {http,https} \N \N {/s332-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3e813626-59d3-4451-8742-932fad93398b 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0158712b-2d90-482a-8ca0-5c4dfdf19d42 {http,https} \N \N {/s332-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e10f9d2b-3688-4733-b20f-9148e630e180 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 0158712b-2d90-482a-8ca0-5c4dfdf19d42 {http,https} \N \N {/s332-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+82e71568-41d7-423e-9ca3-922f02f84408 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 91dbc846-4c2b-48f0-a5a4-651c884f2b5b {http,https} \N \N {/s333-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1d78522a-1f35-4d87-adba-dbc350f2274b 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 91dbc846-4c2b-48f0-a5a4-651c884f2b5b {http,https} \N \N {/s333-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+127c5217-b863-491a-b278-0c2291ccc7f5 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 91dbc846-4c2b-48f0-a5a4-651c884f2b5b {http,https} \N \N {/s333-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+35eafcb0-8512-46d4-aa8f-e173107a1604 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 91dbc846-4c2b-48f0-a5a4-651c884f2b5b {http,https} \N \N {/s333-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a7b427b2-ab87-45d4-bf66-c3c4857dc331 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5a2fb39c-5e8a-42ce-bcbe-a84fa6e4d12d {http,https} \N \N {/s334-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e5759747-a131-4a73-b7f9-a03fa2ae1542 2022-05-26 09:04:31+00 2022-05-26 09:04:31+00 \N 5a2fb39c-5e8a-42ce-bcbe-a84fa6e4d12d {http,https} \N \N {/s334-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+96eaa515-48ba-42cb-b9c9-6448b0dddde2 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5a2fb39c-5e8a-42ce-bcbe-a84fa6e4d12d {http,https} \N \N {/s334-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19096cc7-43da-43c6-9817-8cf391e805c4 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 5a2fb39c-5e8a-42ce-bcbe-a84fa6e4d12d {http,https} \N \N {/s334-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+94a6ef7b-5d4e-4417-902b-e65c02e552fd 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4994d988-d33f-46ae-bec1-f59018f68103 {http,https} \N \N {/s335-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d9382dc-6cca-457a-ab74-3547df4bc9bf 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4994d988-d33f-46ae-bec1-f59018f68103 {http,https} \N \N {/s335-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+64c65c94-5e4f-496b-906c-7612184fb954 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4994d988-d33f-46ae-bec1-f59018f68103 {http,https} \N \N {/s335-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0f5c296c-5db7-493a-beef-c1b94d484c30 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4994d988-d33f-46ae-bec1-f59018f68103 {http,https} \N \N {/s335-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19e0422c-4dc7-4174-b935-fd2774cf6c48 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 3d398236-c1e0-4051-9845-39c6d0d4b547 {http,https} \N \N {/s336-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a725261e-63d1-4f30-a0a9-3dfe9297690f 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 3d398236-c1e0-4051-9845-39c6d0d4b547 {http,https} \N \N {/s336-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4434fce-c6da-45d0-9f69-5cb90f2a009b 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 3d398236-c1e0-4051-9845-39c6d0d4b547 {http,https} \N \N {/s336-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6ba3547d-789e-4f0e-92fe-cbe4c76514b9 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 3d398236-c1e0-4051-9845-39c6d0d4b547 {http,https} \N \N {/s336-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d721787a-9a7e-4237-b879-4aa533d4ff28 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2d0e93c-d371-4a4e-a0c8-f30530c873ab {http,https} \N \N {/s337-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a544f08-0d44-41a9-8116-64eb634a3ceb 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2d0e93c-d371-4a4e-a0c8-f30530c873ab {http,https} \N \N {/s337-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9445a380-80c9-494a-86b9-c0e7b34a159e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2d0e93c-d371-4a4e-a0c8-f30530c873ab {http,https} \N \N {/s337-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b0024ab6-3a6f-4385-8112-b563885e71c5 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2d0e93c-d371-4a4e-a0c8-f30530c873ab {http,https} \N \N {/s337-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2ca93712-d2aa-4861-a69c-8cd7e9decc83 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N ecea8625-a170-4648-b363-e132983ebbcf {http,https} \N \N {/s338-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0f5014ca-782c-4f5a-91c6-5c08dbdc4a5c 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N ecea8625-a170-4648-b363-e132983ebbcf {http,https} \N \N {/s338-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dfa56ed7-daee-4551-a413-905d5cd62469 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N ecea8625-a170-4648-b363-e132983ebbcf {http,https} \N \N {/s338-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+483946bc-6626-4d44-a006-87f6ef0741f3 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N ecea8625-a170-4648-b363-e132983ebbcf {http,https} \N \N {/s338-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+606d55cd-f09c-40a9-8308-37046318b700 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N bfb8643d-7f56-4d95-b2a7-cce9f6a75598 {http,https} \N \N {/s339-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+58ee5bf2-860d-4c46-9c99-228b0038ccba 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N bfb8643d-7f56-4d95-b2a7-cce9f6a75598 {http,https} \N \N {/s339-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+517c94e8-f100-448e-ad63-cdfb3ac4b5dd 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N bfb8643d-7f56-4d95-b2a7-cce9f6a75598 {http,https} \N \N {/s339-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cbadd587-dbca-4c78-86e1-6d9da547d827 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N bfb8643d-7f56-4d95-b2a7-cce9f6a75598 {http,https} \N \N {/s339-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e605c81b-cdce-4efa-b181-dc5933eccbda 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 93947ca9-1278-4b68-bf9a-3be07d766959 {http,https} \N \N {/s340-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+52f3205e-aaaf-4c1f-93e2-b9ed8e195cba 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 93947ca9-1278-4b68-bf9a-3be07d766959 {http,https} \N \N {/s340-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9083933c-c9c8-44de-bc93-3ade3cf235b8 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 93947ca9-1278-4b68-bf9a-3be07d766959 {http,https} \N \N {/s340-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+12fcf5fb-fc25-4b3c-a9cd-156c75b713a9 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 93947ca9-1278-4b68-bf9a-3be07d766959 {http,https} \N \N {/s340-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b25cab50-de05-4726-bde6-ac6e23f78ecd 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N b81aaca3-eebf-4445-8bd9-f803b8b54551 {http,https} \N \N {/s341-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8d9ca2e3-c577-4134-86b7-e823e6b73e59 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N b81aaca3-eebf-4445-8bd9-f803b8b54551 {http,https} \N \N {/s341-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2322db41-34c9-412e-a702-002bc316e023 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N b81aaca3-eebf-4445-8bd9-f803b8b54551 {http,https} \N \N {/s341-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5c97e6f9-414c-4377-832d-989bee35377a 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N b81aaca3-eebf-4445-8bd9-f803b8b54551 {http,https} \N \N {/s341-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e518090-3431-424d-94e9-0ce4fed3dc1b 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4f0fe748-796b-413f-a4f5-3cbbe44c27c2 {http,https} \N \N {/s342-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b253cdee-c36a-4b4e-9f82-861acb678fb5 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4f0fe748-796b-413f-a4f5-3cbbe44c27c2 {http,https} \N \N {/s342-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2bfb2f5e-fbff-43ec-9478-9c8d437d8a93 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4f0fe748-796b-413f-a4f5-3cbbe44c27c2 {http,https} \N \N {/s342-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ed1b8cde-e815-4aff-8480-434c60b6a024 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4f0fe748-796b-413f-a4f5-3cbbe44c27c2 {http,https} \N \N {/s342-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5ea36b55-e87b-4a9a-8553-ade0b92cc448 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N f406cf4a-75c3-4ccf-8f36-9255b36e0f69 {http,https} \N \N {/s343-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d519436e-ecbd-4214-9c45-571516db2062 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N f406cf4a-75c3-4ccf-8f36-9255b36e0f69 {http,https} \N \N {/s343-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+03abb2da-a99d-41ee-b03e-5cab0c96a0db 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N f406cf4a-75c3-4ccf-8f36-9255b36e0f69 {http,https} \N \N {/s343-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3fb5c8e7-69b6-48ca-8d9e-fe9a5de788a8 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N f406cf4a-75c3-4ccf-8f36-9255b36e0f69 {http,https} \N \N {/s343-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+abaf7bb1-202c-4a1a-939b-57841b2a355d 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2817bf9-36c2-4acf-8de3-4468b149d571 {http,https} \N \N {/s344-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e20351c6-e156-4704-9db5-5cc4b91eb840 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2817bf9-36c2-4acf-8de3-4468b149d571 {http,https} \N \N {/s344-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28ef2b55-4bbb-49fc-a509-95b888799a46 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2817bf9-36c2-4acf-8de3-4468b149d571 {http,https} \N \N {/s344-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7dbe296a-4373-4864-b743-759ea36dccf7 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e2817bf9-36c2-4acf-8de3-4468b149d571 {http,https} \N \N {/s344-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af502028-50bd-4bda-b6d1-3aedd395c5ed 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c3f8cf8e-0683-40bc-aabb-8695dce534a2 {http,https} \N \N {/s345-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2a57c331-b134-41be-86d6-fe41a168f35b 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c3f8cf8e-0683-40bc-aabb-8695dce534a2 {http,https} \N \N {/s345-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7cfca594-2827-4f2f-aef5-1db708a6cdbc 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c3f8cf8e-0683-40bc-aabb-8695dce534a2 {http,https} \N \N {/s345-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a6df4d33-4ddc-4211-8aba-ffc049d0633e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c3f8cf8e-0683-40bc-aabb-8695dce534a2 {http,https} \N \N {/s345-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8b5aa23c-fb9c-4d26-a705-5d50a71d2d4f 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N da395198-c4a7-4d67-9e0f-8ea9bd6a72db {http,https} \N \N {/s346-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+41f98379-f615-4b60-a8d3-633a903175d5 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N da395198-c4a7-4d67-9e0f-8ea9bd6a72db {http,https} \N \N {/s346-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a8504c5-a46f-4b1e-9b28-7a9a25fedac7 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N da395198-c4a7-4d67-9e0f-8ea9bd6a72db {http,https} \N \N {/s346-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+86e8e358-7926-4a5a-b9fb-2a7f2ba5d984 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N da395198-c4a7-4d67-9e0f-8ea9bd6a72db {http,https} \N \N {/s346-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+478ff66f-b6ee-4ad2-b7ce-c59a1cea3423 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e5763c8f-13d5-4f01-8ebd-b6db40a89fb0 {http,https} \N \N {/s347-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+70b4c8ac-7ace-4e03-9bbe-d33da69e9b46 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e5763c8f-13d5-4f01-8ebd-b6db40a89fb0 {http,https} \N \N {/s347-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+64329e6f-182a-47dd-ba42-d64150e522a6 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e5763c8f-13d5-4f01-8ebd-b6db40a89fb0 {http,https} \N \N {/s347-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+86de25d5-8059-4b44-96c8-0c283f56e722 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e5763c8f-13d5-4f01-8ebd-b6db40a89fb0 {http,https} \N \N {/s347-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5a45a249-1273-40c6-a277-db604f0ece4e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d84611e-9887-40c6-ab00-01210d1f82b7 {http,https} \N \N {/s348-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+75e39c9b-250a-4877-8535-1334322a8e7f 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d84611e-9887-40c6-ab00-01210d1f82b7 {http,https} \N \N {/s348-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a83e5ce3-6f48-4b55-814b-0786efa3f57a 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d84611e-9887-40c6-ab00-01210d1f82b7 {http,https} \N \N {/s348-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9e090bb4-5252-4dac-8440-46393a08b5e3 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d84611e-9887-40c6-ab00-01210d1f82b7 {http,https} \N \N {/s348-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0e57a6e5-a00e-4d30-b2f0-4dfe33eb6cce 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c238d775-2523-46fc-8d1a-540fac1f6896 {http,https} \N \N {/s349-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9f7adf82-c336-436b-ad3c-f6ef3717aad0 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c238d775-2523-46fc-8d1a-540fac1f6896 {http,https} \N \N {/s349-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a24d389-8b40-4d59-ac92-75125bf6d4e9 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c238d775-2523-46fc-8d1a-540fac1f6896 {http,https} \N \N {/s349-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+69d769b5-0041-4d8e-8b98-d89d3d5a1a4d 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c238d775-2523-46fc-8d1a-540fac1f6896 {http,https} \N \N {/s349-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e1877bca-7a44-4921-8069-99447c8a6f3f 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d915ba2-c858-4732-a9e9-7b21b9d47b27 {http,https} \N \N {/s350-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+89624eec-f60d-4976-8ff8-445e5ac8bc10 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d915ba2-c858-4732-a9e9-7b21b9d47b27 {http,https} \N \N {/s350-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1e18ca64-3817-46bf-aa9d-901f064b43ed 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d915ba2-c858-4732-a9e9-7b21b9d47b27 {http,https} \N \N {/s350-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a0827b4-55b7-4de3-a68c-d1d32352c61b 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 1d915ba2-c858-4732-a9e9-7b21b9d47b27 {http,https} \N \N {/s350-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24428a28-8db0-46c3-a9ba-f613604bfc9b 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 2ddd0eb3-bada-4443-bbfe-5fccde527dca {http,https} \N \N {/s351-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ec8fdc94-187d-42fd-9269-398ee1277e41 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 2ddd0eb3-bada-4443-bbfe-5fccde527dca {http,https} \N \N {/s351-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f7eec7d2-08cb-4080-8257-662e57a049de 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 2ddd0eb3-bada-4443-bbfe-5fccde527dca {http,https} \N \N {/s351-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ebd16e5-1a83-42c9-aaeb-1c6d6a352d6f 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 2ddd0eb3-bada-4443-bbfe-5fccde527dca {http,https} \N \N {/s351-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0305af07-edec-4338-9a35-a70610fdc841 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N fb6cc1c1-f874-4ad9-9a62-3b406f948218 {http,https} \N \N {/s352-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ca14ccb8-b0bc-4584-bd0a-8e5bf15e8f71 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N fb6cc1c1-f874-4ad9-9a62-3b406f948218 {http,https} \N \N {/s352-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d35d85fd-46e6-4659-af15-43f4d3223fbe 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N fb6cc1c1-f874-4ad9-9a62-3b406f948218 {http,https} \N \N {/s352-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+25528edd-75fb-48e4-bab0-19c7b9888670 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N fb6cc1c1-f874-4ad9-9a62-3b406f948218 {http,https} \N \N {/s352-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+93cfa9fd-30e8-49ac-a3fa-367e6ab88a20 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N a7946bd4-5a6b-4f56-bbd5-59cf59fbacc3 {http,https} \N \N {/s353-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c6524368-ce3b-42d9-9626-71a1ac6cc0c5 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N a7946bd4-5a6b-4f56-bbd5-59cf59fbacc3 {http,https} \N \N {/s353-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af27ed48-426a-4b69-9f81-8aca7ab95b87 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N a7946bd4-5a6b-4f56-bbd5-59cf59fbacc3 {http,https} \N \N {/s353-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+878cfaaa-1c75-4a7a-9ff7-324df7c8cec1 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N a7946bd4-5a6b-4f56-bbd5-59cf59fbacc3 {http,https} \N \N {/s353-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f8220ab-b3e0-4149-a5a0-9bed6fd0f766 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c2a397d2-8f91-41d8-9158-97dd24955a80 {http,https} \N \N {/s354-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8460ddfe-8f07-4d0d-83ae-c376236ef347 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c2a397d2-8f91-41d8-9158-97dd24955a80 {http,https} \N \N {/s354-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+991e01eb-9fca-4ca8-9ea0-34f3ea2d3d63 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c2a397d2-8f91-41d8-9158-97dd24955a80 {http,https} \N \N {/s354-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+29b09368-8b00-4dd5-8ffe-ee5cfe06c0f3 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N c2a397d2-8f91-41d8-9158-97dd24955a80 {http,https} \N \N {/s354-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+794e1b54-9252-4c31-81b8-e97f7de7954f 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 959074dc-9a50-4bd8-bb49-d0a9333d0477 {http,https} \N \N {/s355-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b399d469-fe06-45d3-83a9-8399da0459c3 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 959074dc-9a50-4bd8-bb49-d0a9333d0477 {http,https} \N \N {/s355-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5edab9de-fd7c-4745-8802-822070cb1b76 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 959074dc-9a50-4bd8-bb49-d0a9333d0477 {http,https} \N \N {/s355-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c3471b7-1ac2-474d-baf8-c0155b3cc954 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 959074dc-9a50-4bd8-bb49-d0a9333d0477 {http,https} \N \N {/s355-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6700d7a1-8329-4a82-a7b0-7c0482f49839 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4fafaa54-d47d-4488-8c56-94be290f38b7 {http,https} \N \N {/s356-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0320b0e9-a314-4daf-be4b-eb1c4554c0ad 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4fafaa54-d47d-4488-8c56-94be290f38b7 {http,https} \N \N {/s356-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fb7c1e9e-e202-4a6d-b295-ab5768d91390 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4fafaa54-d47d-4488-8c56-94be290f38b7 {http,https} \N \N {/s356-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1584e198-4952-4a7c-a7cc-07de52851883 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 4fafaa54-d47d-4488-8c56-94be290f38b7 {http,https} \N \N {/s356-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc766404-5881-4a64-ad32-45dad707ae63 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e9556ed2-8e33-4130-a9b9-fc6c799655fc {http,https} \N \N {/s357-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7460da23-fec2-4276-838d-bc6ccfdcb35e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e9556ed2-8e33-4130-a9b9-fc6c799655fc {http,https} \N \N {/s357-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5fafe87e-a43e-4de6-881c-7f25cc109d10 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e9556ed2-8e33-4130-a9b9-fc6c799655fc {http,https} \N \N {/s357-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+582e3091-8abd-40f7-b3ab-2787b9976b2a 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N e9556ed2-8e33-4130-a9b9-fc6c799655fc {http,https} \N \N {/s357-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1b6fd211-1332-4c07-b7b2-f0c2dfcde27d 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 9a6c8306-cf36-42a6-9117-724b675fd9a2 {http,https} \N \N {/s358-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bfa87303-9222-471e-9d39-7a1d898bd097 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 9a6c8306-cf36-42a6-9117-724b675fd9a2 {http,https} \N \N {/s358-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5ab771a8-5eef-4328-8609-99ae74d8d7c2 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 9a6c8306-cf36-42a6-9117-724b675fd9a2 {http,https} \N \N {/s358-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b7a6f7a6-aa81-4cef-96d2-dec529a94680 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 9a6c8306-cf36-42a6-9117-724b675fd9a2 {http,https} \N \N {/s358-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0080ed1d-ccc1-4f02-b014-dd3a92ac964e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N af36e2ce-968f-4143-926c-34f5827a2319 {http,https} \N \N {/s359-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ad1e84ac-bc9b-4ab1-a954-afebdc7d5907 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N af36e2ce-968f-4143-926c-34f5827a2319 {http,https} \N \N {/s359-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a10dd6fb-af73-467b-bcc4-869186049cc6 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N af36e2ce-968f-4143-926c-34f5827a2319 {http,https} \N \N {/s359-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc92bade-6f80-4cd0-95f4-1eaf4bfc93a6 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N af36e2ce-968f-4143-926c-34f5827a2319 {http,https} \N \N {/s359-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+07335b05-d85c-45be-a16c-5760a077318b 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 59a3ea50-4f62-4ce2-ad54-8d72abe1ec68 {http,https} \N \N {/s360-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4c892d67-7d8c-4879-93fd-c2bcd7a69271 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 59a3ea50-4f62-4ce2-ad54-8d72abe1ec68 {http,https} \N \N {/s360-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f415709-c4bd-42fb-b916-224f1bb4ee56 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 59a3ea50-4f62-4ce2-ad54-8d72abe1ec68 {http,https} \N \N {/s360-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+000ad825-d106-4ba3-93c8-424338479452 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 59a3ea50-4f62-4ce2-ad54-8d72abe1ec68 {http,https} \N \N {/s360-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5479f8b8-d617-47cd-93c5-ea9c7581a07e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 45cc6295-8cfc-4e44-b124-0d05c04cdd3e {http,https} \N \N {/s361-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9498812b-b58b-4250-94f1-694faebd104c 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 45cc6295-8cfc-4e44-b124-0d05c04cdd3e {http,https} \N \N {/s361-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0e8c019f-1d59-43a1-8e02-b9be646649f1 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 45cc6295-8cfc-4e44-b124-0d05c04cdd3e {http,https} \N \N {/s361-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+72d8cdb5-6f7b-48c9-8a82-eedf0fa5479d 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 45cc6295-8cfc-4e44-b124-0d05c04cdd3e {http,https} \N \N {/s361-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c67e2369-5ff1-40a4-92ba-a63a49d57130 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 8b3db5a2-f3c4-4d2b-b60e-55c3f0d42960 {http,https} \N \N {/s362-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b1566411-b1ff-4055-b8d4-9f274ca268eb 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 8b3db5a2-f3c4-4d2b-b60e-55c3f0d42960 {http,https} \N \N {/s362-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+54f335c0-bc32-4fa9-8929-1c6dccb13d36 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 8b3db5a2-f3c4-4d2b-b60e-55c3f0d42960 {http,https} \N \N {/s362-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7fa94e74-d93b-42b8-ace1-95d5526737df 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 8b3db5a2-f3c4-4d2b-b60e-55c3f0d42960 {http,https} \N \N {/s362-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cc2cfc87-6cd6-4a9c-82af-110aecc7001e 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 809b0fa5-91fe-4f0b-bfa4-1b17ca92647f {http,https} \N \N {/s363-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4709f82-2569-4d4c-a4c9-b3ceeccf6689 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 809b0fa5-91fe-4f0b-bfa4-1b17ca92647f {http,https} \N \N {/s363-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+edcd51f1-9374-49a8-ac8e-ab96a9f249cb 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 809b0fa5-91fe-4f0b-bfa4-1b17ca92647f {http,https} \N \N {/s363-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4f5a5ff5-8ea4-4e02-8ba9-5742fd50e171 2022-05-26 09:04:32+00 2022-05-26 09:04:32+00 \N 809b0fa5-91fe-4f0b-bfa4-1b17ca92647f {http,https} \N \N {/s363-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ae992988-c221-4d56-b3ee-928d7cda0762 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c75cdbd1-8145-48ae-8097-d6ce0ee3d383 {http,https} \N \N {/s364-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ea622405-967e-4c78-bdd1-4547c57aa585 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c75cdbd1-8145-48ae-8097-d6ce0ee3d383 {http,https} \N \N {/s364-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c7fc5f78-b09c-4c74-bd4e-ff12f57bebc8 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c75cdbd1-8145-48ae-8097-d6ce0ee3d383 {http,https} \N \N {/s364-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6e1f0b6c-5c92-4d9e-a468-510ea095dc98 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c75cdbd1-8145-48ae-8097-d6ce0ee3d383 {http,https} \N \N {/s364-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a9ef3f1e-7b53-482d-b4ff-2fdd4c06652c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N e238e1f2-7acb-4caf-a7b9-4abc165b2f78 {http,https} \N \N {/s365-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8af2c3ca-8d5b-4ddb-9ae9-627fe6003eb7 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N e238e1f2-7acb-4caf-a7b9-4abc165b2f78 {http,https} \N \N {/s365-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3297507a-c132-4dc6-afc0-522dac9f4800 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N e238e1f2-7acb-4caf-a7b9-4abc165b2f78 {http,https} \N \N {/s365-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1ddc042c-07c8-4789-9845-85c75efa01dd 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N e238e1f2-7acb-4caf-a7b9-4abc165b2f78 {http,https} \N \N {/s365-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3cc542c4-4412-4796-bddb-83f17634ba53 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 579dd648-5a51-4240-9901-d59ea046dbe4 {http,https} \N \N {/s366-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+329b4835-c874-4fc3-ac09-ab231af047dc 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 579dd648-5a51-4240-9901-d59ea046dbe4 {http,https} \N \N {/s366-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9a0fccd8-69ba-433e-ba8d-523307a4cc74 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 579dd648-5a51-4240-9901-d59ea046dbe4 {http,https} \N \N {/s366-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e04ee641-8b42-4049-8251-d5c5232028b7 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 579dd648-5a51-4240-9901-d59ea046dbe4 {http,https} \N \N {/s366-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+97d3baf7-99fe-46ad-a9ad-594b44ccd95c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 363e3fd7-2510-4b88-8b61-19c6a701a154 {http,https} \N \N {/s367-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c2c78b0c-5593-467d-803f-d81a08e52009 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 363e3fd7-2510-4b88-8b61-19c6a701a154 {http,https} \N \N {/s367-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+51d4c327-304b-4082-acda-ec921b2f0452 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 363e3fd7-2510-4b88-8b61-19c6a701a154 {http,https} \N \N {/s367-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+af0cc7e6-6754-45df-9398-858ec4b6374b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 363e3fd7-2510-4b88-8b61-19c6a701a154 {http,https} \N \N {/s367-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+51656063-1fd6-4352-851c-3d3fdce5f89b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 6bfe7e94-4211-492f-a9db-a6c81dd6f547 {http,https} \N \N {/s368-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5467cdd0-7125-4043-be60-f219600c161b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 6bfe7e94-4211-492f-a9db-a6c81dd6f547 {http,https} \N \N {/s368-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f0a47c4-bbde-4c79-9277-eeb8d6572ef9 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 6bfe7e94-4211-492f-a9db-a6c81dd6f547 {http,https} \N \N {/s368-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc6edc7c-3bcb-456e-a059-e6df5a1dd33a 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 6bfe7e94-4211-492f-a9db-a6c81dd6f547 {http,https} \N \N {/s368-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c454e2c3-b89f-447b-9ba5-373d57a15b13 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 614a1279-a381-4be2-acef-301958e89071 {http,https} \N \N {/s369-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cda42f89-9974-4193-8a36-05532d921f5c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 614a1279-a381-4be2-acef-301958e89071 {http,https} \N \N {/s369-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+315e9356-356c-4fb1-9c90-24f7036d918a 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 614a1279-a381-4be2-acef-301958e89071 {http,https} \N \N {/s369-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d5d61b12-65fb-40f9-8f6d-1a0f2a2d5d3b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 614a1279-a381-4be2-acef-301958e89071 {http,https} \N \N {/s369-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+221875af-ce48-49bd-9221-3041ed8b2c84 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3861f439-875f-453b-8651-03d9359f5788 {http,https} \N \N {/s370-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8d6f924b-ac52-4b3f-9125-a82d6ced70ff 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3861f439-875f-453b-8651-03d9359f5788 {http,https} \N \N {/s370-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+77aec436-9027-467b-9173-542650d94bba 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3861f439-875f-453b-8651-03d9359f5788 {http,https} \N \N {/s370-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+61e5fbf8-5f7e-4d2c-ab9d-e3c04e78d006 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3861f439-875f-453b-8651-03d9359f5788 {http,https} \N \N {/s370-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7f76d3d9-7ad2-4b50-b9db-79d2dbf488c7 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 0663d4a9-d9d4-4d92-ab92-8ecae04c5440 {http,https} \N \N {/s371-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+939a8636-faeb-438f-9db7-3602974a6863 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 0663d4a9-d9d4-4d92-ab92-8ecae04c5440 {http,https} \N \N {/s371-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7f12304e-0c34-4598-94d5-efe0798f705a 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 0663d4a9-d9d4-4d92-ab92-8ecae04c5440 {http,https} \N \N {/s371-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f8a345b6-9917-411d-ad6d-e3e30387b9dc 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 0663d4a9-d9d4-4d92-ab92-8ecae04c5440 {http,https} \N \N {/s371-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+413e7132-1858-41d9-ad19-d3c6fcf9cc8a 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 00a04a0e-8a61-497e-a1b7-555d9edebd3c {http,https} \N \N {/s372-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+236a1762-301b-4970-aad7-42db64186ce2 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 00a04a0e-8a61-497e-a1b7-555d9edebd3c {http,https} \N \N {/s372-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1766c248-137a-4c64-917b-947cc9beed45 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 00a04a0e-8a61-497e-a1b7-555d9edebd3c {http,https} \N \N {/s372-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+da45a0a2-a908-4513-a48b-e802b87306fa 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 00a04a0e-8a61-497e-a1b7-555d9edebd3c {http,https} \N \N {/s372-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+61773a20-69d3-4493-be5a-28c141aa0d1e 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a90836ba-dcb3-4f3f-bf2c-02bc1d5f7453 {http,https} \N \N {/s373-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6862d7e7-6c8a-4a59-bc83-c12c67c58957 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a90836ba-dcb3-4f3f-bf2c-02bc1d5f7453 {http,https} \N \N {/s373-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2c68df09-0ba1-4d91-9503-b013453e457a 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a90836ba-dcb3-4f3f-bf2c-02bc1d5f7453 {http,https} \N \N {/s373-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc03b311-d66f-4cf5-b822-d8455ba367e3 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a90836ba-dcb3-4f3f-bf2c-02bc1d5f7453 {http,https} \N \N {/s373-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de5dbba9-6119-483e-987c-fca0597b20cf 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 001879e3-9e6a-49e1-8893-9bfa1ed0662f {http,https} \N \N {/s374-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+79ab012b-7a07-481e-af00-3e06f1f1f01c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 001879e3-9e6a-49e1-8893-9bfa1ed0662f {http,https} \N \N {/s374-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6785d5f2-2915-4610-9ea4-d82c01cd5f56 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 001879e3-9e6a-49e1-8893-9bfa1ed0662f {http,https} \N \N {/s374-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+648cd88c-5683-4638-bfb4-0e486bed189b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 001879e3-9e6a-49e1-8893-9bfa1ed0662f {http,https} \N \N {/s374-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+84052b2e-d59b-43b2-aaec-7fbd9f994cca 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3b864315-4410-47c4-8d1f-41340443be83 {http,https} \N \N {/s375-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dfd5a62a-1225-4492-a107-5bcdb41b0156 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3b864315-4410-47c4-8d1f-41340443be83 {http,https} \N \N {/s375-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+11603845-42ab-429c-b7c2-1a9f41626e4b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3b864315-4410-47c4-8d1f-41340443be83 {http,https} \N \N {/s375-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc441c3f-d83d-4b49-bc91-db810eb363df 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3b864315-4410-47c4-8d1f-41340443be83 {http,https} \N \N {/s375-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6ad602ad-561f-4f7d-bfe5-fa790ce6a140 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N da92e9da-c205-44a5-8e55-6cabab24e221 {http,https} \N \N {/s376-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bfcc5bbd-046f-4dfb-8ea1-7fbbd0424ca8 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N da92e9da-c205-44a5-8e55-6cabab24e221 {http,https} \N \N {/s376-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f98604e-a592-4420-b50d-7e3441327f39 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N da92e9da-c205-44a5-8e55-6cabab24e221 {http,https} \N \N {/s376-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+086aedad-4995-404b-bf04-79afc201db86 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N da92e9da-c205-44a5-8e55-6cabab24e221 {http,https} \N \N {/s376-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6b566f60-9397-4951-9408-44f3b041d709 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N ec7a7ee9-84ef-4e7e-86dc-6c1ea5db4019 {http,https} \N \N {/s377-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b9f69b21-4680-4dd6-b8d7-d29fcdd3d066 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N ec7a7ee9-84ef-4e7e-86dc-6c1ea5db4019 {http,https} \N \N {/s377-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4ccd11ff-72de-4ceb-8011-83e4d93575b8 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N ec7a7ee9-84ef-4e7e-86dc-6c1ea5db4019 {http,https} \N \N {/s377-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8990d95f-7246-45c8-ab26-d82f8e0b770c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N ec7a7ee9-84ef-4e7e-86dc-6c1ea5db4019 {http,https} \N \N {/s377-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f54a0c19-68fd-4523-9223-eb355b652ba2 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N de23c01f-138f-4b4f-b077-7966e5301849 {http,https} \N \N {/s378-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22d2cc42-2fd1-44b9-bda6-4f18d81c4c69 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N de23c01f-138f-4b4f-b077-7966e5301849 {http,https} \N \N {/s378-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8987a4e8-880e-45e9-a3f3-eb169357c337 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N de23c01f-138f-4b4f-b077-7966e5301849 {http,https} \N \N {/s378-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+80a62322-1d0c-48bf-b529-858c3dfce1a9 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N de23c01f-138f-4b4f-b077-7966e5301849 {http,https} \N \N {/s378-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4af060f3-0c41-420e-8848-e19c64c4f68f 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2231820c-c6c6-4b43-8030-60d84ec840df {http,https} \N \N {/s379-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7160fc2f-ede7-4559-89d4-6fe1a346cdd7 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2231820c-c6c6-4b43-8030-60d84ec840df {http,https} \N \N {/s379-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7444991e-be0a-49e5-966e-af21ed179cd9 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2231820c-c6c6-4b43-8030-60d84ec840df {http,https} \N \N {/s379-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f37b85d-318b-42a0-a2e2-18f3a9487bf0 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2231820c-c6c6-4b43-8030-60d84ec840df {http,https} \N \N {/s379-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+952b4c5c-a71d-49ad-becd-3033f7703e18 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 962b06e6-2702-4267-b103-b352f6b842a4 {http,https} \N \N {/s380-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2bed3e4-72ae-49a1-9263-a729dfb5b028 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 962b06e6-2702-4267-b103-b352f6b842a4 {http,https} \N \N {/s380-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+85f3b168-600e-405a-b66b-ac2cfb321a81 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 962b06e6-2702-4267-b103-b352f6b842a4 {http,https} \N \N {/s380-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+75cdeb50-abb0-4af0-872c-bafbf0c5a51a 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 962b06e6-2702-4267-b103-b352f6b842a4 {http,https} \N \N {/s380-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5213a1c8-19c7-444e-913c-42dfc02a09d0 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 63bfee6a-6d44-4301-9cee-df0105f24f5e {http,https} \N \N {/s381-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+91e485c1-8fda-4a50-b1be-eda59a22fdc9 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 63bfee6a-6d44-4301-9cee-df0105f24f5e {http,https} \N \N {/s381-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c1a188ed-50c2-41ce-92de-d3831e736f71 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 63bfee6a-6d44-4301-9cee-df0105f24f5e {http,https} \N \N {/s381-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1dcfafc0-0ced-4655-aa29-1efd22877b90 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 63bfee6a-6d44-4301-9cee-df0105f24f5e {http,https} \N \N {/s381-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+55d057c2-be1d-477b-a075-cb1bed856b8d 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c6a5a31e-2c88-47c4-8e9a-c60bece7ef75 {http,https} \N \N {/s382-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bd0377bd-ef7d-41eb-a086-2984063615a3 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c6a5a31e-2c88-47c4-8e9a-c60bece7ef75 {http,https} \N \N {/s382-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+58903e6e-39b8-494c-b871-ea65c3aa5fb9 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c6a5a31e-2c88-47c4-8e9a-c60bece7ef75 {http,https} \N \N {/s382-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+59f9b2e4-6dc6-476d-98b4-435519bb3953 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N c6a5a31e-2c88-47c4-8e9a-c60bece7ef75 {http,https} \N \N {/s382-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8e388a1c-cc25-4156-ab6d-d94900121cb1 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2d096abd-ffb0-4143-96a4-7779218d6d4f {http,https} \N \N {/s383-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e465856b-aa77-4837-9ef3-4f3789960415 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2d096abd-ffb0-4143-96a4-7779218d6d4f {http,https} \N \N {/s383-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8870b0c2-6b31-4f3d-a09a-e8afb622a1bf 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2d096abd-ffb0-4143-96a4-7779218d6d4f {http,https} \N \N {/s383-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+985749b3-89f2-40bd-ac5a-fdbba81ebfd3 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 2d096abd-ffb0-4143-96a4-7779218d6d4f {http,https} \N \N {/s383-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1c1992eb-be64-4f77-aadb-9f2464687003 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a10741c9-4ed7-422d-9f52-54c17c4bbd8b {http,https} \N \N {/s384-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28bc0bf3-b497-4694-adf4-221e8c32fa50 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a10741c9-4ed7-422d-9f52-54c17c4bbd8b {http,https} \N \N {/s384-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0f6e5eb8-f2f9-4596-8dc6-d5798fbfcf17 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a10741c9-4ed7-422d-9f52-54c17c4bbd8b {http,https} \N \N {/s384-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c97b2ca4-3ed8-4bc5-b9e8-a0c964c62140 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a10741c9-4ed7-422d-9f52-54c17c4bbd8b {http,https} \N \N {/s384-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+47fcf675-d1d9-49cd-91e6-5319a9868edb 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 234c48dd-9af4-4099-80ff-40ad13f89401 {http,https} \N \N {/s385-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+558293de-13ea-42cc-b124-dc89484f8916 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 234c48dd-9af4-4099-80ff-40ad13f89401 {http,https} \N \N {/s385-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+807fc65e-8053-4b45-9a2c-11358a86b215 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 234c48dd-9af4-4099-80ff-40ad13f89401 {http,https} \N \N {/s385-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de177505-cc95-424a-9848-e72f78b7e110 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 234c48dd-9af4-4099-80ff-40ad13f89401 {http,https} \N \N {/s385-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a821d074-d659-40af-8c2d-9366c9c6ff31 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N bb5d6545-d507-4b3a-ba24-bb510c914e95 {http,https} \N \N {/s386-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ba20cb2d-25b7-4176-a6cf-da9395baec5b 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N bb5d6545-d507-4b3a-ba24-bb510c914e95 {http,https} \N \N {/s386-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+41460742-9989-43a7-a5f4-4bd454a02955 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N bb5d6545-d507-4b3a-ba24-bb510c914e95 {http,https} \N \N {/s386-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c822b82c-79c3-42f9-ae1b-f83a03fc1049 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N bb5d6545-d507-4b3a-ba24-bb510c914e95 {http,https} \N \N {/s386-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+26d19423-642f-46c6-9160-62801b6619da 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 28f712ea-c08c-4e7a-8cf9-4b13e36ff212 {http,https} \N \N {/s387-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c4430fb6-cb22-4f3a-845d-b5f5f003f289 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 28f712ea-c08c-4e7a-8cf9-4b13e36ff212 {http,https} \N \N {/s387-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+164f2566-d220-4140-84bc-3c66ff8e7cbd 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 28f712ea-c08c-4e7a-8cf9-4b13e36ff212 {http,https} \N \N {/s387-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a524151-86f9-42e5-933d-405065d4afd3 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 28f712ea-c08c-4e7a-8cf9-4b13e36ff212 {http,https} \N \N {/s387-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e1ad3f70-d9cb-4bd7-9270-b7920adc4b7a 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 152a5d0e-dc5a-44d9-af10-8ec63701dd3b {http,https} \N \N {/s388-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+33b555ad-42cb-4c55-8f0f-8da3a1ce5f9f 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 152a5d0e-dc5a-44d9-af10-8ec63701dd3b {http,https} \N \N {/s388-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9ddcbe4-12d3-4a16-8c74-6aa16052471c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 152a5d0e-dc5a-44d9-af10-8ec63701dd3b {http,https} \N \N {/s388-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4abc74ac-517c-47b3-9d56-f674a30936de 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 152a5d0e-dc5a-44d9-af10-8ec63701dd3b {http,https} \N \N {/s388-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b42fa17b-9260-464b-a19b-98299f7a0ea4 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 93857261-5bcb-47aa-9144-22b35b135d4b {http,https} \N \N {/s389-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b71c5ee8-da34-4fd1-ba89-60a80f125c9c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 93857261-5bcb-47aa-9144-22b35b135d4b {http,https} \N \N {/s389-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ff3c9019-b6f6-4085-997b-a2fcefed7e6d 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 93857261-5bcb-47aa-9144-22b35b135d4b {http,https} \N \N {/s389-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9c082c36-8d43-4286-82c8-1f4bb9ec059c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 93857261-5bcb-47aa-9144-22b35b135d4b {http,https} \N \N {/s389-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f5b00f8b-9254-41d8-82bb-25137f5c6da9 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 111f99da-d06d-4cb3-b864-8f3e1f49aa74 {http,https} \N \N {/s390-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9c740728-2ed9-436c-9862-685c2a4e8a25 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 111f99da-d06d-4cb3-b864-8f3e1f49aa74 {http,https} \N \N {/s390-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0cd81876-c603-43bd-85cb-02a03a3ad133 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 111f99da-d06d-4cb3-b864-8f3e1f49aa74 {http,https} \N \N {/s390-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be46714f-b556-4bb2-921d-f1d9987003ca 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 111f99da-d06d-4cb3-b864-8f3e1f49aa74 {http,https} \N \N {/s390-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f58d8f45-788f-4b3a-9f03-a3083fba70fa 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3924e923-d2f1-4275-8747-bd11ac4f74d3 {http,https} \N \N {/s391-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ec9e067-61d3-4020-b7c1-9be001df4d9c 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3924e923-d2f1-4275-8747-bd11ac4f74d3 {http,https} \N \N {/s391-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d0c7488b-2fe5-4084-ac74-de4688c18b44 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3924e923-d2f1-4275-8747-bd11ac4f74d3 {http,https} \N \N {/s391-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+200bf282-ca7a-47a1-9345-ec0e38175963 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N 3924e923-d2f1-4275-8747-bd11ac4f74d3 {http,https} \N \N {/s391-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3adb743f-2d77-46ec-84dc-2d0003b50d5f 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a73038fe-4577-4639-a479-767f244244c3 {http,https} \N \N {/s392-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22a08988-6063-4eee-bf9e-1b3e8aeeeb37 2022-05-26 09:04:33+00 2022-05-26 09:04:33+00 \N a73038fe-4577-4639-a479-767f244244c3 {http,https} \N \N {/s392-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b8598f0b-f3b5-4806-b6fd-7c3e590d8775 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N a73038fe-4577-4639-a479-767f244244c3 {http,https} \N \N {/s392-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2bb6a9b6-6da4-4b97-8cd0-b55ea0a031fc 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N a73038fe-4577-4639-a479-767f244244c3 {http,https} \N \N {/s392-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+436b0418-1a0c-4314-9b1e-b92b5268ac2d 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 4a062dd6-f1c2-4b36-ac1d-998925eb0b83 {http,https} \N \N {/s393-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a87ff715-320b-4f9a-a1c3-6e4f73e050d3 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 4a062dd6-f1c2-4b36-ac1d-998925eb0b83 {http,https} \N \N {/s393-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ca7d52dc-bfb7-42f3-95e7-837e002d7a8c 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 4a062dd6-f1c2-4b36-ac1d-998925eb0b83 {http,https} \N \N {/s393-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9416e2cc-af41-4618-b366-844246114c14 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 4a062dd6-f1c2-4b36-ac1d-998925eb0b83 {http,https} \N \N {/s393-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+88efc63a-aaef-4ba5-a7e4-ad7e8d0c3b26 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8c475290-e87c-4711-a6ac-d2dc4028fad6 {http,https} \N \N {/s394-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7a788b39-3ef4-4627-ba39-823ce3b3135e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8c475290-e87c-4711-a6ac-d2dc4028fad6 {http,https} \N \N {/s394-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d9a329b4-59e1-4d94-8c50-331df0da25e2 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8c475290-e87c-4711-a6ac-d2dc4028fad6 {http,https} \N \N {/s394-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2f331ace-1d1b-4068-b543-a67043408803 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8c475290-e87c-4711-a6ac-d2dc4028fad6 {http,https} \N \N {/s394-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eefd9468-e6b6-4f30-be8a-77e2da8d3c9f 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8cec9caf-f09c-4e50-ab29-a23009c77cb7 {http,https} \N \N {/s395-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5adb33b8-3ec9-4c38-b64a-e7db42204bdf 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8cec9caf-f09c-4e50-ab29-a23009c77cb7 {http,https} \N \N {/s395-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b0ee32c5-5e4f-43b5-aee6-77eb539e4961 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8cec9caf-f09c-4e50-ab29-a23009c77cb7 {http,https} \N \N {/s395-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+95c9a80f-5ab6-4364-8ca7-ec3080743b49 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 8cec9caf-f09c-4e50-ab29-a23009c77cb7 {http,https} \N \N {/s395-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+deea16af-e5df-47aa-a869-414656ee2d30 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3a1b190c-0930-4404-bee0-eca6c7621114 {http,https} \N \N {/s396-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ef7b4a9f-4ba5-408c-81b7-47ae27350a82 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3a1b190c-0930-4404-bee0-eca6c7621114 {http,https} \N \N {/s396-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a8f75c71-0778-4453-8514-27df41e14a3b 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3a1b190c-0930-4404-bee0-eca6c7621114 {http,https} \N \N {/s396-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+08b777bf-d125-429b-8d28-48e909bf7f4b 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3a1b190c-0930-4404-bee0-eca6c7621114 {http,https} \N \N {/s396-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28ab6b88-5d8e-4859-b882-9e82a00f460c 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N ccb26ed5-9dd0-46b3-8cb5-3584782c9d06 {http,https} \N \N {/s397-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be3158c6-d0e2-45b9-928f-f0d96aa0867e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N ccb26ed5-9dd0-46b3-8cb5-3584782c9d06 {http,https} \N \N {/s397-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4bec0e71-22e6-4959-accb-e4e2019f392f 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N ccb26ed5-9dd0-46b3-8cb5-3584782c9d06 {http,https} \N \N {/s397-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a539a7c1-ce69-4d1e-b467-33fd3d68b514 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N ccb26ed5-9dd0-46b3-8cb5-3584782c9d06 {http,https} \N \N {/s397-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8bbbf888-17b3-4862-a1fd-9aa2063f6383 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6bce2b2a-c6a0-4463-9dfc-bd9366f62b3a {http,https} \N \N {/s398-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+62a54ead-af8e-4e0d-b316-e2ecf13627b9 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6bce2b2a-c6a0-4463-9dfc-bd9366f62b3a {http,https} \N \N {/s398-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+925c217c-669b-4111-8985-008e61aff1d4 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6bce2b2a-c6a0-4463-9dfc-bd9366f62b3a {http,https} \N \N {/s398-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+27ee97d0-2dc6-4cab-a807-6d96645e467e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6bce2b2a-c6a0-4463-9dfc-bd9366f62b3a {http,https} \N \N {/s398-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d2e96e0-1a59-4290-92c6-cb1c8798aef1 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 050c4646-3958-40b1-92f3-2a7979732b5b {http,https} \N \N {/s399-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a696295f-4a96-4414-b113-a81d63435f8d 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 050c4646-3958-40b1-92f3-2a7979732b5b {http,https} \N \N {/s399-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+36121b59-fcfb-4a14-8d31-ac9931afbdd5 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 050c4646-3958-40b1-92f3-2a7979732b5b {http,https} \N \N {/s399-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e8472a7d-4b68-40c7-9b60-41bccc7a189a 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 050c4646-3958-40b1-92f3-2a7979732b5b {http,https} \N \N {/s399-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0ad4944e-0971-4fbd-85ac-4ea55a56e14f 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N dfc084df-46cb-4a7e-b89c-b84ae3634ed3 {http,https} \N \N {/s400-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+658db0dc-6b0d-4559-9f6c-57d70b7792b2 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N dfc084df-46cb-4a7e-b89c-b84ae3634ed3 {http,https} \N \N {/s400-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+04a523c4-1983-47be-a1ab-b9ad0cb558e9 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N dfc084df-46cb-4a7e-b89c-b84ae3634ed3 {http,https} \N \N {/s400-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d7a17d3f-b2d2-4d98-836d-8a07bbfdf567 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N dfc084df-46cb-4a7e-b89c-b84ae3634ed3 {http,https} \N \N {/s400-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+01f3f0ed-6b5c-46e2-9ecc-c63b5614179d 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5c96e4e4-bd3c-458a-aecb-70a0e97258d6 {http,https} \N \N {/s401-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+383e7800-07aa-4b13-9017-c7ecf8f75732 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5c96e4e4-bd3c-458a-aecb-70a0e97258d6 {http,https} \N \N {/s401-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b50a2a4a-5e12-47a5-a60e-ea0da37a2f3d 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5c96e4e4-bd3c-458a-aecb-70a0e97258d6 {http,https} \N \N {/s401-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8378a247-4321-4fa1-8d57-106eb3639f8f 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 5c96e4e4-bd3c-458a-aecb-70a0e97258d6 {http,https} \N \N {/s401-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5cd832f9-aa54-47b8-a52e-73e69a0e1718 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 643ed9d5-7abd-498c-aa27-e54406f62657 {http,https} \N \N {/s402-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2ba96167-2daa-413c-9b07-f9833307fa67 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 643ed9d5-7abd-498c-aa27-e54406f62657 {http,https} \N \N {/s402-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+75c4eb2d-3511-4e86-9892-096bbde16d13 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 643ed9d5-7abd-498c-aa27-e54406f62657 {http,https} \N \N {/s402-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+58874cf9-0216-4378-af62-dc7de48a36b8 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 643ed9d5-7abd-498c-aa27-e54406f62657 {http,https} \N \N {/s402-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cce66afe-de5b-4247-a04f-e464f62ed3d7 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3b43313b-92e3-4a71-89b9-5c94e508ffa4 {http,https} \N \N {/s403-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6859a3a2-9ea5-423c-bf5c-6d9ac7355791 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3b43313b-92e3-4a71-89b9-5c94e508ffa4 {http,https} \N \N {/s403-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+52b0f641-c655-47d1-84e0-5ba8e8751e93 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3b43313b-92e3-4a71-89b9-5c94e508ffa4 {http,https} \N \N {/s403-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ceacde02-edfb-4ae8-b4d5-10bc70de61d0 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 3b43313b-92e3-4a71-89b9-5c94e508ffa4 {http,https} \N \N {/s403-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7156e88a-d9d1-4315-9e1d-5c87a062eccf 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d1f25d2e-1765-431d-b8ce-c971848c140b {http,https} \N \N {/s404-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4dad8fd6-92f0-4661-bb90-98389477dd7d 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d1f25d2e-1765-431d-b8ce-c971848c140b {http,https} \N \N {/s404-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+810fc05e-9ca1-4950-ba8d-a09b39187270 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d1f25d2e-1765-431d-b8ce-c971848c140b {http,https} \N \N {/s404-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aad96b96-b873-48f5-a8a3-1e6124df6216 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d1f25d2e-1765-431d-b8ce-c971848c140b {http,https} \N \N {/s404-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aa1f89cc-75a8-4a7b-8591-f3ba7c13529e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N a986ba78-0f21-4714-98af-030c39a99d98 {http,https} \N \N {/s405-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f4b35db-1ab1-4866-8712-086f8e6a2fec 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N a986ba78-0f21-4714-98af-030c39a99d98 {http,https} \N \N {/s405-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ccbcb619-83b4-4951-a41a-9e20ae65e251 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N a986ba78-0f21-4714-98af-030c39a99d98 {http,https} \N \N {/s405-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+08654641-6d0c-44b2-9c3c-5682b4bb1340 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N a986ba78-0f21-4714-98af-030c39a99d98 {http,https} \N \N {/s405-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+79a35cda-0cc2-418b-94ad-95dc57e1b093 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 186d8c4f-7240-47be-baec-da9793982cfe {http,https} \N \N {/s406-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9351be75-b763-44e2-9dde-c912c4e179f0 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 186d8c4f-7240-47be-baec-da9793982cfe {http,https} \N \N {/s406-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b1473c31-579d-4868-b517-22b046e8503d 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 186d8c4f-7240-47be-baec-da9793982cfe {http,https} \N \N {/s406-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b75a16d6-56a1-46b0-b96a-b765f4350017 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 186d8c4f-7240-47be-baec-da9793982cfe {http,https} \N \N {/s406-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+97fb40c7-904c-4193-9be7-1abe23532019 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 29eb0b4a-38c1-44e3-a342-a738f884bdb8 {http,https} \N \N {/s407-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31220fad-7d79-49a6-bb67-2e941dfd3cd0 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 29eb0b4a-38c1-44e3-a342-a738f884bdb8 {http,https} \N \N {/s407-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+53eb5882-367d-45ef-a7e5-440116bb92f8 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 29eb0b4a-38c1-44e3-a342-a738f884bdb8 {http,https} \N \N {/s407-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9bb107a2-7a71-488c-a15c-9177eb47cd45 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 29eb0b4a-38c1-44e3-a342-a738f884bdb8 {http,https} \N \N {/s407-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cce5650f-ebcf-4398-a62e-16ed830104a8 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d6344072-d70a-419e-b400-f792fd7816a6 {http,https} \N \N {/s408-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+59d3a177-9f2d-4565-9a77-bfefcf96c164 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d6344072-d70a-419e-b400-f792fd7816a6 {http,https} \N \N {/s408-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a50c6467-7fb9-463a-a78e-5b02dde0a523 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d6344072-d70a-419e-b400-f792fd7816a6 {http,https} \N \N {/s408-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dcb58a4a-dc96-4a4b-9ff5-eb56fb81664e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d6344072-d70a-419e-b400-f792fd7816a6 {http,https} \N \N {/s408-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+67cd080f-6a50-41c7-bb3e-5774a3929944 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 65dbc1e9-8bf0-4494-b3e7-c6b6445d805f {http,https} \N \N {/s409-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a69e23c8-6161-41e4-8cd3-cc06b1ff2607 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 65dbc1e9-8bf0-4494-b3e7-c6b6445d805f {http,https} \N \N {/s409-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3ac795e6-ed24-498e-b72c-574e0ca1df09 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 65dbc1e9-8bf0-4494-b3e7-c6b6445d805f {http,https} \N \N {/s409-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a88aef7-b902-4783-ad97-513428000f05 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 65dbc1e9-8bf0-4494-b3e7-c6b6445d805f {http,https} \N \N {/s409-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ca7ccc60-1ce1-42ea-9743-32e2cac6d156 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 82e159a7-b83d-4eb9-9228-26eea20c0301 {http,https} \N \N {/s410-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+85f63859-375e-409c-a720-da75a13aaa26 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 82e159a7-b83d-4eb9-9228-26eea20c0301 {http,https} \N \N {/s410-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1eb10b28-b23b-4140-8e6b-065df19fc5e6 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 82e159a7-b83d-4eb9-9228-26eea20c0301 {http,https} \N \N {/s410-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2fcc0d8-73f4-441f-ad80-3cf1b67420e4 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 82e159a7-b83d-4eb9-9228-26eea20c0301 {http,https} \N \N {/s410-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+25020e19-af27-4047-9818-3b9ccf3f8d94 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 85cab86c-ef60-4b00-ab3a-83649782cbdc {http,https} \N \N {/s411-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ace35e0e-e5b0-42e8-a2d4-44cd4f6be88b 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 85cab86c-ef60-4b00-ab3a-83649782cbdc {http,https} \N \N {/s411-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2d9665e4-118d-4b7d-b402-92bf81971dbe 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 85cab86c-ef60-4b00-ab3a-83649782cbdc {http,https} \N \N {/s411-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b6d6b10f-87e1-4e17-b945-74f98c071448 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 85cab86c-ef60-4b00-ab3a-83649782cbdc {http,https} \N \N {/s411-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5840fd00-3446-43ab-bad9-e5f306bfd1fd 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6d8a4447-dba8-40c4-8fa3-9ea447aa4431 {http,https} \N \N {/s412-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2d6812b-9cee-4238-a979-97cb70f88e5a 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6d8a4447-dba8-40c4-8fa3-9ea447aa4431 {http,https} \N \N {/s412-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+81327c65-dbe9-499b-9c87-a4bf8d7e1af3 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6d8a4447-dba8-40c4-8fa3-9ea447aa4431 {http,https} \N \N {/s412-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cd75f2c7-e8f4-4ace-9d06-816214d24dd2 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 6d8a4447-dba8-40c4-8fa3-9ea447aa4431 {http,https} \N \N {/s412-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+56da08be-da5f-43b0-a57d-39c1c307bb99 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 297aa958-dd8d-4838-8658-21c7a2f6a45c {http,https} \N \N {/s413-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2b204232-7211-441c-9092-095417c7f065 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 297aa958-dd8d-4838-8658-21c7a2f6a45c {http,https} \N \N {/s413-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6eeadf66-273b-4782-a45d-549367043e38 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 297aa958-dd8d-4838-8658-21c7a2f6a45c {http,https} \N \N {/s413-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ac9d5b89-eae8-4f56-a14e-e4aa3cf0131d 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 297aa958-dd8d-4838-8658-21c7a2f6a45c {http,https} \N \N {/s413-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1b844bea-9033-4cb1-a2c6-634820fc8567 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 516d1b3c-20ec-4abe-9d05-7c10f45cc2b7 {http,https} \N \N {/s414-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+461dfe4a-61f0-495b-86a7-8abb9e916648 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 516d1b3c-20ec-4abe-9d05-7c10f45cc2b7 {http,https} \N \N {/s414-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+589265b9-2632-4803-9468-1c493ac14ca1 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 516d1b3c-20ec-4abe-9d05-7c10f45cc2b7 {http,https} \N \N {/s414-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+88caa8a6-bffe-435b-8ee8-b13c57ec33d3 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 516d1b3c-20ec-4abe-9d05-7c10f45cc2b7 {http,https} \N \N {/s414-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bffd14fc-2aff-47ad-8329-0b031c57a7b6 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N c2cfb252-5288-4b94-b4a8-79a8d86e6c7c {http,https} \N \N {/s415-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6cf6f30f-a166-46ca-b420-b4e42ead43ef 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N c2cfb252-5288-4b94-b4a8-79a8d86e6c7c {http,https} \N \N {/s415-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4826ce43-fd72-4290-8f46-cf9079a64a9f 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N c2cfb252-5288-4b94-b4a8-79a8d86e6c7c {http,https} \N \N {/s415-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0b5c2a84-bbf9-45ed-8c3d-1e6c35b5b9b5 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N c2cfb252-5288-4b94-b4a8-79a8d86e6c7c {http,https} \N \N {/s415-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3be50a21-5eac-4560-84bf-35f16456257e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d32ddeef-adf4-43e5-b533-d6218f89194e {http,https} \N \N {/s416-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2d1f7635-e80d-4a5c-ad59-754df502b60e 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d32ddeef-adf4-43e5-b533-d6218f89194e {http,https} \N \N {/s416-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83b4f771-9ac8-432f-be0b-cf7c5a233ad2 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d32ddeef-adf4-43e5-b533-d6218f89194e {http,https} \N \N {/s416-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fe612456-09ef-4714-a074-3c36de689640 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d32ddeef-adf4-43e5-b533-d6218f89194e {http,https} \N \N {/s416-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aad96364-6f16-4578-8419-c52d08be4016 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d735e2a6-44ce-421b-8041-dbeac83b0388 {http,https} \N \N {/s417-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+37affbe9-c9f0-42da-801f-9af9480b5a36 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d735e2a6-44ce-421b-8041-dbeac83b0388 {http,https} \N \N {/s417-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a88dc384-982b-4a2c-9700-5bea758a85c9 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d735e2a6-44ce-421b-8041-dbeac83b0388 {http,https} \N \N {/s417-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a201d66f-a0fe-4f24-8f8e-55fccb90eb25 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N d735e2a6-44ce-421b-8041-dbeac83b0388 {http,https} \N \N {/s417-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a011f41-d99a-4836-8251-a0cec458068a 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 2f34b698-bdc6-4a34-8568-54e2051c301e {http,https} \N \N {/s418-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e4dad1df-04b0-4424-8fbe-53cf792ca530 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 2f34b698-bdc6-4a34-8568-54e2051c301e {http,https} \N \N {/s418-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+27e08bdf-b6f2-4ff0-9dfd-988504c11433 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 2f34b698-bdc6-4a34-8568-54e2051c301e {http,https} \N \N {/s418-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b036ee57-36c2-49f1-a891-8220081f59b2 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 2f34b698-bdc6-4a34-8568-54e2051c301e {http,https} \N \N {/s418-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dba746b6-4d8b-4409-a15f-ae105f8026d7 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 1f25c2c5-b997-474a-82c0-2dfe225b38f7 {http,https} \N \N {/s419-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1bf6a5c3-ee00-4360-b6eb-001a12606257 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 1f25c2c5-b997-474a-82c0-2dfe225b38f7 {http,https} \N \N {/s419-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c0da6fdb-0e2f-47dc-8bb4-783b40b8bf72 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 1f25c2c5-b997-474a-82c0-2dfe225b38f7 {http,https} \N \N {/s419-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c0c748a3-e6bc-4f94-bcbd-26bd0b618c12 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 1f25c2c5-b997-474a-82c0-2dfe225b38f7 {http,https} \N \N {/s419-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+25094cba-976c-462d-8390-050eecf804b2 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 409a0334-ad83-4abe-92bf-9f86cee8e629 {http,https} \N \N {/s420-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7d875813-49ed-48dd-bb45-95d895ca75dc 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 409a0334-ad83-4abe-92bf-9f86cee8e629 {http,https} \N \N {/s420-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a9c3865-8bf4-42d0-8aec-705dfd492387 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 409a0334-ad83-4abe-92bf-9f86cee8e629 {http,https} \N \N {/s420-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d3efc16-1557-486c-a580-f1405863b379 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 409a0334-ad83-4abe-92bf-9f86cee8e629 {http,https} \N \N {/s420-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+685ef39a-44c3-4ff3-a80f-8aede0d29716 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 21a86be9-f740-47d6-aef6-ea678179d442 {http,https} \N \N {/s421-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+42b9812d-1e90-4173-91fe-b5644dc092e1 2022-05-26 09:04:34+00 2022-05-26 09:04:34+00 \N 21a86be9-f740-47d6-aef6-ea678179d442 {http,https} \N \N {/s421-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+862e1cc2-612c-4983-9398-e31d24a74769 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 21a86be9-f740-47d6-aef6-ea678179d442 {http,https} \N \N {/s421-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31eb93b2-8cbf-4b74-9b40-2042c7ff1d4a 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 21a86be9-f740-47d6-aef6-ea678179d442 {http,https} \N \N {/s421-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e246e51f-3229-4a29-9591-35c9aedc356d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dc85040e-5868-4e67-99ae-ae2a83870651 {http,https} \N \N {/s422-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9e975049-6e6c-46b3-8bd9-a8fbdf47b77e 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dc85040e-5868-4e67-99ae-ae2a83870651 {http,https} \N \N {/s422-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6003dc95-e8af-43c6-a916-108476ee2294 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dc85040e-5868-4e67-99ae-ae2a83870651 {http,https} \N \N {/s422-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a3af20e5-798e-40ce-a257-e2a3bc9601f0 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dc85040e-5868-4e67-99ae-ae2a83870651 {http,https} \N \N {/s422-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+796f20e9-9fee-4a38-9ed3-3f878dac9b09 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 83f56af1-9785-4627-8682-5d9f40d9e567 {http,https} \N \N {/s423-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ce65c939-d17b-4abf-ac74-c04354726e3c 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 83f56af1-9785-4627-8682-5d9f40d9e567 {http,https} \N \N {/s423-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3df3e212-70a4-4f03-a487-572fd89c5b9d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 83f56af1-9785-4627-8682-5d9f40d9e567 {http,https} \N \N {/s423-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9281a796-531f-4f56-8e2b-e82ad80f6ab4 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 83f56af1-9785-4627-8682-5d9f40d9e567 {http,https} \N \N {/s423-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f4178e3d-327c-4d18-9705-98327d29fb4d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b8670494-46f7-4ac6-a67b-92662a89eabb {http,https} \N \N {/s424-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9b193f7e-3e1f-47ce-81cb-baa11abad8ea 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b8670494-46f7-4ac6-a67b-92662a89eabb {http,https} \N \N {/s424-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5040e3e7-b96c-4ff0-8aaa-2dae06704791 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b8670494-46f7-4ac6-a67b-92662a89eabb {http,https} \N \N {/s424-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+68ba6e34-a781-4a8b-882e-03fac53367f0 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b8670494-46f7-4ac6-a67b-92662a89eabb {http,https} \N \N {/s424-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+332a858f-f03c-4230-83e8-ef08961739f2 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cb4d87c3-1fb7-4b16-8094-eed4a3d00968 {http,https} \N \N {/s425-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63e6bf30-2271-4d34-aac3-ad36fb6a4a24 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cb4d87c3-1fb7-4b16-8094-eed4a3d00968 {http,https} \N \N {/s425-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ce5b9cdc-4973-41bc-9b31-34cabf0a6669 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cb4d87c3-1fb7-4b16-8094-eed4a3d00968 {http,https} \N \N {/s425-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b68588d8-d53c-4392-8611-94ab67eacc14 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cb4d87c3-1fb7-4b16-8094-eed4a3d00968 {http,https} \N \N {/s425-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f2108d5-5006-483f-98c0-ea742be4e801 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 106044fb-fc87-41f6-9e71-3faffe47e00b {http,https} \N \N {/s426-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ed520698-3eb3-49b7-807d-d398e8c386f5 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 106044fb-fc87-41f6-9e71-3faffe47e00b {http,https} \N \N {/s426-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bfcb594c-3473-41ae-92aa-949571895fdf 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 106044fb-fc87-41f6-9e71-3faffe47e00b {http,https} \N \N {/s426-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+602701ea-004a-440f-8b32-0de658928841 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 106044fb-fc87-41f6-9e71-3faffe47e00b {http,https} \N \N {/s426-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+44779b09-653d-43fb-977a-ab86d3bedb55 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N a88fd1e2-7344-47b5-a7b8-9bd716f94c5d {http,https} \N \N {/s427-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9cbabfe0-14c9-44bf-8380-9d21ce4e8c78 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N a88fd1e2-7344-47b5-a7b8-9bd716f94c5d {http,https} \N \N {/s427-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a898c036-f030-4347-b629-5d26221d2807 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N a88fd1e2-7344-47b5-a7b8-9bd716f94c5d {http,https} \N \N {/s427-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ddb74d4c-be57-4411-83d6-a6f9b593bf5d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N a88fd1e2-7344-47b5-a7b8-9bd716f94c5d {http,https} \N \N {/s427-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3dd511df-0974-4fa4-812b-d617d0aa4e7b 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 53f91d1f-e644-4040-bb9c-009b94cdb8e8 {http,https} \N \N {/s428-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+73058d2b-ceef-486a-8e20-53287ebe6b97 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 53f91d1f-e644-4040-bb9c-009b94cdb8e8 {http,https} \N \N {/s428-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+16a20100-ef5a-4412-b1e6-7bdb520fd215 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 53f91d1f-e644-4040-bb9c-009b94cdb8e8 {http,https} \N \N {/s428-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d22c3097-4d54-4e65-a3ff-e422785ea684 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 53f91d1f-e644-4040-bb9c-009b94cdb8e8 {http,https} \N \N {/s428-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+baec13c8-483c-47eb-9412-5003efcf5560 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dd07fe79-a01b-4e7e-b0d7-2556523cb39e {http,https} \N \N {/s429-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0d48392-1ee3-442d-956b-4e1be1bfb2ea 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dd07fe79-a01b-4e7e-b0d7-2556523cb39e {http,https} \N \N {/s429-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+928a6194-6852-444c-8321-6679bc4d116f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dd07fe79-a01b-4e7e-b0d7-2556523cb39e {http,https} \N \N {/s429-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aa93e1d0-2e0e-4f62-9bb7-979e28c18105 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N dd07fe79-a01b-4e7e-b0d7-2556523cb39e {http,https} \N \N {/s429-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+64bde6f9-51c5-4e41-817f-d1c55f5f65cb 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b2faf9ae-52e2-4dae-a484-7e9978de7057 {http,https} \N \N {/s430-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de4e4f36-bc95-4fd1-954f-4a239a006a0f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b2faf9ae-52e2-4dae-a484-7e9978de7057 {http,https} \N \N {/s430-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+035f23a4-99bc-48b6-934e-273cbeb4c4c3 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b2faf9ae-52e2-4dae-a484-7e9978de7057 {http,https} \N \N {/s430-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d96f636c-6524-48d1-94c3-cb08066fddb7 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N b2faf9ae-52e2-4dae-a484-7e9978de7057 {http,https} \N \N {/s430-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22f8a8a0-fc47-4b1d-9c43-cda860699f25 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 587584bd-581c-4ec6-90a4-4196ebe3e639 {http,https} \N \N {/s431-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f35e1eb-6957-48c2-8b9d-e67189a74e29 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 587584bd-581c-4ec6-90a4-4196ebe3e639 {http,https} \N \N {/s431-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+699001c3-4b00-43c7-a34e-4c1efa3f910b 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 587584bd-581c-4ec6-90a4-4196ebe3e639 {http,https} \N \N {/s431-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c9bd1d4c-bd11-409b-9991-de547fa66154 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 587584bd-581c-4ec6-90a4-4196ebe3e639 {http,https} \N \N {/s431-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+629efa23-6418-428c-9232-056dae0f8a8f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c1e06d08-f053-4e2f-98cb-dfe2b4523fc8 {http,https} \N \N {/s432-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9c8aeeb6-88fd-4512-97a2-b1344be5c973 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c1e06d08-f053-4e2f-98cb-dfe2b4523fc8 {http,https} \N \N {/s432-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d08ec189-3c74-48b0-93ef-a6f37a1bf514 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c1e06d08-f053-4e2f-98cb-dfe2b4523fc8 {http,https} \N \N {/s432-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a5e88bd-38cd-46dc-b77c-995a49f1c0fc 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c1e06d08-f053-4e2f-98cb-dfe2b4523fc8 {http,https} \N \N {/s432-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b4522141-769c-463e-b461-34a464626121 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N ce17ffbe-39d4-4bba-badd-3fd6a51a909b {http,https} \N \N {/s433-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a42961ef-d801-4810-9521-c0e5b00d39fd 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N ce17ffbe-39d4-4bba-badd-3fd6a51a909b {http,https} \N \N {/s433-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a83f503-9745-474b-a1e8-a323ab9111ff 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N ce17ffbe-39d4-4bba-badd-3fd6a51a909b {http,https} \N \N {/s433-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2fa6dc93-4a07-426d-abe9-57ab379ac1be 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N ce17ffbe-39d4-4bba-badd-3fd6a51a909b {http,https} \N \N {/s433-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fe5e88e8-cda5-41ad-af58-514648c3fb53 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N df0f28b8-833d-4962-9750-0e2c7dcf1aef {http,https} \N \N {/s434-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0ccffa33-9e36-46be-a1e1-95703d57c087 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N df0f28b8-833d-4962-9750-0e2c7dcf1aef {http,https} \N \N {/s434-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3897b977-24b3-4d61-aeb7-5da41eea369f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N df0f28b8-833d-4962-9750-0e2c7dcf1aef {http,https} \N \N {/s434-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d3964655-3562-449c-a996-188d928e4416 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N df0f28b8-833d-4962-9750-0e2c7dcf1aef {http,https} \N \N {/s434-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+95226f06-eaa4-4eb5-b0e2-97446f6eaf10 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 42463594-07f9-463b-8d3d-e640679cf9a0 {http,https} \N \N {/s435-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4b35e94a-4a4f-42ff-b535-87a2c952f8f9 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 42463594-07f9-463b-8d3d-e640679cf9a0 {http,https} \N \N {/s435-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+de996ae3-1009-4904-b43f-a8c0719eb142 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 42463594-07f9-463b-8d3d-e640679cf9a0 {http,https} \N \N {/s435-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c29cd9ce-c6df-4966-b9d9-3113cba54214 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 42463594-07f9-463b-8d3d-e640679cf9a0 {http,https} \N \N {/s435-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ac266bff-33ea-4308-98ee-3feffbf0c68d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8dc13325-56ce-4b86-bd36-b090b0f6caab {http,https} \N \N {/s436-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d96be58d-b781-4fe9-aa94-cce5025d99d1 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8dc13325-56ce-4b86-bd36-b090b0f6caab {http,https} \N \N {/s436-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f82a40d3-42fd-45ad-bb65-5d2518933867 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8dc13325-56ce-4b86-bd36-b090b0f6caab {http,https} \N \N {/s436-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c60a482b-ce4e-45f2-a927-f92bf18fbb0e 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8dc13325-56ce-4b86-bd36-b090b0f6caab {http,https} \N \N {/s436-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f4b22302-a261-4a49-ba01-82de71cb8f1f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c629d453-a5a6-431f-8f90-9b27722a415a {http,https} \N \N {/s437-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2e9e6753-7e85-41fd-8d1f-9adb3928d74f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c629d453-a5a6-431f-8f90-9b27722a415a {http,https} \N \N {/s437-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1dc1dbe7-a85c-4a9f-90bd-8d65c484021f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c629d453-a5a6-431f-8f90-9b27722a415a {http,https} \N \N {/s437-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fc73c2b0-4025-4f15-83fb-6dc460aa2f7e 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c629d453-a5a6-431f-8f90-9b27722a415a {http,https} \N \N {/s437-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9e369f00-4fc8-4576-a55f-ae12f08a9dfa 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c265592f-8adf-4f8c-bb4f-1b4a984dc600 {http,https} \N \N {/s438-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b2dff9b6-1050-4831-aff0-a556b5f3dfc9 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c265592f-8adf-4f8c-bb4f-1b4a984dc600 {http,https} \N \N {/s438-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b874a1d4-7d08-4c7b-bf16-d7388c0000dc 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c265592f-8adf-4f8c-bb4f-1b4a984dc600 {http,https} \N \N {/s438-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+037fdcd7-d5af-4e8e-a79b-0282ff6720fb 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c265592f-8adf-4f8c-bb4f-1b4a984dc600 {http,https} \N \N {/s438-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ef456973-296b-4562-8e2e-5cf6fd081f6d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N bbfadf44-58fe-4693-9f6b-f1897ad92eb6 {http,https} \N \N {/s439-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+441cf7fb-a81c-44de-b667-2cd0b0e4ec83 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N bbfadf44-58fe-4693-9f6b-f1897ad92eb6 {http,https} \N \N {/s439-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1b04ac64-689f-43f1-9466-3157ac0f0a95 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N bbfadf44-58fe-4693-9f6b-f1897ad92eb6 {http,https} \N \N {/s439-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f8d12639-4bc3-4d83-a10d-501c0ea50549 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N bbfadf44-58fe-4693-9f6b-f1897ad92eb6 {http,https} \N \N {/s439-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+30a2db7d-800f-4719-8562-168dc1286507 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 515bf1e2-6b17-448a-ad26-6276526a88c2 {http,https} \N \N {/s440-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+845b106b-35b7-48f5-875c-e384c6f6b67e 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 515bf1e2-6b17-448a-ad26-6276526a88c2 {http,https} \N \N {/s440-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+27955626-cbbc-42bd-815b-02e0234af5a8 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 515bf1e2-6b17-448a-ad26-6276526a88c2 {http,https} \N \N {/s440-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bda33765-6241-4fed-b4d7-b633ce66428f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 515bf1e2-6b17-448a-ad26-6276526a88c2 {http,https} \N \N {/s440-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+eb478595-1abe-4bc9-885f-042cf6130695 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 4f1086b3-8849-4d42-a9fb-5395f1cb573f {http,https} \N \N {/s441-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+aabb4603-89c3-4e74-b1ba-35c3db96b301 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 4f1086b3-8849-4d42-a9fb-5395f1cb573f {http,https} \N \N {/s441-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e28134da-413b-450c-a399-87a783ce54ae 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 4f1086b3-8849-4d42-a9fb-5395f1cb573f {http,https} \N \N {/s441-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7302f741-b7c4-428c-85f2-3b1c47203038 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 4f1086b3-8849-4d42-a9fb-5395f1cb573f {http,https} \N \N {/s441-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a02b0fe6-a210-4190-8ec7-e056824aa9d0 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N d0e54e7a-8475-44f5-af06-0852acc18ada {http,https} \N \N {/s442-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8e100cd5-ee9e-4f65-b059-5ae366597489 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N d0e54e7a-8475-44f5-af06-0852acc18ada {http,https} \N \N {/s442-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8df16482-225a-4078-81fa-dad84e01abc4 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N d0e54e7a-8475-44f5-af06-0852acc18ada {http,https} \N \N {/s442-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+35cd220d-170f-42ed-a7ff-c69afcc9bf50 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N d0e54e7a-8475-44f5-af06-0852acc18ada {http,https} \N \N {/s442-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2005f03c-633c-47b1-a600-d074ac298f1d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cedaaa13-f4a0-4aa1-86bd-29f20d10cb17 {http,https} \N \N {/s443-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+63e91ee0-15fe-4538-8b7d-f10744a01e85 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cedaaa13-f4a0-4aa1-86bd-29f20d10cb17 {http,https} \N \N {/s443-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a42d4d9-6676-4b9b-9500-6f9eb4a9450e 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cedaaa13-f4a0-4aa1-86bd-29f20d10cb17 {http,https} \N \N {/s443-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0c772d39-7359-4978-aac2-efa3e9266682 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N cedaaa13-f4a0-4aa1-86bd-29f20d10cb17 {http,https} \N \N {/s443-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0a2a695a-b01b-4105-89a8-46dc8936cc92 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N af2095eb-cb46-45e8-8e62-23c528e8451c {http,https} \N \N {/s444-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5dca14c8-a7b0-4944-b7f7-08ffaaf9ca84 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N af2095eb-cb46-45e8-8e62-23c528e8451c {http,https} \N \N {/s444-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+39518705-d1ee-4023-b9c5-1bf33d9cfd6a 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N af2095eb-cb46-45e8-8e62-23c528e8451c {http,https} \N \N {/s444-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+acf1ec7f-8f26-4733-9d8b-599a71f0748b 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N af2095eb-cb46-45e8-8e62-23c528e8451c {http,https} \N \N {/s444-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cbc05dd0-bea4-4a26-a13e-34c90f60c3db 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 39f8b870-e4a7-4f7c-93ba-7354ffdc3b7a {http,https} \N \N {/s445-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e97f6a04-5013-4d19-85af-d9bb2304e9b7 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 39f8b870-e4a7-4f7c-93ba-7354ffdc3b7a {http,https} \N \N {/s445-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d63846ed-e5c6-4141-acf1-2fb001179132 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 39f8b870-e4a7-4f7c-93ba-7354ffdc3b7a {http,https} \N \N {/s445-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3bf553f4-1aea-44f6-b75a-0ddcd8e4994e 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 39f8b870-e4a7-4f7c-93ba-7354ffdc3b7a {http,https} \N \N {/s445-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+693f2f3a-0157-4896-948c-d964c4fe7d63 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8b196676-5e99-4ffb-9cf7-e59dd42c9b61 {http,https} \N \N {/s446-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a6f8a21-e961-4362-9394-d0ed942b768f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8b196676-5e99-4ffb-9cf7-e59dd42c9b61 {http,https} \N \N {/s446-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+18859324-0c22-40f3-8c10-d3d9c8b6aeb9 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8b196676-5e99-4ffb-9cf7-e59dd42c9b61 {http,https} \N \N {/s446-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4bf7f1a5-5102-48bc-a4de-89fe1fb6d450 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 8b196676-5e99-4ffb-9cf7-e59dd42c9b61 {http,https} \N \N {/s446-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+716db20a-f3e6-4c4e-a3ec-39b98c272af5 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 3ed2e405-1166-499d-84ca-abf27c4420d6 {http,https} \N \N {/s447-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+92ee91d3-befa-4eea-8f02-a6659f9bbe50 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 3ed2e405-1166-499d-84ca-abf27c4420d6 {http,https} \N \N {/s447-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c79bbbe1-a759-45fe-9c43-c05981da2b52 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 3ed2e405-1166-499d-84ca-abf27c4420d6 {http,https} \N \N {/s447-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a23b9326-baac-4524-bafd-cf431f8acf92 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 3ed2e405-1166-499d-84ca-abf27c4420d6 {http,https} \N \N {/s447-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ea7be992-3302-4778-b897-82fab2848357 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 6e94f9f7-f322-4be2-a6e3-25220b00d9f6 {http,https} \N \N {/s448-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7d0f8aee-48aa-416b-b844-1324475985b2 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 6e94f9f7-f322-4be2-a6e3-25220b00d9f6 {http,https} \N \N {/s448-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a3ab15b6-a233-4720-b0ce-18f5d52f616d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 6e94f9f7-f322-4be2-a6e3-25220b00d9f6 {http,https} \N \N {/s448-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+982884e2-8b41-442f-9520-7b5c7bfbc734 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 6e94f9f7-f322-4be2-a6e3-25220b00d9f6 {http,https} \N \N {/s448-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1299cf5e-49fe-4346-815e-f355b5c47a2f 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 2ee7b426-001c-4f81-a4b9-f5f6e94dacd9 {http,https} \N \N {/s449-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3743842-c6ff-464e-9876-5f4f09826103 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 2ee7b426-001c-4f81-a4b9-f5f6e94dacd9 {http,https} \N \N {/s449-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4d3e31d6-54c9-4457-a9fa-42d1d798d474 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 2ee7b426-001c-4f81-a4b9-f5f6e94dacd9 {http,https} \N \N {/s449-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5cc5a134-3225-4ffe-9e54-cb108db54ff9 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 2ee7b426-001c-4f81-a4b9-f5f6e94dacd9 {http,https} \N \N {/s449-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+74a99ab8-12cf-42ef-98ae-bab2200d712d 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c235ddd9-4a8b-4ed4-996d-f32d97c2febf {http,https} \N \N {/s450-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7b6edd61-322c-4014-b0eb-ba31540657d3 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c235ddd9-4a8b-4ed4-996d-f32d97c2febf {http,https} \N \N {/s450-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f5c4836-3803-4015-9df3-d4701d9da5f5 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c235ddd9-4a8b-4ed4-996d-f32d97c2febf {http,https} \N \N {/s450-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8e9069f5-1f20-4b38-9a10-61bf35aa17b2 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N c235ddd9-4a8b-4ed4-996d-f32d97c2febf {http,https} \N \N {/s450-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d5391c92-a824-48d8-acb5-afb842d854d4 2022-05-26 09:04:35+00 2022-05-26 09:04:35+00 \N 3443f990-ed97-482a-b60d-f9a4fae6dce7 {http,https} \N \N {/s451-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e674c13d-c97b-40ad-912b-0b3ddbafbc1b 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 3443f990-ed97-482a-b60d-f9a4fae6dce7 {http,https} \N \N {/s451-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b168028b-8819-4141-8ed7-840efb851df0 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 3443f990-ed97-482a-b60d-f9a4fae6dce7 {http,https} \N \N {/s451-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+459abb4f-1140-44e4-8155-03a2031b3f0c 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 3443f990-ed97-482a-b60d-f9a4fae6dce7 {http,https} \N \N {/s451-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a15175ec-ed00-4bc7-a9f1-feda48fa738e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N bf3887ae-ebac-4278-aa88-b211be9a6ef4 {http,https} \N \N {/s452-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2b703033-8e5c-40f9-aca8-f3482b927a07 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N bf3887ae-ebac-4278-aa88-b211be9a6ef4 {http,https} \N \N {/s452-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+362732aa-8820-46f1-ad5a-11088daf1d95 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N bf3887ae-ebac-4278-aa88-b211be9a6ef4 {http,https} \N \N {/s452-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a4067a1b-a7de-4444-bb97-d3f20f9d922e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N bf3887ae-ebac-4278-aa88-b211be9a6ef4 {http,https} \N \N {/s452-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1828cabb-c68f-493f-b289-e03040fb5bca 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5db483a-11d5-4fb7-b977-ddb1b55b6923 {http,https} \N \N {/s453-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e2121668-7f21-4951-81a0-315e7104858c 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5db483a-11d5-4fb7-b977-ddb1b55b6923 {http,https} \N \N {/s453-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f900b38-e6e0-419f-87cb-dc18ef0fc407 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5db483a-11d5-4fb7-b977-ddb1b55b6923 {http,https} \N \N {/s453-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e0e09eaa-0951-4d65-b0bb-43076d4d659e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5db483a-11d5-4fb7-b977-ddb1b55b6923 {http,https} \N \N {/s453-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cfc3836f-6a6e-4b12-8b40-872258301b4a 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7560adfa-0d51-42e6-b727-78821e9404f8 {http,https} \N \N {/s454-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c75d182b-0b2e-450e-ae09-213438cd85aa 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7560adfa-0d51-42e6-b727-78821e9404f8 {http,https} \N \N {/s454-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+24d8a298-f52e-4f92-8a0d-b8804c489376 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7560adfa-0d51-42e6-b727-78821e9404f8 {http,https} \N \N {/s454-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83ca008b-c45f-40fc-a7e3-76e161eebb31 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7560adfa-0d51-42e6-b727-78821e9404f8 {http,https} \N \N {/s454-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7b5bb779-02ea-446d-97d7-31d60246df94 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N efe7075c-0084-4620-976d-57dcbaf3893b {http,https} \N \N {/s455-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a3a831ec-aab7-4f9c-910b-2baf43fffceb 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N efe7075c-0084-4620-976d-57dcbaf3893b {http,https} \N \N {/s455-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d80258d8-4588-41ad-8d2e-b092e995f875 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N efe7075c-0084-4620-976d-57dcbaf3893b {http,https} \N \N {/s455-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fb82fc75-0533-4801-8826-d9ef4c07b9fa 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N efe7075c-0084-4620-976d-57dcbaf3893b {http,https} \N \N {/s455-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b5f48d1e-4613-42d3-adc0-3917b542dc8c 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f062ee0d-1d60-4ac5-bf80-fad59a54306f {http,https} \N \N {/s456-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+fc84f22c-9877-4151-866e-4611f73aba61 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f062ee0d-1d60-4ac5-bf80-fad59a54306f {http,https} \N \N {/s456-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9eb2fb93-7229-4f2d-b719-0ea3ae35732e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f062ee0d-1d60-4ac5-bf80-fad59a54306f {http,https} \N \N {/s456-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b9205cd6-7d62-498e-a7e4-934491693c89 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f062ee0d-1d60-4ac5-bf80-fad59a54306f {http,https} \N \N {/s456-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f5e72d25-7288-4835-bb58-b9b46844e186 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 838a3bbf-b6e9-4174-9e2f-4c5903f85b51 {http,https} \N \N {/s457-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c058491d-f008-4be7-b154-c2080f177cdf 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 838a3bbf-b6e9-4174-9e2f-4c5903f85b51 {http,https} \N \N {/s457-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+75dc36cc-8f3b-4130-a3f9-d7c75704107f 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 838a3bbf-b6e9-4174-9e2f-4c5903f85b51 {http,https} \N \N {/s457-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1e37f25f-37e4-493a-9401-0f11e083923d 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 838a3bbf-b6e9-4174-9e2f-4c5903f85b51 {http,https} \N \N {/s457-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9ef8a655-ac65-46e8-ab96-98a5ca2d687b 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1813a575-32ba-4c94-99a5-19295b0921de {http,https} \N \N {/s458-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+21a0ed20-8689-42d8-b1bc-3d949638ffc7 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1813a575-32ba-4c94-99a5-19295b0921de {http,https} \N \N {/s458-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+880c58b3-ea22-4f40-9e81-98b5ba83f64d 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1813a575-32ba-4c94-99a5-19295b0921de {http,https} \N \N {/s458-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22d3e5b0-d209-4248-ad44-5e8308287366 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1813a575-32ba-4c94-99a5-19295b0921de {http,https} \N \N {/s458-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0bac6e77-a2ed-48f8-a22e-47289c607c67 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7aff390f-97f8-4e64-9b95-c85a9002c33c {http,https} \N \N {/s459-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31e10549-c69a-4a12-8fee-ec0980eff22d 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7aff390f-97f8-4e64-9b95-c85a9002c33c {http,https} \N \N {/s459-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1157895c-0bc6-4e8e-aca8-3cacfb38a2e3 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7aff390f-97f8-4e64-9b95-c85a9002c33c {http,https} \N \N {/s459-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ed80a6be-75c3-40a7-9260-e37b02953e21 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7aff390f-97f8-4e64-9b95-c85a9002c33c {http,https} \N \N {/s459-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+11fa8193-b685-4daa-818f-050e1ee78a94 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c6298096-10b7-441c-9688-4695b88a8660 {http,https} \N \N {/s460-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3487f8a1-8c7d-43a1-8841-0bcdba3367cf 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c6298096-10b7-441c-9688-4695b88a8660 {http,https} \N \N {/s460-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8d19797e-fdaf-4506-ac6e-9e0f4ee38b2e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c6298096-10b7-441c-9688-4695b88a8660 {http,https} \N \N {/s460-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31cc408d-655a-459b-a9ab-3199d73bcf8a 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c6298096-10b7-441c-9688-4695b88a8660 {http,https} \N \N {/s460-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a428bb72-a27d-4ec7-8bf1-bed2c543b6f7 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N dada2f21-3866-4778-a319-a91f82f8ad76 {http,https} \N \N {/s461-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c97ce96e-a8c1-4637-9dfd-1c416ae616a5 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N dada2f21-3866-4778-a319-a91f82f8ad76 {http,https} \N \N {/s461-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9384c3e2-f1e1-4854-83df-d11f9b30344e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N dada2f21-3866-4778-a319-a91f82f8ad76 {http,https} \N \N {/s461-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+070b854f-a709-428c-808b-c2f116c28254 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N dada2f21-3866-4778-a319-a91f82f8ad76 {http,https} \N \N {/s461-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8a09c21e-38a6-4b36-9127-314d6e6c3b72 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5016d6d-f10c-4846-83d5-7bf231c044d3 {http,https} \N \N {/s462-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5d98f7d4-5de2-4f9c-84fe-fdb3236bd303 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5016d6d-f10c-4846-83d5-7bf231c044d3 {http,https} \N \N {/s462-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f0176518-e3ae-4658-ac29-dc59f29c2485 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5016d6d-f10c-4846-83d5-7bf231c044d3 {http,https} \N \N {/s462-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+93e08cc0-3fb4-4bd4-9592-adce2a1684e4 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f5016d6d-f10c-4846-83d5-7bf231c044d3 {http,https} \N \N {/s462-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6ad81b72-200f-454c-ae5f-6a817a257a55 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7463f25e-841f-4e23-9fb3-4dbe0c2554d2 {http,https} \N \N {/s463-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc92a638-89e7-4677-afa7-2a8cb7ee9ab4 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7463f25e-841f-4e23-9fb3-4dbe0c2554d2 {http,https} \N \N {/s463-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+22f79c49-0d58-4997-a244-a38f94acce12 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7463f25e-841f-4e23-9fb3-4dbe0c2554d2 {http,https} \N \N {/s463-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+409dbe83-1650-4149-9b40-8d03aaf9b607 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7463f25e-841f-4e23-9fb3-4dbe0c2554d2 {http,https} \N \N {/s463-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4ddaca3a-02d7-4ea8-a73c-762cfa3462b6 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1e87a29f-8009-41bd-8b71-f8800f1dab1e {http,https} \N \N {/s464-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ddb714fc-1535-49cb-8590-96b4553fa6f4 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1e87a29f-8009-41bd-8b71-f8800f1dab1e {http,https} \N \N {/s464-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19fb2a92-672b-49f1-a1e5-7c95e865ee76 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1e87a29f-8009-41bd-8b71-f8800f1dab1e {http,https} \N \N {/s464-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+57e61c94-cd64-4669-a33b-4a6105a034cf 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 1e87a29f-8009-41bd-8b71-f8800f1dab1e {http,https} \N \N {/s464-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3bc338fe-1d42-499e-817f-98c71292d864 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 30e14345-9d6a-42c1-b33f-59cb014e5b68 {http,https} \N \N {/s465-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2ea78bee-9b42-4346-9900-57400da07b37 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 30e14345-9d6a-42c1-b33f-59cb014e5b68 {http,https} \N \N {/s465-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+caeb38de-87f3-47fc-8222-508d38f7c660 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 30e14345-9d6a-42c1-b33f-59cb014e5b68 {http,https} \N \N {/s465-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+13bfbc09-4bc2-4b21-9c51-c75df526211c 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 30e14345-9d6a-42c1-b33f-59cb014e5b68 {http,https} \N \N {/s465-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+92cc82f5-3599-4cc9-b5fc-43fca3c9dceb 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 86c6fa66-322e-487a-8999-ecc03a830fd3 {http,https} \N \N {/s466-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+92e36d2d-f87c-45f1-a324-70453d608e51 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 86c6fa66-322e-487a-8999-ecc03a830fd3 {http,https} \N \N {/s466-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1b1c60ca-05d2-4415-b2ff-3cbddde1e5a4 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 86c6fa66-322e-487a-8999-ecc03a830fd3 {http,https} \N \N {/s466-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c3677645-9805-4e82-af47-e9a963d16091 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 86c6fa66-322e-487a-8999-ecc03a830fd3 {http,https} \N \N {/s466-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c7e10fe-1939-4813-ab29-e4795edbc5ff 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 35847d15-de55-4a1b-9493-0d691a83a641 {http,https} \N \N {/s467-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+693b8d67-5d36-40fe-89ec-3a53b4272463 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 35847d15-de55-4a1b-9493-0d691a83a641 {http,https} \N \N {/s467-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e49b36e7-fef7-4ba3-890d-c5471138f2ed 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 35847d15-de55-4a1b-9493-0d691a83a641 {http,https} \N \N {/s467-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4cf67451-f2aa-4974-b700-30a8951866a8 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 35847d15-de55-4a1b-9493-0d691a83a641 {http,https} \N \N {/s467-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ca6253c1-3a62-413e-b97a-43399244e3ff 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f18b3241-50bd-45b5-8c61-8858473e10fb {http,https} \N \N {/s468-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5e8377b3-4bcb-4fb9-b7b1-2013d0645ec7 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f18b3241-50bd-45b5-8c61-8858473e10fb {http,https} \N \N {/s468-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1df52a05-4f48-4af3-8cdf-0da33141a4e9 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f18b3241-50bd-45b5-8c61-8858473e10fb {http,https} \N \N {/s468-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+283da355-d78e-415c-851a-165af8070103 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f18b3241-50bd-45b5-8c61-8858473e10fb {http,https} \N \N {/s468-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d46e10e2-5c30-4fad-af2b-3e31ce034d6d 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 3f90d40a-eef1-4a6b-953c-6919087c9b6b {http,https} \N \N {/s469-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5ef1787b-24ec-4a50-93d7-e6c2175201a0 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 3f90d40a-eef1-4a6b-953c-6919087c9b6b {http,https} \N \N {/s469-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+902f1a1e-26f0-49d6-bdb0-ac94d57085b4 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 3f90d40a-eef1-4a6b-953c-6919087c9b6b {http,https} \N \N {/s469-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0d4245e3-e09f-47f6-8e85-095dca32ab4e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 3f90d40a-eef1-4a6b-953c-6919087c9b6b {http,https} \N \N {/s469-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3e4ca35e-f94b-458d-a588-668c78320040 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c81f7cfe-c388-4731-88f9-f3eccc0e1aae {http,https} \N \N {/s470-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+afb9c5ec-ad49-458f-87da-8f9e74ebce0d 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c81f7cfe-c388-4731-88f9-f3eccc0e1aae {http,https} \N \N {/s470-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+abd31258-aa72-4fe1-bdff-397abfb64934 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c81f7cfe-c388-4731-88f9-f3eccc0e1aae {http,https} \N \N {/s470-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6c86a7a6-e243-41da-bbd8-c34bba6381f0 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N c81f7cfe-c388-4731-88f9-f3eccc0e1aae {http,https} \N \N {/s470-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+30b83f00-8969-44f5-87c2-f88e886a7bc8 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 54f45fd9-b956-4dd8-a9a2-aa025395fe9b {http,https} \N \N {/s471-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4f579d4b-bfab-42f0-bf5e-92ba2891066b 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 54f45fd9-b956-4dd8-a9a2-aa025395fe9b {http,https} \N \N {/s471-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ef8bf65e-0847-410b-97b8-78a140284248 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 54f45fd9-b956-4dd8-a9a2-aa025395fe9b {http,https} \N \N {/s471-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9e71f4aa-f7fc-4a66-9e87-840479699e8d 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 54f45fd9-b956-4dd8-a9a2-aa025395fe9b {http,https} \N \N {/s471-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+91131f39-d683-4f10-abdb-c8ee69fe26a2 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f0f92b13-e8a2-4208-af35-88c2f57053ed {http,https} \N \N {/s472-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+534e8382-13c5-4bf2-b7b5-b665cf70a8f8 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f0f92b13-e8a2-4208-af35-88c2f57053ed {http,https} \N \N {/s472-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8802df97-7210-454c-918e-a6b5138bdcaa 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f0f92b13-e8a2-4208-af35-88c2f57053ed {http,https} \N \N {/s472-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+19f9eb11-c202-4b14-ab7c-cd0971a424db 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N f0f92b13-e8a2-4208-af35-88c2f57053ed {http,https} \N \N {/s472-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+97772726-85c5-4469-a489-e862aa6bddb8 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 50b2eea6-fcae-41c7-872a-7f725aad8f68 {http,https} \N \N {/s473-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a5fc7fe6-cb38-4c40-888d-b829e1d2eb0c 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 50b2eea6-fcae-41c7-872a-7f725aad8f68 {http,https} \N \N {/s473-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6e96309a-1c5e-416f-94b9-ae94f9451a6d 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 50b2eea6-fcae-41c7-872a-7f725aad8f68 {http,https} \N \N {/s473-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+61ca5840-595c-4661-934a-327e4a15640b 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 50b2eea6-fcae-41c7-872a-7f725aad8f68 {http,https} \N \N {/s473-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+00c6602a-885b-441c-ad13-39eb3c1fda8c 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5d22741a-9f70-4978-a113-4e3370595e14 {http,https} \N \N {/s474-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8538e410-547d-4af1-a5e4-a3e7491b64ce 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5d22741a-9f70-4978-a113-4e3370595e14 {http,https} \N \N {/s474-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+516eeb29-4c13-4502-84bd-cbaff4b5e540 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5d22741a-9f70-4978-a113-4e3370595e14 {http,https} \N \N {/s474-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e77d4b44-4733-493a-975b-9762f987d109 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5d22741a-9f70-4978-a113-4e3370595e14 {http,https} \N \N {/s474-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4e7b3320-325c-4c94-8967-6a3de95dea3e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5e9f240d-6e21-4393-b37c-f9f1e8ca70f3 {http,https} \N \N {/s475-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ea66dc1a-9b79-402e-8585-01afeab94962 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5e9f240d-6e21-4393-b37c-f9f1e8ca70f3 {http,https} \N \N {/s475-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e2d661f8-add0-4cd3-a766-aa3152afbf2e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5e9f240d-6e21-4393-b37c-f9f1e8ca70f3 {http,https} \N \N {/s475-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f9dd2af8-4d40-4368-93a4-e80590f59d0e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 5e9f240d-6e21-4393-b37c-f9f1e8ca70f3 {http,https} \N \N {/s475-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+90010a98-3ee3-46d2-9767-f80944e8c593 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 84d0828f-fe77-41f1-928e-11706edb8821 {http,https} \N \N {/s476-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+80be433d-83b1-4635-a8f9-825da2430b41 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 84d0828f-fe77-41f1-928e-11706edb8821 {http,https} \N \N {/s476-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5418854d-e234-45fd-8312-d518a6ef7b41 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 84d0828f-fe77-41f1-928e-11706edb8821 {http,https} \N \N {/s476-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f6d6a613-de42-499f-b225-77580c97ec89 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 84d0828f-fe77-41f1-928e-11706edb8821 {http,https} \N \N {/s476-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9762fb31-d4b9-4430-9b19-3e28edee92cd 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7c9d3f4c-4e57-450e-b12f-7db6ebcb9aea {http,https} \N \N {/s477-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5f7ad1f4-1385-423c-a952-bbb9bd2be874 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7c9d3f4c-4e57-450e-b12f-7db6ebcb9aea {http,https} \N \N {/s477-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d974ac69-db43-4e85-9a87-f9342fe8d912 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7c9d3f4c-4e57-450e-b12f-7db6ebcb9aea {http,https} \N \N {/s477-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d44df5f8-a07c-4ff5-9625-35526371b822 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 7c9d3f4c-4e57-450e-b12f-7db6ebcb9aea {http,https} \N \N {/s477-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1830c64f-60d2-44fd-b9e4-0729764c033e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N b1f4f818-0f47-4372-868c-df50e9603ed0 {http,https} \N \N {/s478-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+83588352-b2c2-4572-acdc-65b246a782cd 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N b1f4f818-0f47-4372-868c-df50e9603ed0 {http,https} \N \N {/s478-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+78aa5f81-0230-4005-8b32-b98a4d9e79e5 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N b1f4f818-0f47-4372-868c-df50e9603ed0 {http,https} \N \N {/s478-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b32d93cc-f2db-4337-98c8-ad29cf07af27 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N b1f4f818-0f47-4372-868c-df50e9603ed0 {http,https} \N \N {/s478-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+227095bd-7f4a-4260-bc8e-3f0e483a60a7 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N ea4910d2-9eaa-4e94-8f10-94d0da66aa12 {http,https} \N \N {/s479-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f2d72654-4dbe-418e-81f1-b7f57f6010a2 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N ea4910d2-9eaa-4e94-8f10-94d0da66aa12 {http,https} \N \N {/s479-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bc7e358a-b8eb-4243-9ffe-d23ac5f84d0e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N ea4910d2-9eaa-4e94-8f10-94d0da66aa12 {http,https} \N \N {/s479-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9d861fc6-747d-4703-9167-c5f0ba831697 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N ea4910d2-9eaa-4e94-8f10-94d0da66aa12 {http,https} \N \N {/s479-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d885bdcd-efe2-4188-aaf3-ba94d761876a 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 84164c99-8064-4616-9b89-4ad2cd3ee6da {http,https} \N \N {/s480-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e04162d2-1d25-42e8-9974-be98ae62fa91 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 84164c99-8064-4616-9b89-4ad2cd3ee6da {http,https} \N \N {/s480-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+72075bd9-b063-4a57-af12-3a4a88828b3e 2022-05-26 09:04:36+00 2022-05-26 09:04:36+00 \N 84164c99-8064-4616-9b89-4ad2cd3ee6da {http,https} \N \N {/s480-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0af1158f-9fc4-4ece-a444-d11bd29b730c 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 84164c99-8064-4616-9b89-4ad2cd3ee6da {http,https} \N \N {/s480-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5d61baba-08f7-41b2-906d-af28e90761d7 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 64f3861f-7ec7-45bf-a781-73de35a51bf3 {http,https} \N \N {/s481-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b58a7295-19fe-4862-8636-af354002176e 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 64f3861f-7ec7-45bf-a781-73de35a51bf3 {http,https} \N \N {/s481-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c27c93de-efe2-4751-8c68-704590169272 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 64f3861f-7ec7-45bf-a781-73de35a51bf3 {http,https} \N \N {/s481-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e49dc496-bbf0-4744-913e-b4c93011ef7c 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 64f3861f-7ec7-45bf-a781-73de35a51bf3 {http,https} \N \N {/s481-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+31b5fbc7-e064-424b-8913-0237f253d47d 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 0501b4de-a562-45ac-a4f8-ca0b0a5f2be4 {http,https} \N \N {/s482-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f5a41a52-afcc-4559-8d58-a02dd7eb4c19 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 0501b4de-a562-45ac-a4f8-ca0b0a5f2be4 {http,https} \N \N {/s482-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a4cd39a9-79c6-40ae-86c6-d43961fe2f88 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 0501b4de-a562-45ac-a4f8-ca0b0a5f2be4 {http,https} \N \N {/s482-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b7de46b0-d84d-4ec9-a5fe-58e76bd17f38 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 0501b4de-a562-45ac-a4f8-ca0b0a5f2be4 {http,https} \N \N {/s482-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a9aa0edb-7c39-4e31-aedd-67c612e0d649 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N edf40205-69ee-4f3b-ba0c-09d70531b17b {http,https} \N \N {/s483-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+57980eec-3861-4b4a-b1a2-a0e3bbbbffd9 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N edf40205-69ee-4f3b-ba0c-09d70531b17b {http,https} \N \N {/s483-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+405ceb75-7c44-49c3-aaa7-806c7518a0a8 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N edf40205-69ee-4f3b-ba0c-09d70531b17b {http,https} \N \N {/s483-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+89a3c416-e757-4363-9c83-bb2dbe801c02 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N edf40205-69ee-4f3b-ba0c-09d70531b17b {http,https} \N \N {/s483-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a625b1a2-07c7-4f1f-aafa-47dec58a5e65 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N f18530a1-b79f-404c-97b5-c8cb7d4df0d3 {http,https} \N \N {/s484-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d6f362a2-87fa-4e66-a1ed-9fe48088b2ca 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N f18530a1-b79f-404c-97b5-c8cb7d4df0d3 {http,https} \N \N {/s484-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+294c3258-e1fd-4e94-8054-d680c05c0279 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N f18530a1-b79f-404c-97b5-c8cb7d4df0d3 {http,https} \N \N {/s484-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+97e87056-b434-49f0-bab5-7bad670c1c4c 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N f18530a1-b79f-404c-97b5-c8cb7d4df0d3 {http,https} \N \N {/s484-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bcedcdfe-d236-4679-84a0-841a71f3e905 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6b7f220c-1df2-41b3-9ea3-a6bd5ece4a4f {http,https} \N \N {/s485-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+20ca2aa9-96af-43c7-a0f9-d404bc537b6c 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6b7f220c-1df2-41b3-9ea3-a6bd5ece4a4f {http,https} \N \N {/s485-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bdc1037c-1e47-43ed-b82a-a54cea48ffdb 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6b7f220c-1df2-41b3-9ea3-a6bd5ece4a4f {http,https} \N \N {/s485-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+436a2d1b-66be-49cd-9748-0fcd0d982db4 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6b7f220c-1df2-41b3-9ea3-a6bd5ece4a4f {http,https} \N \N {/s485-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6922cc8a-c642-4165-8479-31327ac0abfc 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 06b00f42-c69b-4243-8506-582504283fb7 {http,https} \N \N {/s486-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f3c32d74-ceee-4cd8-bbc8-d1f908e80eaa 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 06b00f42-c69b-4243-8506-582504283fb7 {http,https} \N \N {/s486-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+e3cf12f4-da14-4f3e-905c-479914468396 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 06b00f42-c69b-4243-8506-582504283fb7 {http,https} \N \N {/s486-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9dff2046-de1f-4009-90b9-7be7bf99b487 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 06b00f42-c69b-4243-8506-582504283fb7 {http,https} \N \N {/s486-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+958190df-2bcd-4965-a530-93c3fd16554c 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 9fa2ce85-2954-470e-9a8f-b80a94d18b5c {http,https} \N \N {/s487-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6d2a94aa-d74d-4849-8c26-251b29b8e701 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 9fa2ce85-2954-470e-9a8f-b80a94d18b5c {http,https} \N \N {/s487-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+02886cc1-42d3-4b55-bc1e-ad78a366d1b1 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 9fa2ce85-2954-470e-9a8f-b80a94d18b5c {http,https} \N \N {/s487-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9d74ce27-9141-43bb-a072-0c7df671c5bd 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 9fa2ce85-2954-470e-9a8f-b80a94d18b5c {http,https} \N \N {/s487-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8ba7ede1-e414-4d2b-9840-2655b34c92ea 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 690744c2-57e5-458b-aa9c-eec197957ecc {http,https} \N \N {/s488-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d2918e6e-c2d0-48e9-b36c-336710f3d078 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 690744c2-57e5-458b-aa9c-eec197957ecc {http,https} \N \N {/s488-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+169bf08d-00cf-4209-baff-ff9ecc883977 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 690744c2-57e5-458b-aa9c-eec197957ecc {http,https} \N \N {/s488-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b2e1d473-5314-4dbe-b583-04ec6d4730a7 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 690744c2-57e5-458b-aa9c-eec197957ecc {http,https} \N \N {/s488-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bbf9c50c-f4b3-415a-bf15-9089f84cf322 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 4a74034a-2448-42f4-98d3-dc1fe050f6ce {http,https} \N \N {/s489-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b1ef0d2b-2454-42d4-bd8b-b0fa58a927b0 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 4a74034a-2448-42f4-98d3-dc1fe050f6ce {http,https} \N \N {/s489-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+4358263d-ff4c-4a06-a0bb-d4db3dee6760 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 4a74034a-2448-42f4-98d3-dc1fe050f6ce {http,https} \N \N {/s489-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3c9becf1-889c-42cc-b80b-9e875f07f91a 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 4a74034a-2448-42f4-98d3-dc1fe050f6ce {http,https} \N \N {/s489-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6f810c20-bfe2-49e7-9eac-52b581e91df7 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c4507468-ff51-4d6f-977f-0969cca30830 {http,https} \N \N {/s490-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+3e5b3cf6-9cbb-4258-93b0-6b4058aab21b 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c4507468-ff51-4d6f-977f-0969cca30830 {http,https} \N \N {/s490-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9254b00b-e706-456f-a0a2-b0982568526b 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c4507468-ff51-4d6f-977f-0969cca30830 {http,https} \N \N {/s490-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b196ce2a-423d-4a40-b89b-0cada79c24b1 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c4507468-ff51-4d6f-977f-0969cca30830 {http,https} \N \N {/s490-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0469b9be-1eb9-4769-a3a3-4a6b2ac11f3d 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c865afc-9439-411c-ade4-6fd8ac429c07 {http,https} \N \N {/s491-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6a70ee41-c184-43ef-ab43-28ae6362fcfc 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c865afc-9439-411c-ade4-6fd8ac429c07 {http,https} \N \N {/s491-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d9e3ace8-afd2-4d21-936a-18a8a36eee98 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c865afc-9439-411c-ade4-6fd8ac429c07 {http,https} \N \N {/s491-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c3051e9f-9b15-4200-8c55-32e5f5de4db2 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c865afc-9439-411c-ade4-6fd8ac429c07 {http,https} \N \N {/s491-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+57d989e7-a5bb-415c-a662-5d395092e40e 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N e04db553-36a3-468d-82b4-938514fc8cdb {http,https} \N \N {/s492-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+be81249d-b3ff-437a-b97f-2d90ed894210 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N e04db553-36a3-468d-82b4-938514fc8cdb {http,https} \N \N {/s492-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b5760cbe-8c1a-4d3c-ba0b-5f1f525ffc19 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N e04db553-36a3-468d-82b4-938514fc8cdb {http,https} \N \N {/s492-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+28b3c04b-9586-4612-90de-e274a0ddc863 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N e04db553-36a3-468d-82b4-938514fc8cdb {http,https} \N \N {/s492-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2349d849-97c4-4779-8899-e92411c04986 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ecaca662-b04b-474b-a038-c185ac99a3e1 {http,https} \N \N {/s493-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+48795b76-6f8d-45d5-8950-74c60e0d7df1 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ecaca662-b04b-474b-a038-c185ac99a3e1 {http,https} \N \N {/s493-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+36a4c536-7342-430e-8346-c4fc17ff487a 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ecaca662-b04b-474b-a038-c185ac99a3e1 {http,https} \N \N {/s493-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+907f153a-b5e2-4c95-bb66-f6ad726270c0 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ecaca662-b04b-474b-a038-c185ac99a3e1 {http,https} \N \N {/s493-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+d4faaf1a-9e86-4a49-b1e7-4565b776d84b 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3c19f673-974e-4d27-8aa8-c8b3be9a268a {http,https} \N \N {/s494-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+05e5e286-865b-4f6c-bb73-235808c32eb9 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3c19f673-974e-4d27-8aa8-c8b3be9a268a {http,https} \N \N {/s494-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ce3ff41e-8aa4-46cd-872e-8e9f55f72c0a 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3c19f673-974e-4d27-8aa8-c8b3be9a268a {http,https} \N \N {/s494-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+b3524c08-b846-4546-882f-cc6207e90183 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3c19f673-974e-4d27-8aa8-c8b3be9a268a {http,https} \N \N {/s494-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a06facca-91a6-4a98-b3a9-e51484166998 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c5851b2-0b70-4fd8-9d95-b5f60e89b8d8 {http,https} \N \N {/s495-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8e5dc74b-4585-4417-9444-6e0d185466dc 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c5851b2-0b70-4fd8-9d95-b5f60e89b8d8 {http,https} \N \N {/s495-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9b9e6e65-8544-4f89-a19b-16ddc70b1f52 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c5851b2-0b70-4fd8-9d95-b5f60e89b8d8 {http,https} \N \N {/s495-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9f35ed1f-4138-4640-b127-43dd0a528965 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 6c5851b2-0b70-4fd8-9d95-b5f60e89b8d8 {http,https} \N \N {/s495-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+415b2561-a1e7-4e05-9e86-3c44a0edb91a 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ca7691e7-644f-4503-8661-255efc4f2d73 {http,https} \N \N {/s496-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f581e64d-fc6f-4f91-8bbe-600232ec7d3e 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ca7691e7-644f-4503-8661-255efc4f2d73 {http,https} \N \N {/s496-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+6da5537f-8a92-4b9b-848e-d1864069f23c 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ca7691e7-644f-4503-8661-255efc4f2d73 {http,https} \N \N {/s496-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+5031154c-ed28-400a-b134-c9af8a782571 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N ca7691e7-644f-4503-8661-255efc4f2d73 {http,https} \N \N {/s496-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+8f366d8c-728c-4eac-921a-d62ec110631a 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c520c41e-eaac-436b-8943-9d96b749a386 {http,https} \N \N {/s497-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+ba697728-5e97-46ff-8bb8-b5b90a96a8f0 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c520c41e-eaac-436b-8943-9d96b749a386 {http,https} \N \N {/s497-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+481ffcdf-5d20-42de-a6c2-df0a613f7d7f 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c520c41e-eaac-436b-8943-9d96b749a386 {http,https} \N \N {/s497-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a0d9909b-5c47-4ed6-bdee-d0b1ff643370 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N c520c41e-eaac-436b-8943-9d96b749a386 {http,https} \N \N {/s497-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+2c2f7c68-48a6-4629-85b7-17f62ed9f218 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 35071e24-8e47-4af5-adfd-b91431777cfb {http,https} \N \N {/s498-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+bef6af9d-3386-434d-b1d7-65d1c330c453 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 35071e24-8e47-4af5-adfd-b91431777cfb {http,https} \N \N {/s498-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+a39ba195-5d74-485b-8997-166fb79f6fb4 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 35071e24-8e47-4af5-adfd-b91431777cfb {http,https} \N \N {/s498-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+cd0d5bf9-4493-43ef-9a0e-b3035651ddb9 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 35071e24-8e47-4af5-adfd-b91431777cfb {http,https} \N \N {/s498-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+1b476ff0-69c7-4274-92b1-cc56e2ec5b95 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3206e638-1f43-47b7-8b36-e5a70cf785b2 {http,https} \N \N {/s499-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+84196bb5-7d3d-42ee-b404-af4409e35c66 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3206e638-1f43-47b7-8b36-e5a70cf785b2 {http,https} \N \N {/s499-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+c51be90b-9f47-47f5-a8bf-09865ab9bf97 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3206e638-1f43-47b7-8b36-e5a70cf785b2 {http,https} \N \N {/s499-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+7d91e732-5d39-4cf0-840d-1bb9d54fe465 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N 3206e638-1f43-47b7-8b36-e5a70cf785b2 {http,https} \N \N {/s499-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+9564ba87-46a0-47f9-8f9d-037c8619963a 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N d665c6e1-e3a9-4f58-bb0b-29a67711080f {http,https} \N \N {/s500-r1} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+dc7b472b-29a5-48dc-9a97-dd6996a2d219 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N d665c6e1-e3a9-4f58-bb0b-29a67711080f {http,https} \N \N {/s500-r2} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+0c28aff6-defb-4390-9af5-a587cf80cc89 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N d665c6e1-e3a9-4f58-bb0b-29a67711080f {http,https} \N \N {/s500-r3} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+f5230700-c5b2-411f-8bfb-5307e70ef52f 2022-05-26 09:04:37+00 2022-05-26 09:04:37+00 \N d665c6e1-e3a9-4f58-bb0b-29a67711080f {http,https} \N \N {/s500-r4} \N \N \N 0 t f \N 426 \N v0 dde1a96f-1d2f-41dc-bcc3-2c393ec42c65 t t
+\.
+
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/charts/test_data1.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/charts/test_data1.json
new file mode 100644
index 00000000..c8239d9f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/charts/test_data1.json
@@ -0,0 +1,547 @@
+{
+ "options": {
+ "suite_sequential": true,
+ "xaxis_title": "Upstreams count"
+ },
+ "data": [{
+ "latencies_p90": [1030, 1040],
+ "latencies_p99": [1040, 1200, 1600],
+ "latency_avg": 6.66,
+ "rpss": [498.4, 500],
+ "rps": 498.4,
+ "latency_max": 1040,
+ "version": "2.8.1.1",
+ "suite": "7501"
+ }, {
+ "latency_avg": 1.84,
+ "latency_max": 133.76,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [133.89],
+ "latencies_p90": [129.85],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "7501"
+ }, {
+ "latencies_p90": [1240],
+ "latencies_p99": [1250],
+ "latency_avg": 9.22,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latency_max": 1250,
+ "version": "2.8.1.1",
+ "suite": "9501"
+ }, {
+ "latency_avg": 2.07,
+ "latency_max": 178.05,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [178.18],
+ "latencies_p90": [172.41],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "9501"
+ }, {
+ "latencies_p90": [1570],
+ "latencies_p99": [1570],
+ "latency_avg": 15.31,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latency_max": 1570,
+ "version": "2.8.1.1",
+ "suite": "10001"
+ }, {
+ "latency_avg": 2,
+ "latency_max": 169.86,
+ "rpss": [498.41],
+ "rps": 498.41,
+ "latencies_p99": [169.98],
+ "latencies_p90": [159.62],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "10001"
+ }, {
+ "latencies_p90": [5000],
+ "latencies_p99": [5010],
+ "latency_avg": 165.13,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latency_max": 5010,
+ "version": "2.8.1.1",
+ "suite": "7001"
+ }, {
+ "latency_avg": 3.32,
+ "latency_max": 216.83,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [216.96],
+ "latencies_p90": [178.18],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "7001"
+ }, {
+ "latencies_p90": [964.09],
+ "latencies_p99": [969.22],
+ "latency_avg": 7.28,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latency_max": 968.7,
+ "version": "2.8.1.1",
+ "suite": "8001"
+ }, {
+ "latency_avg": 1.91,
+ "latency_max": 102.34,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [102.4],
+ "latencies_p90": [101.5],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "8001"
+ }, {
+ "latencies_p90": [1210],
+ "latencies_p99": [1230],
+ "latency_avg": 11.7,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latency_max": 1230,
+ "version": "2.8.1.1",
+ "suite": "8501"
+ }, {
+ "latency_avg": 2.14,
+ "latency_max": 269.82,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [270.08],
+ "latencies_p90": [266.75],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "8501"
+ }, {
+ "latencies_p90": [2110],
+ "latencies_p99": [2110],
+ "latency_avg": 49.73,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latency_max": 2110,
+ "version": "2.8.1.1",
+ "suite": "9001"
+ }, {
+ "latency_avg": 1.93,
+ "latency_max": 138.75,
+ "rpss": [498.41],
+ "rps": 498.41,
+ "latencies_p99": [138.88],
+ "latencies_p90": [138.49],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "9001"
+ }, {
+ "latency_avg": 5.73,
+ "latency_max": 953.86,
+ "rpss": [498.41],
+ "rps": 498.41,
+ "latencies_p99": [954.37],
+ "latencies_p90": [945.66],
+ "version": "2.8.1.1",
+ "suite": "6501"
+ }, {
+ "latency_avg": 1.75,
+ "latency_max": 104.13,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [104.19],
+ "latencies_p90": [85.12],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "6501"
+ }, {
+ "latency_avg": 74.1,
+ "latency_max": 2620,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [2620],
+ "latencies_p90": [2610],
+ "version": "2.8.1.1",
+ "suite": "12001"
+ }, {
+ "latency_avg": 2.94,
+ "latency_max": 447.23,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [447.49],
+ "latencies_p90": [444.42],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "12001"
+ }, {
+ "latency_avg": 1.98,
+ "latency_max": 349.95,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [350.21],
+ "latencies_p90": [340.99],
+ "version": "2.8.1.1",
+ "suite": "2501"
+ }, {
+ "latency_avg": 1.42,
+ "latency_max": 86.08,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [86.14],
+ "latencies_p90": [66.94],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "2501"
+ }, {
+ "latency_avg": 14.97,
+ "latency_max": 1580,
+ "rpss": [498.41],
+ "rps": 498.41,
+ "latencies_p99": [1580],
+ "latencies_p90": [1570],
+ "version": "2.8.1.1",
+ "suite": "11001"
+ }, {
+ "latency_avg": 2.36,
+ "latency_max": 219.14,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [219.26],
+ "latencies_p90": [215.42],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "11001"
+ }, {
+ "latency_avg": 6.16,
+ "latency_max": 1280,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [1280],
+ "latencies_p90": [1180],
+ "version": "2.8.1.1",
+ "suite": "6001"
+ }, {
+ "latency_avg": 1.84,
+ "latency_max": 199.81,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [199.93],
+ "latencies_p90": [198.53],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "6001"
+ }, {
+ "latency_avg": 14.46,
+ "latency_max": 1930,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [1930],
+ "latencies_p90": [1930],
+ "version": "2.8.1.1",
+ "suite": "13001"
+ }, {
+ "latency_avg": 2.36,
+ "latency_max": 391.17,
+ "rpss": [498.42],
+ "rps": 498.42,
+ "latencies_p99": [391.42],
+ "latencies_p90": [358.4],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "13001"
+ }, {
+ "latency_avg": 3.78,
+ "latency_max": 848.38,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [848.9],
+ "latencies_p90": [843.26],
+ "version": "2.8.1.1",
+ "suite": "5001"
+ }, {
+ "latency_avg": 1.53,
+ "latency_max": 84.54,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [84.61],
+ "latencies_p90": [83.14],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "5001"
+ }, {
+ "latency_avg": 3.86,
+ "latency_max": 774.66,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [775.17],
+ "latencies_p90": [771.07],
+ "version": "2.8.1.1",
+ "suite": "3501"
+ }, {
+ "latency_avg": 1.53,
+ "latency_max": 69.25,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [69.31],
+ "latencies_p90": [67.84],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "3501"
+ }, {
+ "latency_avg": 3.23,
+ "latency_max": 563.2,
+ "rpss": [500.03],
+ "rps": 500.03,
+ "latencies_p99": [563.71],
+ "latencies_p90": [561.66],
+ "version": "2.8.1.1",
+ "suite": "4001"
+ }, {
+ "latency_avg": 1.61,
+ "latency_max": 215.3,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [215.42],
+ "latencies_p90": [203.9],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "4001"
+ }, {
+ "latency_avg": 70.08,
+ "latency_max": 2390,
+ "rpss": [500.06],
+ "rps": 500.06,
+ "latencies_p99": [2390],
+ "latencies_p90": [2390],
+ "version": "2.8.1.1",
+ "suite": "14001"
+ }, {
+ "latency_avg": 84.42,
+ "latency_max": 2270,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [2280],
+ "latencies_p90": [2270],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "14001"
+ }, {
+ "latency_avg": 2.34,
+ "latency_max": 512.77,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [513.02],
+ "latencies_p90": [511.23],
+ "version": "2.8.1.1",
+ "suite": "2001"
+ }, {
+ "latency_avg": 1.41,
+ "latency_max": 34.56,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [34.59],
+ "latencies_p90": [33.82],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "2001"
+ }, {
+ "latency_avg": 1.35,
+ "latency_max": 63.1,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [63.13],
+ "latencies_p90": [60.16],
+ "version": "2.8.1.1",
+ "suite": "1001"
+ }, {
+ "latency_avg": 1.34,
+ "latency_max": 39.14,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [39.17],
+ "latencies_p90": [35.68],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "1001"
+ }, {
+ "latency_avg": 2.08,
+ "latency_max": 434.18,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [434.43],
+ "latencies_p90": [429.57],
+ "version": "2.8.1.1",
+ "suite": "3001"
+ }, {
+ "latency_avg": 1.43,
+ "latency_max": 49.6,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [49.63],
+ "latencies_p90": [40.26],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "3001"
+ }, {
+ "latency_avg": 1.67,
+ "latency_max": 298.24,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [298.49],
+ "latencies_p90": [296.96],
+ "version": "2.8.1.1",
+ "suite": "1501"
+ }, {
+ "latency_avg": 1.32,
+ "latency_max": 28.4,
+ "rpss": [498.41],
+ "rps": 498.41,
+ "latencies_p99": [28.42],
+ "latencies_p90": [27.66],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "1501"
+ }, {
+ "latency_avg": 1.34,
+ "latency_max": 67.07,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [67.14],
+ "latencies_p90": [60.1],
+ "version": "2.8.1.1",
+ "suite": "501"
+ }, {
+ "latency_avg": 1.36,
+ "latency_max": 26.19,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [26.21],
+ "latencies_p90": [21.95],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "501"
+ }, {
+ "latency_avg": 16.9,
+ "latency_max": 2140,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [2150],
+ "latencies_p90": [2140],
+ "version": "2.8.1.1",
+ "suite": "12501"
+ }, {
+ "latency_avg": 2.28,
+ "latency_max": 323.33,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [323.58],
+ "latencies_p90": [320.26],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "12501"
+ }, {
+ "latency_avg": 13.71,
+ "latency_max": 1760,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [1770],
+ "latencies_p90": [1760],
+ "version": "2.8.1.1",
+ "suite": "10501"
+ }, {
+ "latency_avg": 2.3,
+ "latency_max": 198.4,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [198.53],
+ "latencies_p90": [198.14],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "10501"
+ }, {
+ "latency_avg": 9.52,
+ "latency_max": 769.02,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [769.53],
+ "latencies_p90": [763.39],
+ "version": "2.8.1.1",
+ "suite": "4501"
+ }, {
+ "latency_avg": 4.56,
+ "latency_max": 238.59,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [238.72],
+ "latencies_p90": [236.93],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "4501"
+ }, {
+ "latency_avg": 24.86,
+ "latency_max": 2410,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [2420],
+ "latencies_p90": [2420],
+ "version": "2.8.1.1",
+ "suite": "13501"
+ }, {
+ "latency_avg": 5.96,
+ "latency_max": 342.78,
+ "rpss": [498.46],
+ "rps": 498.46,
+ "latencies_p99": [343.04],
+ "latencies_p90": [342.78],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "13501"
+ }, {
+ "latency_avg": 4.1,
+ "latency_max": 1110,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [1110],
+ "latencies_p90": [1100],
+ "version": "2.8.1.1",
+ "suite": "5501"
+ }, {
+ "latency_avg": 1.62,
+ "latency_max": 126.85,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [126.91],
+ "latencies_p90": [126.46],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "5501"
+ }, {
+ "latency_avg": 13.74,
+ "latency_max": 1670,
+ "rpss": [498.42],
+ "rps": 498.42,
+ "latencies_p99": [1670],
+ "latencies_p90": [1600],
+ "version": "2.8.1.1",
+ "suite": "11501"
+ }, {
+ "latency_avg": 2.8,
+ "latency_max": 350.98,
+ "rpss": [498.42],
+ "rps": 498.42,
+ "latencies_p99": [351.23],
+ "latencies_p90": [344.06],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "11501"
+ }, {
+ "latency_avg": 1.33,
+ "latency_max": 3.84,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [3.84],
+ "latencies_p90": [3.65],
+ "version": "2.8.1.1",
+ "suite": "1"
+ }, {
+ "latency_avg": 1.31,
+ "latency_max": 4.77,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [4.77],
+ "latencies_p90": [4.02],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "1"
+ }, {
+ "latency_avg": 28.93,
+ "latency_max": 2530,
+ "rpss": [498.4],
+ "rps": 498.4,
+ "latencies_p99": [2530],
+ "latencies_p90": [2520],
+ "version": "2.8.1.1",
+ "suite": "14501"
+ }, {
+ "latency_avg": 2.63,
+ "latency_max": 352,
+ "rpss": [500.07],
+ "rps": 500.07,
+ "latencies_p99": [352.26],
+ "latencies_p90": [334.33],
+ "version": "2.8.1.2~internal-preview",
+ "suite": "14501"
+ }]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/charts/test_data2.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/charts/test_data2.json
new file mode 100644
index 00000000..b47d1b9d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/charts/test_data2.json
@@ -0,0 +1,60 @@
+{
+ "options": {
+ "suite_sequential": false
+ },
+ "data": [{
+ "rpss": [146063.92, 146845.9, 145638.07],
+ "rps": 146182.63,
+ "latencies_p90": [3.14, 3.27, 3.2],
+ "latencies_p99": [9, 9.18, 9.16],
+ "latency_max": 35.16,
+ "latency_avg": 1.1800240836152,
+ "suite": " #simple #no_plugins #single_route",
+ "version": "git:6098495"
+ }, {
+ "rpss": [144977.6, 143785.14, 145332.58],
+ "rps": 144698.44,
+ "latencies_p90": [3.12, 3.15, 3.28],
+ "latencies_p99": [9.01, 9.04, 9.57],
+ "latency_max": 42.51,
+ "latency_avg": 1.1900081272116,
+ "suite": " #simple #no_plugins #single_route",
+ "version": "git:master"
+ }, {
+ "rpss": [110197.34, 109968.96, 110591.39],
+ "rps": 110252.56333333,
+ "latencies_p90": [2.4, 2.28, 2.22],
+ "latencies_p99": [8.48, 8.14, 7.88],
+ "latency_max": 40.12,
+ "latency_avg": 1.2133197804852,
+ "suite": " #simple #key-auth 10 services each has 10 routes with key-auth, 100 consumers",
+ "version": "git:6098495"
+ }, {
+ "rpss": [109102.58, 109136.34, 108647.47],
+ "rps": 108962.13,
+ "latencies_p90": [2.48, 2.27, 2.14],
+ "latencies_p99": [7.87, 8.15, 7.91],
+ "latency_max": 35.43,
+ "latency_avg": 1.2066902201559,
+ "suite": " #simple #key-auth 10 services each has 10 routes with key-auth, 100 consumers",
+ "version": "git:master"
+ }, {
+ "rpss": [133682.24, 133910.3, 134863],
+ "rps": 134151.84666667,
+ "latencies_p90": [2.72, 2.78, 2.72],
+ "latencies_p99": [8.65, 8.87, 8.49],
+ "latency_max": 35.7,
+ "latency_avg": 1.1699763325704,
+ "suite": " #simple #no_plugins 10 services each has 10 routes",
+ "version": "git:6098495"
+ }, {
+ "rpss": [133410.76, 134137.81, 134780.55],
+ "rps": 134109.70666667,
+ "latencies_p90": [2.83, 2.73, 2.88],
+ "latencies_p99": [9.09, 8.87, 8.98],
+ "latency_max": 43.7,
+ "latency_avg": 1.1932978944078,
+ "suite": " #simple #no_plugins 10 services each has 10 routes",
+ "version": "git:master"
+ }]
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/.gitignore b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/.gitignore
new file mode 100644
index 00000000..cbd4daf5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/.gitignore
@@ -0,0 +1,8 @@
+.terraform*
+!.terraform-version
+terraform.tfstate*
+*.deb
+output
+id_rsa
+license.json
+cm-*
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/ec2.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/ec2.tf
new file mode 100644
index 00000000..8d7a96f1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/ec2.tf
@@ -0,0 +1,111 @@
+resource "aws_key_pair" "perf" {
+ key_name = "key-perf-test-${random_string.ident.result}"
+ public_key = tls_private_key.key.public_key_openssh
+}
+
+data "aws_ami" "perf" {
+ most_recent = true
+
+ filter {
+ name = "name"
+ values = [var.ec2_os]
+ }
+
+ filter {
+ name = "virtualization-type"
+ values = ["hvm"]
+ }
+
+ owners = ["099720109477"] # Canonical
+}
+
+resource "aws_security_group" "openall" {
+ ingress {
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ ipv6_cidr_blocks = ["::/0"]
+ }
+
+ egress {
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ ipv6_cidr_blocks = ["::/0"]
+ }
+}
+
+resource "aws_instance" "kong" {
+ ami = data.aws_ami.perf.id
+ instance_type = var.ec2_instance_type
+ key_name = aws_key_pair.perf.key_name
+ monitoring = true
+ security_groups = [aws_security_group.openall.name]
+ associate_public_ip_address = true
+
+ root_block_device {
+ tags = {
+ PerfTest = "perf-${random_string.ident.result}"
+ Name = "kong-${random_string.ident.result}"
+ }
+ volume_size = 100
+ }
+
+ tags = {
+ PerfTest = "perf-${random_string.ident.result}"
+ Name = "kong-${random_string.ident.result}"
+ }
+}
+
+resource "aws_instance" "db" {
+ count = var.seperate_db_node ? 1: 0
+ ami = data.aws_ami.perf.id
+ instance_type = var.ec2_instance_db_type
+ key_name = aws_key_pair.perf.key_name
+ monitoring = true
+ security_groups = [aws_security_group.openall.name]
+ associate_public_ip_address = true
+
+ root_block_device {
+ tags = {
+ PerfTest = "perf-${random_string.ident.result}"
+ Name = "kong-${random_string.ident.result}"
+ }
+ volume_size = 100
+ }
+
+ tags = {
+ PerfTest = "perf-${random_string.ident.result}"
+ Name = "db-${random_string.ident.result}"
+ }
+}
+
+resource "aws_instance" "worker" {
+ ami = data.aws_ami.perf.id
+ instance_type = var.ec2_instance_worker_type
+ key_name = aws_key_pair.perf.key_name
+ monitoring = true
+ security_groups = [aws_security_group.openall.name]
+ associate_public_ip_address = true
+
+ root_block_device {
+ tags = {
+ PerfTest = "perf-${random_string.ident.result}"
+ Name = "kong-${random_string.ident.result}"
+ }
+ volume_size = 100
+ }
+
+ tags = {
+ PerfTest = "perf-${random_string.ident.result}"
+ Name = "worker-${random_string.ident.result}"
+ }
+}
+
+
+resource "random_string" "ident" {
+ length = 4
+ special = false
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/main.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/main.tf
new file mode 100644
index 00000000..a6fd2d5e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/main.tf
@@ -0,0 +1,26 @@
+terraform {
+ required_version = "~> 1.2"
+
+ required_providers {
+ local = {
+ version = "~> 2.2"
+ }
+ null = {
+ version = "~> 3.1"
+ }
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 3.0"
+ }
+ tls = {
+ version = "~> 3.4"
+ }
+ random = {
+ version = "~> 3.3"
+ }
+ }
+}
+
+provider "aws" {
+ region = var.aws_region
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/output.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/output.tf
new file mode 100644
index 00000000..79156979
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/output.tf
@@ -0,0 +1,23 @@
+output "kong-ip" {
+ value = aws_instance.kong.public_ip
+}
+
+output "kong-internal-ip" {
+ value = aws_instance.kong.private_ip
+}
+
+output "db-ip" {
+ value = var.seperate_db_node ? aws_instance.db.0.public_ip: ""
+}
+
+output "db-internal-ip" {
+ value = var.seperate_db_node ? aws_instance.db.0.private_ip: ""
+}
+
+output "worker-ip" {
+ value = aws_instance.worker.public_ip
+}
+
+output "worker-internal-ip" {
+ value = aws_instance.worker.private_ip
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/ssh.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/ssh.tf
new file mode 100644
index 00000000..30629007
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/ssh.tf
@@ -0,0 +1,10 @@
+resource "tls_private_key" "key" {
+ algorithm = "RSA"
+}
+
+
+resource "local_sensitive_file" "key_priv" {
+ content = tls_private_key.key.private_key_pem
+ filename = "./id_rsa"
+ file_permission = "0600"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/variables.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/variables.tf
new file mode 100644
index 00000000..f357e884
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/aws-ec2/variables.tf
@@ -0,0 +1,36 @@
+variable "aws_region" {
+ type = string
+ description = "The EC2 region in which to create the EC2 instances"
+ default = "us-east-2"
+}
+
+variable "ec2_instance_type" {
+ type = string
+ description = "The EC2 size on which to run the kong"
+ default = "c5a.2xlarge"
+}
+
+variable "ec2_instance_worker_type" {
+ type = string
+ description = "The EC2 size on which to run the worker"
+ default = "c5a.large"
+}
+
+variable "ec2_instance_db_type" {
+ type = string
+ description = "The EC2 size on which to run the db"
+ default = "c5a.large"
+}
+
+variable "ec2_os" {
+ type = string
+ description = "The OS to install on the EC2"
+ default = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
+}
+
+variable "seperate_db_node" {
+ type = bool
+ description = "Whether to create a separate db instance"
+ default = false
+}
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/main.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/main.tf
new file mode 100644
index 00000000..2d446342
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/main.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = "~> 1.2"
+
+ required_providers {
+ local = {
+ version = "~> 2.2"
+ }
+ }
+}
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/output.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/output.tf
new file mode 100644
index 00000000..a01f3ea9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/output.tf
@@ -0,0 +1,23 @@
+output "kong-ip" {
+ value = var.kong_ip
+}
+
+output "kong-internal-ip" {
+ value = local.kong_internal_ip_fallback
+}
+
+output "db-ip" {
+ value = var.db_ip
+}
+
+output "db-internal-ip" {
+ value = var.db_internal_ip
+}
+
+output "worker-ip" {
+ value = var.worker_ip
+}
+
+output "worker-internal-ip" {
+ value = local.worker_internal_ip_fallback
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/ssh.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/ssh.tf
new file mode 100644
index 00000000..1659b857
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/ssh.tf
@@ -0,0 +1,6 @@
+# copy the file to current directory to be loaded by framework
+resource "local_sensitive_file" "key_priv" {
+ source = var.ssh_key_path
+ filename = "./id_rsa"
+ file_permission = "0600"
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/variables.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/variables.tf
new file mode 100644
index 00000000..f34c1fab
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/bring-your-own/variables.tf
@@ -0,0 +1,43 @@
+variable "kong_ip" {
+ type = string
+}
+
+variable "kong_internal_ip" {
+ type = string
+ default = ""
+}
+
+variable "worker_ip" {
+ type = string
+}
+
+variable "worker_internal_ip" {
+ type = string
+ default = ""
+}
+
+locals {
+ kong_internal_ip_fallback = var.kong_internal_ip != "" ? var.kong_internal_ip : var.kong_ip
+ worker_internal_ip_fallback = var.worker_internal_ip != "" ? var.worker_internal_ip : var.worker_ip
+}
+
+# db IP fallback is done in the lua part
+variable "db_ip" {
+ type = string
+ default = ""
+}
+
+variable "db_internal_ip" {
+ type = string
+ default = ""
+}
+
+variable "ssh_key_path" {
+ type = string
+}
+
+variable "seperate_db_node" {
+ type = bool
+ description = "Whether to create a separate db instance"
+ default = false
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/.gitignore b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/.gitignore
new file mode 100644
index 00000000..cbd4daf5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/.gitignore
@@ -0,0 +1,8 @@
+.terraform*
+!.terraform-version
+terraform.tfstate*
+*.deb
+output
+id_rsa
+license.json
+cm-*
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/droplets.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/droplets.tf
new file mode 100644
index 00000000..bfdd2e01
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/droplets.tf
@@ -0,0 +1,34 @@
+resource "digitalocean_ssh_key" "key" {
+ name = "key1"
+ public_key = tls_private_key.key.public_key_openssh
+}
+
+resource "digitalocean_droplet" "kong" {
+ name = "kong-${random_string.ident.result}"
+ size = var.do_size
+ region = var.do_region
+ image = var.do_os
+ ssh_keys = [digitalocean_ssh_key.key.fingerprint]
+}
+
+resource "digitalocean_droplet" "db" {
+ count = var.seperate_db_node ? 1: 0
+ name = "db-${random_string.ident.result}"
+ size = var.do_db_size
+ region = var.do_region
+ image = var.do_os
+ ssh_keys = [digitalocean_ssh_key.key.fingerprint]
+}
+
+resource "digitalocean_droplet" "worker" {
+ name = "worker-${random_string.ident.result}"
+ size = var.do_worker_size
+ region = var.do_region
+ image = var.do_os
+ ssh_keys = [digitalocean_ssh_key.key.fingerprint]
+}
+
+resource "random_string" "ident" {
+ length = 4
+ special = false
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/main.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/main.tf
new file mode 100644
index 00000000..e6f29bbd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/main.tf
@@ -0,0 +1,26 @@
+terraform {
+ required_version = "~> 1.2"
+
+ required_providers {
+ local = {
+ version = "~> 2.2"
+ }
+ null = {
+ version = "~> 3.1"
+ }
+ digitalocean = {
+ source = "digitalocean/digitalocean"
+ version = "~> 2.0"
+ }
+ tls = {
+ version = "~> 3.4"
+ }
+ random = {
+ version = "~> 3.3"
+ }
+ }
+}
+
+provider "digitalocean" {
+ token = var.do_token
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/output.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/output.tf
new file mode 100644
index 00000000..f5c06c89
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/output.tf
@@ -0,0 +1,23 @@
+output "kong-ip" {
+ value = digitalocean_droplet.kong.ipv4_address
+}
+
+output "kong-internal-ip" {
+ value = digitalocean_droplet.kong.ipv4_address_private
+}
+
+output "db-ip" {
+ value = var.seperate_db_node ? digitalocean_droplet.db.0.ipv4_address: ""
+}
+
+output "db-internal-ip" {
+ value = var.seperate_db_node ? digitalocean_droplet.db.0.ipv4_address_private: ""
+}
+
+output "worker-ip" {
+ value = digitalocean_droplet.worker.ipv4_address
+}
+
+output "worker-internal-ip" {
+ value = digitalocean_droplet.worker.ipv4_address_private
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/project.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/project.tf
new file mode 100644
index 00000000..eadd28e1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/project.tf
@@ -0,0 +1,12 @@
+data "digitalocean_project" "benchmark" {
+ name = var.do_project_name
+}
+
+resource "digitalocean_project_resources" "benchmark" {
+ project = data.digitalocean_project.benchmark.id
+ resources = [
+ digitalocean_droplet.kong.urn,
+ digitalocean_droplet.db.urn,
+ digitalocean_droplet.worker.urn
+ ]
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/ssh.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/ssh.tf
new file mode 100644
index 00000000..30629007
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/ssh.tf
@@ -0,0 +1,10 @@
+resource "tls_private_key" "key" {
+ algorithm = "RSA"
+}
+
+
+resource "local_sensitive_file" "key_priv" {
+ content = tls_private_key.key.private_key_pem
+ filename = "./id_rsa"
+ file_permission = "0600"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/variables.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/variables.tf
new file mode 100644
index 00000000..9bdaee70
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/digitalocean/variables.tf
@@ -0,0 +1,49 @@
+variable "do_token" {
+ type = string
+ description = "The digitalocean auth token"
+}
+
+variable "do_project_name" {
+ type = string
+ description = "The digitalocean project ID under which to create the droplets"
+ default = "Benchmark"
+}
+
+variable "do_size" {
+ type = string
+ description = "The droplet size on which to create the kong droplet"
+ default = "c2-8vpcu-16gb"
+}
+
+variable "do_worker_size" {
+ type = string
+ description = "The droplet size on which to create the worker droplet"
+ default = "s-1vcpu-1gb"
+}
+
+variable "do_db_size" {
+ type = string
+ description = "The droplet size on which to create the db droplet"
+ default = "s-1vcpu-1gb"
+}
+
+
+variable "do_region" {
+ type = string
+ description = "The digitalocean region in which to create the droplets"
+ default = "sfo3"
+}
+
+variable "do_os" {
+ type = string
+ description = "The OS to install on the Metal droplets"
+ default = "ubuntu-20-04-x64"
+}
+
+variable "seperate_db_node" {
+ type = bool
+ description = "Whether to create a separate db instance"
+ default = false
+}
+
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/.gitignore b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/.gitignore
new file mode 100644
index 00000000..cbd4daf5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/.gitignore
@@ -0,0 +1,8 @@
+.terraform*
+!.terraform-version
+terraform.tfstate*
+*.deb
+output
+id_rsa
+license.json
+cm-*
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/README.md b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/README.md
new file mode 100644
index 00000000..33266d29
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/README.md
@@ -0,0 +1,5 @@
+Perf test terraform driver expects:
+- `id_rsa` as the private key present
+- `kong-ip`, `kong-internal-ip`, `worker-ip` and `worker-internal-ip`
+to present in terraform output. If instance has no private IP,
+use `-ip` as `-internal-ip` is also accepted.
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/main.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/main.tf
new file mode 100644
index 00000000..57e7eeb7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/main.tf
@@ -0,0 +1,26 @@
+terraform {
+ required_version = "~> 1.2"
+
+ required_providers {
+ local = {
+ version = "~> 2.2"
+ }
+ null = {
+ version = "~> 3.1"
+ }
+ equinix = {
+ source = "equinix/equinix"
+ version = "~> 1.6"
+ }
+ tls = {
+ version = "~> 3.4"
+ }
+ random = {
+ version = "~> 3.3"
+ }
+ }
+}
+
+provider "equinix" {
+ auth_token = var.metal_auth_token
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/metal.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/metal.tf
new file mode 100644
index 00000000..af8a39f9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/metal.tf
@@ -0,0 +1,49 @@
+resource "equinix_metal_ssh_key" "key" {
+ name = "key1"
+ public_key = tls_private_key.key.public_key_openssh
+}
+
+resource "equinix_metal_device" "kong" {
+ hostname = "kong-${random_string.ident.result}"
+ plan = var.metal_plan
+ facilities = var.metal_region
+ operating_system = var.metal_os
+ billing_cycle = "hourly"
+ project_id = var.metal_project_id
+ tags = []
+ depends_on = [
+ equinix_metal_ssh_key.key,
+ ]
+}
+
+resource "equinix_metal_device" "db" {
+ count = var.seperate_db_node ? 1: 0
+ hostname = "db-${random_string.ident.result}"
+ plan = var.metal_db_plan
+ facilities = var.metal_region
+ operating_system = var.metal_os
+ billing_cycle = "hourly"
+ project_id = var.metal_project_id
+ tags = []
+ depends_on = [
+ equinix_metal_ssh_key.key,
+ ]
+}
+
+resource "equinix_metal_device" "worker" {
+ hostname = "worker-${random_string.ident.result}"
+ plan = var.metal_worker_plan
+ facilities = var.metal_region
+ operating_system = var.metal_os
+ billing_cycle = "hourly"
+ project_id = var.metal_project_id
+ tags = []
+ depends_on = [
+ equinix_metal_ssh_key.key,
+ ]
+}
+
+resource "random_string" "ident" {
+ length = 4
+ special = false
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/output.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/output.tf
new file mode 100644
index 00000000..18a40e86
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/output.tf
@@ -0,0 +1,23 @@
+output "kong-ip" {
+ value = equinix_metal_device.kong.access_public_ipv4
+}
+
+output "kong-internal-ip" {
+ value = equinix_metal_device.kong.access_private_ipv4
+}
+
+output "db-ip" {
+ value = var.seperate_db_node ? equinix_metal_device.db.0.access_public_ipv4: ""
+}
+
+output "db-internal-ip" {
+ value = var.seperate_db_node ? equinix_metal_device.db.0.access_private_ipv4: ""
+}
+
+output "worker-ip" {
+ value = equinix_metal_device.worker.access_public_ipv4
+}
+
+output "worker-internal-ip" {
+ value = equinix_metal_device.worker.access_private_ipv4
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/scripts/wrk.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/scripts/wrk.lua
new file mode 100644
index 00000000..d8cd38b6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/scripts/wrk.lua
@@ -0,0 +1,54 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- luacheck: ignore
+--This script is executed in conjuction with the wrk benchmarking tool via demo.sh
+math.randomseed(os.time()) -- Generate PRNG seed
+local rand = math.random -- Cache random method
+
+-- Get env vars for consumer and api count or assign defaults
+local consumer_count = os.getenv("KONG_DEMO_CONSUMER_COUNT") or 5
+local service_count = os.getenv("KONG_DEMO_SERVICE_COUNT") or 5
+local workspace_count = os.getenv("KONG_DEMO_WORKSPACE_COUNT") or 1
+local route_per_service = os.getenv("KONG_DEMO_ROUTE_PER_SERVICE") or 5
+
+function request()
+ -- generate random URLs, some of which may yield non-200 response codes
+ local random_consumer = rand(consumer_count)
+ local random_service = rand(service_count)
+ local random_route = rand(route_per_service)
+ -- Concat the url parts
+ if workspace_count == 1 then
+ url_path = string.format("/s%s-r%s?apikey=consumer-%s", random_service, random_route, random_consumer)
+ else
+ random_workspace = rand(workspace_count)
+ url_path = string.format("/w%s-s%s-r%s?apikey=consumer-%s", random_workspace, random_service, random_route, random_consumer)
+ end
+ -- Return the request object with the current URL path
+ return wrk.format(nil, url_path, headers)
+end
+
+--[[function done(summary, latency, requests)
+ local file = io.open("output.csv", "a")
+ file:write(string.format(
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ os.time(),
+ latency.min,
+ latency.max,
+ latency.mean,
+ latency:percentile(50),
+ latency:percentile(90),
+ latency:percentile(99),
+ summary.duration,
+ summary.requests,
+ summary.errors.connect,
+ summary.errors.read,
+ summary.errors.write,
+ summary.errors.status,
+ summary.errors.timeout
+ ))
+end]]
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/ssh.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/ssh.tf
new file mode 100644
index 00000000..9ca74532
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/ssh.tf
@@ -0,0 +1,5 @@
+resource "local_sensitive_file" "key_priv" {
+ content = tls_private_key.key.private_key_pem
+ filename = "./id_rsa"
+ file_permission = "0600"
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/tls.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/tls.tf
new file mode 100644
index 00000000..1ee3f01a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/tls.tf
@@ -0,0 +1,4 @@
+resource "tls_private_key" "key" {
+ algorithm = "RSA"
+}
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/variables.tf b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/variables.tf
new file mode 100644
index 00000000..69792e03
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/perf/terraform/equinix-metal/variables.tf
@@ -0,0 +1,48 @@
+variable "metal_auth_token" {
+ type = string
+ description = "The pre-existing Metal auth token"
+}
+
+variable "metal_project_id" {
+ type = string
+ description = "The pre-existing Metal project ID under which to create the devices"
+}
+
+variable "metal_plan" {
+ type = string
+ description = "The Metal device plan on which to create the kong devices"
+ default = "c3.small.x86"
+}
+
+variable "metal_worker_plan" {
+ type = string
+ description = "The Metal device plan on which to create the worker devices"
+ default = "c3.small.x86"
+}
+
+variable "metal_db_plan" {
+ type = string
+ description = "The Metal device plan on which to create the db devices"
+ default = "c3.small.x86"
+}
+
+variable "metal_region" {
+ type = list(string)
+ description = "The Metal region in which to create the devices"
+ # All AMER facilities
+ default = ["dc13", "da11", "sv15", "sv16", "sp4", "ch3", "ny5", "ny7", "la4", "tr2", "se4"]
+}
+
+variable "metal_os" {
+ type = string
+ description = "The OS to install on the Metal devices"
+ default = "ubuntu_20_04"
+}
+
+variable "seperate_db_node" {
+ type = bool
+ description = "Whether to create a separate db instance"
+ default = false
+}
+
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/prometheus/metrics.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/prometheus/metrics.conf
new file mode 100644
index 00000000..8436a97a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/prometheus/metrics.conf
@@ -0,0 +1,13 @@
+server {
+ server_name kong_prometheus_exporter;
+ listen 0.0.0.0:9542;
+
+ location / {
+ default_type text/plain;
+ content_by_lua_block {
+ local serve = require "kong.plugins.prometheus.serve"
+ serve.prometheus_server()
+ }
+ }
+
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/Cargo.lock b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/Cargo.lock
new file mode 100644
index 00000000..5c0d98b6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/Cargo.lock
@@ -0,0 +1,753 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-integer",
+ "num-traits",
+ "time",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "cxx"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "enum-utils"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed327f716d0d351d86c9fd3398d20ee39ad8f681873cc081da2ca1c10fed398a"
+dependencies = [
+ "enum-utils-from-str",
+ "failure",
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn",
+]
+
+[[package]]
+name = "enum-utils-from-str"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d49be08bad6e4ca87b2b8e74146987d4e5cb3b7512efa50ef505b51a22227ee1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "failure"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "gimli"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
+[[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "num"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
+dependencies = [
+ "autocfg",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "object"
+version = "0.30.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "parse_duration"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7037e5e93e0172a5a96874380bf73bc6ecef022e26fa25f2be26864d6b3ba95d"
+dependencies = [
+ "lazy_static",
+ "num",
+ "regex",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proxy-wasm"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "823b744520cd4a54ba7ebacbffe4562e839d6dcd8f89209f96a1ace4f5229cd4"
+dependencies = [
+ "hashbrown",
+ "log",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "response_transformer"
+version = "0.0.1"
+dependencies = [
+ "log",
+ "proxy-wasm",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "scratch"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
+
+[[package]]
+name = "serde"
+version = "1.0.156"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.156"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "tests"
+version = "0.0.1"
+dependencies = [
+ "chrono",
+ "enum-utils",
+ "http",
+ "log",
+ "parse_duration",
+ "proxy-wasm",
+ "url",
+]
+
+[[package]]
+name = "time"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
+dependencies = [
+ "libc",
+ "wasi",
+ "winapi",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d502c968c6a838ead8e69b2ee18ec708802f99db92a0d156705ec9ef801993b"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "url"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/Cargo.toml b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/Cargo.toml
new file mode 100644
index 00000000..d8e358e7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/Cargo.toml
@@ -0,0 +1,5 @@
+[workspace]
+members = [
+ "tests",
+ "response_transformer",
+]
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/Cargo.toml b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/Cargo.toml
new file mode 100644
index 00000000..65b3e049
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "response_transformer"
+version = "0.0.1"
+authors = ["Michael Martin "]
+edition = "2018"
+
+[lib]
+path = "src/filter.rs"
+crate-type = ["cdylib"]
+
+[dependencies]
+proxy-wasm = "0.2"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+log = "0.4"
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/src/filter.rs b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/src/filter.rs
new file mode 100644
index 00000000..fb23189b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/src/filter.rs
@@ -0,0 +1,92 @@
+mod types;
+
+use proxy_wasm::traits::{Context, RootContext, HttpContext};
+use proxy_wasm::types::{Action, LogLevel, ContextType};
+use crate::types::*;
+use serde_json;
+use log::*;
+
+proxy_wasm::main! {{
+ proxy_wasm::set_log_level(LogLevel::Info);
+ proxy_wasm::set_root_context(|_| -> Box {
+ Box::new(ResponseTransformerContext { config: Config::default() } )
+ });
+}}
+
+
+struct ResponseTransformerContext {
+ config: Config,
+}
+
+impl ResponseTransformerContext {
+}
+
+impl RootContext for ResponseTransformerContext {
+ fn on_configure(&mut self, _: usize) -> bool {
+ let bytes = self.get_plugin_configuration().unwrap();
+ match serde_json::from_slice::(bytes.as_slice()) {
+ Ok(config) => {
+ self.config = config;
+ true
+ },
+ Err(e) => {
+ error!("failed parsing filter config: {}", e);
+ false
+ }
+ }
+ }
+
+ fn create_http_context(&self, _: u32) -> Option> {
+ Some(Box::new(ResponseTransformerContext{
+ config: self.config.clone(),
+ }))
+ }
+
+ fn get_type(&self) -> Option {
+ Some(ContextType::HttpContext)
+ }
+}
+
+impl Context for ResponseTransformerContext {
+ fn on_done(&mut self) -> bool {
+ true
+ }
+}
+
+impl HttpContext for ResponseTransformerContext {
+ fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
+ self.config.remove.headers.iter().for_each(|name| {
+ info!("[response-transformer] removing header: {}", name);
+ self.set_http_response_header(&name, None);
+ });
+
+ self.config.rename.headers.iter().for_each(|KeyValuePair(from, to)| {
+ info!("[response-transformer] renaming header {} => {}", from, to);
+ let value = self.get_http_response_header(&from);
+ self.set_http_response_header(&from, None);
+ self.set_http_response_header(&to, value.as_deref());
+ });
+
+ self.config.replace.headers.iter().for_each(|KeyValuePair(name, value)| {
+ if self.get_http_response_header(&name).is_some() {
+ info!("[response-transformer] updating header {} value to {}", name, value);
+ self.set_http_response_header(&name, Some(&value));
+ }
+ });
+
+ self.config.add.headers.iter().for_each(|KeyValuePair(name, value)| {
+ if self.get_http_response_header(&name).is_none() {
+ info!("[response-transformer] adding header {} => {}", name, value);
+ self.set_http_response_header(&name, Some(&value));
+ }
+ });
+
+ self.config.append.headers.iter().for_each(|KeyValuePair(name, value)| {
+ info!("[response-transformer] appending header {} => {}", name, value);
+ self.add_http_response_header(&name, &value);
+ });
+
+
+ Action::Continue
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/src/types.rs b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/src/types.rs
new file mode 100644
index 00000000..1e9d4342
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/response_transformer/src/types.rs
@@ -0,0 +1,99 @@
+use std::convert::TryFrom;
+use std::fmt;
+
+use serde::Deserialize;
+
+#[derive(Debug, Clone, PartialEq)]
+pub(crate) struct InvalidHeader(String);
+
+impl fmt::Display for InvalidHeader {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Invalid : => {}", self.0)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
+#[serde(try_from = "String")]
+pub(crate) struct KeyValuePair(pub(crate) String, pub(crate) String);
+
+impl TryFrom for KeyValuePair {
+ type Error = InvalidHeader;
+
+ fn try_from(input: String) -> std::result::Result {
+ input
+ .split_once(':')
+ .filter(|(name, value)| {
+ name.len() > 0 && value.len() > 0
+ })
+ .ok_or_else(|| InvalidHeader(input.clone()))
+ .and_then(|(name, value)| {
+ Ok(KeyValuePair(name.to_string(), value.to_string()))
+ })
+ }
+}
+
+impl TryFrom<&str> for KeyValuePair {
+ type Error = InvalidHeader;
+
+ fn try_from(value: &str) -> std::result::Result {
+ KeyValuePair::try_from(value.to_string())
+ }
+}
+
+#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
+pub(crate) struct Transformations {
+ pub(crate) headers: Vec,
+}
+
+impl Default for Transformations {
+ fn default() -> Self {
+ Transformations { headers: vec![] }
+ }
+}
+
+#[derive(Deserialize, Default, PartialEq, Eq, Debug, Clone)]
+#[serde(default)]
+pub(crate) struct Config {
+ pub(crate) remove: Transformations,
+ pub(crate) rename: Transformations,
+ pub(crate) replace: Transformations,
+ pub(crate) add: Transformations,
+ pub(crate) append: Transformations,
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use serde_json;
+
+ impl KeyValuePair {
+ #[warn(unused)]
+ pub(crate) fn new(name: T, value: T) -> Self {
+ KeyValuePair(name.to_string(), value.to_string())
+ }
+ }
+
+
+ #[test]
+ fn test_header_try_from_valid() {
+ assert_eq!(Ok(KeyValuePair::new("a", "b")), KeyValuePair::try_from("a:b"));
+ }
+
+ #[test]
+ fn test_header_try_from_invalid() {
+ assert_eq!(Err(InvalidHeader("a".to_string())), KeyValuePair::try_from("a"));
+ assert_eq!(Err(InvalidHeader("a:".to_string())), KeyValuePair::try_from("a:"));
+ assert_eq!(Err(InvalidHeader(":b".to_string())), KeyValuePair::try_from(":b"));
+ }
+
+ #[test]
+ fn test_json_deserialize_transformations() {
+ assert_eq!(
+ Transformations {
+ headers: vec![KeyValuePair::new("a", "b"), KeyValuePair::new("c", "d")]
+ },
+ serde_json::from_str(r#"{ "headers": ["a:b", "c:d"] }"#).unwrap()
+ );
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/Cargo.toml b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/Cargo.toml
new file mode 100644
index 00000000..07a64787
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "tests"
+version = "0.0.1"
+authors = ["Thibault Charbonnier "]
+edition = "2018"
+
+[lib]
+path = "src/filter.rs"
+crate-type = ["cdylib"]
+
+[dependencies]
+proxy-wasm = "0.2"
+url = "2.2"
+log = "0.4"
+http = "0.2"
+chrono = "0.4"
+enum-utils = "0.1.2"
+parse_duration = "2.1.1"
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/filter.rs b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/filter.rs
new file mode 100644
index 00000000..9251987e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/filter.rs
@@ -0,0 +1,150 @@
+mod routines;
+mod test_http;
+mod types;
+
+use crate::routines::*;
+use crate::test_http::*;
+use crate::types::*;
+use http::StatusCode;
+use log::*;
+use proxy_wasm::traits::*;
+use proxy_wasm::types::*;
+use std::str::FromStr;
+use std::time::Duration;
+
+proxy_wasm::main! {{
+ proxy_wasm::set_log_level(LogLevel::Info);
+ proxy_wasm::set_root_context(|_| -> Box {
+ Box::new(TestRoot { config: None })
+ });
+}}
+
+struct TestRoot {
+ config: Option,
+}
+
+impl Context for TestRoot {}
+
+impl RootContext for TestRoot {
+ fn on_vm_start(&mut self, conf_size: usize) -> bool {
+ info!("[proxy-wasm root] on_vm_start (conf_size: {})", conf_size);
+ true
+ }
+
+ fn on_configure(&mut self, conf_size: usize) -> bool {
+ info!("[proxy-wasm root] on_configure (conf_size: {})", conf_size);
+
+ if let Some(bytes) = self.get_plugin_configuration() {
+ let config: &str = std::str::from_utf8(&bytes).unwrap();
+ self.config = TestConfig::from_str(config).ok();
+
+ if let Some(every) = self.config.as_ref().unwrap().map.get("tick_every") {
+ let ms = every.parse().expect("bad tick_every");
+ info!("starting on_tick every {}ms", ms);
+
+ self.set_tick_period(Duration::from_millis(ms));
+ }
+ }
+
+ true
+ }
+
+ fn get_type(&self) -> Option {
+ Some(ContextType::HttpContext)
+ }
+
+ fn create_http_context(&self, context_id: u32) -> Option> {
+ info!(
+ "[proxy-wasm root] create_http_context (id: #{})",
+ context_id
+ );
+
+ let config = if let Some(config) = &self.config {
+ Some(TestConfig{ map: config.map.clone()})
+ } else {
+ None
+ };
+
+ Some(Box::new(TestHttp { config: config }))
+ }
+
+ fn on_tick(&mut self) {
+ info!("[proxy-wasm root] on_tick");
+ }
+}
+
+impl Context for TestHttp {
+ fn on_http_call_response(
+ &mut self,
+ token_id: u32,
+ nheaders: usize,
+ body_size: usize,
+ _ntrailers: usize,
+ ) {
+ const HEADER_NAME: &str = "X-PW-Dispatch-Echo";
+
+ info!(
+ "[proxy-wasm http] on_http_call_response (token_id: {}, headers: {}, body_bytes: {})",
+ token_id, nheaders, body_size
+ );
+
+ if let Some(bytes) = self.get_http_call_response_body(0, usize::MAX) {
+ let body = String::from_utf8(bytes).unwrap();
+ info!("[proxy-wasm] http_call_response body: {:?}", body);
+
+ if let Some(v) = self.get_http_request_header(HEADER_NAME) {
+ match v.as_str() {
+ "on" | "true" | "T" | "1" => {
+ self.send_plain_response(StatusCode::OK, Some(body.trim()))
+ }
+ _ => {}
+ }
+ }
+ }
+
+ self.resume_http_request()
+ }
+}
+
+impl HttpContext for TestHttp {
+ fn on_http_request_headers(&mut self, nheaders: usize, eof: bool) -> Action {
+ info!(
+ "[proxy-wasm http] on_request_headers ({} headers, eof: {})",
+ nheaders, eof
+ );
+
+ self.run_tests(TestPhase::RequestHeaders)
+ }
+
+ fn on_http_request_body(&mut self, size: usize, eof: bool) -> Action {
+ info!(
+ "[proxy-wasm http] on_request_body ({} bytes, eof: {})",
+ size, eof
+ );
+
+ self.run_tests(TestPhase::RequestBody)
+ }
+
+ fn on_http_response_headers(&mut self, nheaders: usize, eof: bool) -> Action {
+ info!(
+ "[proxy-wasm http] on_response_headers ({} headers, eof: {})",
+ nheaders, eof
+ );
+
+ self.run_tests(TestPhase::ResponseHeaders)
+ }
+
+ fn on_http_response_body(&mut self, size: usize, eof: bool) -> Action {
+ info!(
+ "[proxy-wasm http] on_response_body ({} bytes, eof {})",
+ size, eof
+ );
+
+ self.run_tests(TestPhase::ResponseBody)
+ }
+
+ fn on_log(&mut self) {
+ info!("[proxy-wasm http] on_log");
+ self.run_tests(TestPhase::Log);
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/routines.rs b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/routines.rs
new file mode 100644
index 00000000..dc8fff04
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/routines.rs
@@ -0,0 +1,30 @@
+use crate::*;
+
+pub(crate) fn add_request_header(ctx: &mut TestHttp) {
+ const HEADER_NAME: &str = "X-PW-Add-Header";
+
+ if let Some(header) = ctx.get_http_request_header(HEADER_NAME) {
+ let (name, value) = header.split_once('=').unwrap();
+
+ ctx.add_http_request_header(name, value);
+ ctx.set_http_request_header(HEADER_NAME, None)
+ }
+}
+
+pub(crate) fn add_response_header(ctx: &mut TestHttp) {
+ const HEADER_NAME: &str = "X-PW-Add-Resp-Header";
+
+ if let Some(header) = ctx.get_http_request_header(HEADER_NAME) {
+ let (name, value) = header.split_once('=').unwrap();
+
+ ctx.add_http_response_header(name, value);
+ }
+
+ const CONFIG_HEADER_NAME: &str = "X-PW-Resp-Header-From-Config";
+ if let Some(config) = &ctx.config {
+ info!("[proxy-wasm] setting {:?} header from config", CONFIG_HEADER_NAME);
+ if let Some(value) = config.map.get("add_resp_header") {
+ ctx.add_http_response_header(CONFIG_HEADER_NAME, value);
+ }
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/test_cases.rs b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/test_cases.rs
new file mode 100644
index 00000000..4e548ea0
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/test_cases.rs
@@ -0,0 +1,23 @@
+use crate::*;
+
+pub(crate) fn add_request_header(ctx: &mut TestHttpHostcalls) {
+ const HEADER_NAME: &str = "X-PW-Add-Header";
+
+ if let Some(header) = ctx.get_http_request_header(HEADER_NAME) {
+ let (name, value) = header.split_once('=').unwrap();
+
+ ctx.add_http_request_header(name, value);
+ ctx.set_http_request_header(HEADER_NAME, None);
+ }
+}
+
+pub(crate) fn add_response_header(ctx: &mut TestHttpHostcalls) {
+ const HEADER_NAME: &str = "X-PW-Add-Resp-Header";
+
+ if let Some(header) = ctx.get_http_request_header(HEADER_NAME) {
+ let (name, value) = header.split_once('=').unwrap();
+
+ ctx.add_http_response_header(name, value);
+ ctx.set_http_request_header(HEADER_NAME, None);
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs
new file mode 100644
index 00000000..38d78be5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs
@@ -0,0 +1,147 @@
+use crate::*;
+
+pub struct TestHttp {
+ pub config: Option,
+}
+
+impl TestHttp {
+ pub fn send_plain_response(&mut self, status: StatusCode, body: Option<&str>) {
+ self.send_http_response(status.as_u16() as u32, vec![], body.map(|b| b.as_bytes()))
+ }
+
+ fn get_prop(&self, ns: &str, prop: &str) -> String {
+ if let Some(addr) = self.get_property(vec![ns, prop]) {
+ match std::str::from_utf8(&addr) {
+ Ok(value) => value.to_string(),
+ Err(_) => "".to_string(),
+ }
+ } else {
+ "".to_string()
+ }
+ }
+
+ fn set_prop(&self, ns: &str, prop: &str, value: Option<&str>) {
+ let value: Option<&[u8]> = value.map(|v| v.as_bytes());
+ self.set_property(vec![ns, prop], value);
+ }
+
+ fn send_http_dispatch(&mut self, config: TestConfig) -> Action {
+ let mut timeout = Duration::from_secs(0);
+ let mut headers = Vec::new();
+
+ headers.push((
+ ":method",
+ config
+ .map
+ .get("method")
+ .map(|v| v.as_str())
+ .unwrap_or("GET"),
+ ));
+
+ headers.push((
+ ":path",
+ config.map.get("path").map(|v| v.as_str()).unwrap_or("/"),
+ ));
+
+ headers.push((
+ ":authority",
+ config
+ .map
+ .get("host")
+ .map(|v| v.as_str())
+ .unwrap_or("127.0.0.1:15555"),
+ ));
+
+ if let Some(vals) = config.map.get("headers") {
+ for (k, v) in vals.split('|').filter_map(|s| s.split_once(':')) {
+ headers.push((k, v));
+ }
+ }
+
+ if let Some(val) = config.map.get("timeout") {
+ if let Ok(t) = parse_duration::parse(val) {
+ timeout = t;
+ }
+ }
+
+ self.dispatch_http_call(
+ config
+ .map
+ .get("host")
+ .map(|v| v.as_str())
+ .unwrap_or("127.0.0.1:15555"),
+ headers,
+ config.map.get("body").map(|v| v.as_bytes()),
+ vec![],
+ timeout,
+ )
+ .expect("dispatch error");
+
+ Action::Pause
+ }
+
+ pub fn run_tests(&mut self, cur_phase: TestPhase) -> Action {
+ const PHASE_HEADER_NAME: &str = "X-PW-Phase";
+ const TEST_HEADER_NAME: &str = "X-PW-Test";
+ const INPUT_HEADER_NAME: &str = "X-PW-Input";
+
+ let opt_input = self.get_http_request_header(INPUT_HEADER_NAME);
+ let opt_test = self.get_http_request_header(TEST_HEADER_NAME);
+ let on_phase = self.get_http_request_header(PHASE_HEADER_NAME).map_or(
+ TestPhase::RequestHeaders,
+ |s| {
+ s.parse()
+ .unwrap_or_else(|_| panic!("unknown phase: {:?}", s))
+ },
+ );
+
+ if cur_phase == on_phase {
+ info!("[proxy-wasm] testing in \"{:?}\"", on_phase);
+
+ if cur_phase == TestPhase::RequestHeaders || cur_phase == TestPhase::RequestBody {
+ self.set_http_request_header(INPUT_HEADER_NAME, None);
+ self.set_http_request_header(TEST_HEADER_NAME, None);
+ self.set_http_request_header(PHASE_HEADER_NAME, None);
+
+ add_request_header(self);
+ }
+
+ add_response_header(self);
+
+ if let Some(test) = opt_test {
+ match test.as_str() {
+ "trap" => panic!("trap msg"),
+ "local_response" => {
+ self.send_plain_response(StatusCode::OK, opt_input.as_deref())
+ }
+ "get_kong_property" => {
+ let name = &opt_input.unwrap_or("".to_string());
+ let value = self.get_prop("kong", name);
+ info!("[proxy-wasm] kong.{}: \"{:?}\"", name, value);
+ self.send_plain_response(StatusCode::OK, Some(&value))
+ }
+ "set_kong_property" => {
+ if let Some(input) = opt_input {
+ let (key, value) = match input.split_once('=') {
+ Some((key, value)) => (key, Some(value)),
+ None => (input.as_ref(), None),
+ };
+
+ self.set_prop("kong", key, value);
+ info!("[proxy-wasm] kong.{} = \"{:?}\"", key, value);
+ }
+ }
+ "echo_http_dispatch" => {
+ let config = TestConfig::from_str(&opt_input.unwrap_or("".to_string()))
+ .expect("invalid configuration");
+
+ return self.send_http_dispatch(config);
+ }
+ _ => (),
+ }
+ }
+ }
+
+ Action::Continue
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/types.rs b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/types.rs
new file mode 100644
index 00000000..29f4d86a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/proxy_wasm_filters/tests/src/types.rs
@@ -0,0 +1,30 @@
+use crate::*;
+use std::collections::HashMap;
+
+pub struct TestConfig {
+ pub map: HashMap,
+}
+
+impl FromStr for TestConfig {
+ type Err = std::str::Utf8Error;
+
+ fn from_str(s: &str) -> Result {
+ Ok(TestConfig {
+ map: s
+ .split_whitespace()
+ .filter_map(|s| s.split_once('='))
+ .map(|(k, v)| (k.to_string(), v.to_string()))
+ .collect(),
+ })
+ }
+}
+
+#[derive(Debug, Eq, PartialEq, enum_utils::FromStr)]
+#[enumeration(rename_all = "snake_case")]
+pub enum TestPhase {
+ RequestHeaders,
+ RequestBody,
+ ResponseHeaders,
+ ResponseBody,
+ Log,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/ca.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/ca.crt
new file mode 100644
index 00000000..54b617b7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/ca.crt
@@ -0,0 +1,121 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:1e:bb:94:6a:44:ac:b3:2d:3b:fb:25:68:c5:0f:c7:8e:15:54:a7
+ Signature Algorithm: sha512WithRSAEncryption
+ Issuer: C = US, ST = California, O = Kong, CN = Kong Testing Root CA
+ Validity
+ Not Before: Jan 4 06:45:00 2023 GMT
+ Not After : Dec 30 06:45:00 2042 GMT
+ Subject: C = US, ST = California, O = Kong, CN = Kong Testing Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:d5:57:b6:92:01:54:fa:d9:7c:7f:05:10:93:cd:
+ 22:a5:a1:ca:f0:ef:58:12:da:08:33:f1:39:55:41:
+ 39:03:4a:98:54:1b:a7:d5:30:53:05:69:2d:69:f9:
+ d7:b7:b7:8a:5d:b0:cf:3c:bd:9e:51:e7:35:bc:b4:
+ 5c:db:f4:f0:44:77:9c:2e:51:e0:9f:93:49:42:6b:
+ 5d:f7:de:35:72:68:ee:c2:d7:08:47:ff:09:fa:75:
+ f1:2e:fc:34:e1:d1:b6:75:11:c6:72:18:7a:80:ff:
+ b4:df:82:e5:25:9d:06:70:fc:64:5f:0b:a0:ec:3b:
+ 82:65:6e:13:23:18:db:22:d2:66:79:cd:d9:9e:24:
+ af:76:b2:30:3a:cf:c2:50:6e:8e:61:f1:f1:c4:ad:
+ 3e:28:53:c8:6e:ee:98:f6:d2:ed:ad:7f:fe:46:98:
+ 8e:1d:4b:c4:21:ab:e3:43:76:7f:71:2c:d7:0f:d2:
+ 30:a3:42:b9:23:fc:99:ed:18:d8:a0:64:d2:9c:93:
+ 02:98:33:e5:9e:c0:48:35:8a:de:a1:46:a3:e8:02:
+ 06:cb:17:ff:2f:2b:b2:2a:28:80:48:7c:a6:01:d9:
+ 26:b1:1a:71:7a:f2:46:fc:b8:f7:d4:90:89:5f:73:
+ 10:56:4e:db:b4:de:39:c5:ee:61:4e:58:1d:10:f6:
+ cb:18:35:8a:d9:b6:c8:67:c4:fd:59:3b:d2:30:f2:
+ 33:f6:9b:c7:71:27:a8:c4:54:d7:26:86:78:2d:ef:
+ 51:e6:46:d2:56:8e:e3:4d:26:70:15:ef:a2:ac:c1:
+ 90:d9:24:60:cb:f8:54:47:91:78:e9:4a:b3:47:82:
+ e8:75:c3:2d:40:df:95:cf:8a:ca:6b:47:cb:f1:3f:
+ 01:3c:91:99:cb:6d:64:6f:35:69:6d:51:68:eb:bb:
+ f8:27:5d:4f:5e:df:fe:a5:3e:29:ee:ed:d2:65:c4:
+ 75:15:06:f2:10:51:0e:80:8e:23:d6:1c:00:be:a7:
+ f4:53:ca:c4:5e:b1:ff:8f:d2:d9:b8:6a:26:ee:ba:
+ bb:77:02:54:5c:f9:a8:f1:fb:84:aa:61:6f:03:d0:
+ 0e:67:7e:9f:a8:3d:57:f2:f0:35:ff:3d:c1:63:56:
+ 12:75:66:e0:1d:3a:b9:d0:b6:a3:12:9f:a9:30:01:
+ 0b:1f:87:74:d2:30:88:ea:e3:f5:ea:f5:d1:6c:34:
+ 33:7f:aa:a3:d5:59:ee:08:8c:a0:37:5f:57:c9:43:
+ e4:b6:ad:ae:be:43:dc:46:b7:ba:dd:e0:21:51:bb:
+ 83:b0:16:95:ab:b0:13:a6:d3:22:f4:c6:c8:e0:2a:
+ 5b:82:f0:dd:e5:55:d8:d1:1b:73:ab:47:3a:c6:77:
+ ed:dc:bb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ 13:6D:39:C0:7E:81:64:DE:4D:9F:5A:64:60:95:F7:44:37:5F:13:8E
+ Signature Algorithm: sha512WithRSAEncryption
+ Signature Value:
+ b2:7d:b7:01:d2:28:4d:eb:ff:b5:db:ca:02:5e:72:11:c8:34:
+ 49:ca:ea:86:00:28:f4:4f:7d:9e:3d:ed:d7:ef:ac:f8:59:20:
+ 78:3b:51:96:a4:e8:f8:99:77:f4:69:d7:c0:bf:26:30:43:de:
+ f6:71:b0:c1:59:23:85:29:ea:80:b8:52:2c:1a:8a:d0:c0:03:
+ 82:9c:83:eb:04:5d:08:e9:fd:dc:ce:a7:22:e4:d7:0d:cf:62:
+ 7b:dd:52:29:70:cb:04:1d:ad:cc:be:b4:04:fc:2b:8e:46:83:
+ 1f:87:5f:90:5b:d7:6b:b3:e1:30:55:b7:1b:9c:7d:a4:85:7b:
+ 12:d0:4d:a4:2b:2c:79:de:3e:1c:cb:be:04:6c:08:48:cd:b1:
+ d5:72:96:cb:17:18:88:35:20:ca:c5:cf:4f:73:7e:73:2f:04:
+ cf:3d:90:7c:0f:c5:1a:2c:6e:89:87:19:ed:28:99:50:b9:b5:
+ 3b:c1:68:fa:51:de:35:ad:ae:a6:17:c5:74:47:fb:fa:31:b0:
+ 59:21:6d:2a:50:a5:28:2e:12:5a:c8:a3:4f:7c:78:d8:62:fc:
+ e6:c7:d8:53:6b:9d:56:db:5b:71:4d:2c:32:01:e7:2e:ca:a4:
+ 93:92:7e:29:8c:13:ed:6e:f2:b0:59:53:03:69:20:93:69:5c:
+ 21:3e:0b:a7:9c:db:39:fc:18:6e:96:9a:7c:86:0f:fb:99:92:
+ 3b:c2:09:5d:ce:b0:cc:0d:ab:28:58:10:6c:5c:11:09:26:d2:
+ d6:1d:ac:cf:8e:0e:08:14:ed:5e:78:9b:4e:e9:c4:39:95:dc:
+ b6:c4:a1:1f:ae:5f:6c:47:47:a6:3a:8c:0c:df:82:7a:7f:a2:
+ d0:ed:e4:f9:d9:e4:1f:7e:a5:71:65:8d:f0:44:78:1a:ee:7d:
+ b1:af:ea:a1:8f:4a:50:cd:2c:76:1f:06:1b:48:1f:42:2f:72:
+ e5:35:0b:71:68:ef:a9:8e:42:00:67:9b:e4:30:36:29:37:12:
+ eb:3c:a2:74:7b:94:fc:3b:84:b9:7d:f5:b9:fc:d5:08:74:b6:
+ ea:9c:89:78:94:2e:51:6a:37:60:9b:24:95:da:63:bd:d7:ca:
+ 40:2c:57:8c:dd:5c:fd:78:d8:51:0c:bc:23:06:9e:fb:b0:8a:
+ 50:ea:aa:c1:f0:a3:a1:85:d8:81:a1:84:19:c2:71:0d:ce:dd:
+ b7:e8:c7:b9:4f:2f:7d:5b:83:34:d9:2c:1c:3d:68:92:2f:4c:
+ 63:67:d3:cb:9a:c5:e8:d6:98:76:d4:32:03:92:19:02:73:09:
+ 1a:29:74:58:e9:a1:29:f8:30:54:f4:fb:9e:c8:13:7f:96:59:
+ 2c:54:19:40:99:3e:0e:ee
+-----BEGIN CERTIFICATE-----
+MIIFcDCCA1igAwIBAgIUBB67lGpErLMtO/slaMUPx44VVKcwDQYJKoZIhvcNAQEN
+BQAwUDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDTALBgNVBAoT
+BEtvbmcxHTAbBgNVBAMTFEtvbmcgVGVzdGluZyBSb290IENBMB4XDTIzMDEwNDA2
+NDUwMFoXDTQyMTIzMDA2NDUwMFowUDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
+bGlmb3JuaWExDTALBgNVBAoTBEtvbmcxHTAbBgNVBAMTFEtvbmcgVGVzdGluZyBS
+b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1Ve2kgFU+tl8
+fwUQk80ipaHK8O9YEtoIM/E5VUE5A0qYVBun1TBTBWktafnXt7eKXbDPPL2eUec1
+vLRc2/TwRHecLlHgn5NJQmtd9941cmjuwtcIR/8J+nXxLvw04dG2dRHGchh6gP+0
+34LlJZ0GcPxkXwug7DuCZW4TIxjbItJmec3ZniSvdrIwOs/CUG6OYfHxxK0+KFPI
+bu6Y9tLtrX/+RpiOHUvEIavjQ3Z/cSzXD9Iwo0K5I/yZ7RjYoGTSnJMCmDPlnsBI
+NYreoUaj6AIGyxf/LyuyKiiASHymAdkmsRpxevJG/Lj31JCJX3MQVk7btN45xe5h
+TlgdEPbLGDWK2bbIZ8T9WTvSMPIz9pvHcSeoxFTXJoZ4Le9R5kbSVo7jTSZwFe+i
+rMGQ2SRgy/hUR5F46UqzR4LodcMtQN+Vz4rKa0fL8T8BPJGZy21kbzVpbVFo67v4
+J11PXt/+pT4p7u3SZcR1FQbyEFEOgI4j1hwAvqf0U8rEXrH/j9LZuGom7rq7dwJU
+XPmo8fuEqmFvA9AOZ36fqD1X8vA1/z3BY1YSdWbgHTq50LajEp+pMAELH4d00jCI
+6uP16vXRbDQzf6qj1VnuCIygN19XyUPktq2uvkPcRre63eAhUbuDsBaVq7ATptMi
+9MbI4CpbgvDd5VXY0Rtzq0c6xnft3LsCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG
+MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBNtOcB+gWTeTZ9aZGCV90Q3XxOO
+MA0GCSqGSIb3DQEBDQUAA4ICAQCyfbcB0ihN6/+128oCXnIRyDRJyuqGACj0T32e
+Pe3X76z4WSB4O1GWpOj4mXf0adfAvyYwQ972cbDBWSOFKeqAuFIsGorQwAOCnIPr
+BF0I6f3czqci5NcNz2J73VIpcMsEHa3MvrQE/CuORoMfh1+QW9drs+EwVbcbnH2k
+hXsS0E2kKyx53j4cy74EbAhIzbHVcpbLFxiINSDKxc9Pc35zLwTPPZB8D8UaLG6J
+hxntKJlQubU7wWj6Ud41ra6mF8V0R/v6MbBZIW0qUKUoLhJayKNPfHjYYvzmx9hT
+a51W21txTSwyAecuyqSTkn4pjBPtbvKwWVMDaSCTaVwhPgunnNs5/Bhulpp8hg/7
+mZI7wgldzrDMDasoWBBsXBEJJtLWHazPjg4IFO1eeJtO6cQ5ldy2xKEfrl9sR0em
+OowM34J6f6LQ7eT52eQffqVxZY3wRHga7n2xr+qhj0pQzSx2HwYbSB9CL3LlNQtx
+aO+pjkIAZ5vkMDYpNxLrPKJ0e5T8O4S5ffW5/NUIdLbqnIl4lC5RajdgmySV2mO9
+18pALFeM3Vz9eNhRDLwjBp77sIpQ6qrB8KOhhdiBoYQZwnENzt236Me5Ty99W4M0
+2SwcPWiSL0xjZ9PLmsXo1ph21DIDkhkCcwkaKXRY6aEp+DBU9PueyBN/llksVBlA
+mT4O7g==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/ca.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/ca.key
new file mode 100644
index 00000000..871f6b34
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/ca.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEA1Ve2kgFU+tl8fwUQk80ipaHK8O9YEtoIM/E5VUE5A0qYVBun
+1TBTBWktafnXt7eKXbDPPL2eUec1vLRc2/TwRHecLlHgn5NJQmtd9941cmjuwtcI
+R/8J+nXxLvw04dG2dRHGchh6gP+034LlJZ0GcPxkXwug7DuCZW4TIxjbItJmec3Z
+niSvdrIwOs/CUG6OYfHxxK0+KFPIbu6Y9tLtrX/+RpiOHUvEIavjQ3Z/cSzXD9Iw
+o0K5I/yZ7RjYoGTSnJMCmDPlnsBINYreoUaj6AIGyxf/LyuyKiiASHymAdkmsRpx
+evJG/Lj31JCJX3MQVk7btN45xe5hTlgdEPbLGDWK2bbIZ8T9WTvSMPIz9pvHcSeo
+xFTXJoZ4Le9R5kbSVo7jTSZwFe+irMGQ2SRgy/hUR5F46UqzR4LodcMtQN+Vz4rK
+a0fL8T8BPJGZy21kbzVpbVFo67v4J11PXt/+pT4p7u3SZcR1FQbyEFEOgI4j1hwA
+vqf0U8rEXrH/j9LZuGom7rq7dwJUXPmo8fuEqmFvA9AOZ36fqD1X8vA1/z3BY1YS
+dWbgHTq50LajEp+pMAELH4d00jCI6uP16vXRbDQzf6qj1VnuCIygN19XyUPktq2u
+vkPcRre63eAhUbuDsBaVq7ATptMi9MbI4CpbgvDd5VXY0Rtzq0c6xnft3LsCAwEA
+AQKCAgBwQKWkbyT6lEKoRs7xJcdsJRQ174mE6cnVIsCK9jV8YNyDrMWDK9kTCMNH
+dpklZmJcZ7KzAAZ0i9Y/gxs09M0TCWhZCuXIsOOkGgAocnfmygWO6TvHPg9PBI2x
+rixZAVIiiQbEc9LJW0IdNK9DOjrwaiyZwfGbOriii+dv2R08Vj5rKn+tcRoNtzYf
+S7+vOGycZoRSeuEwsNzOWaaMgHFkj+sH1C86hOoe2WVL0ua9ct15ypui23G02K1Z
+DnC0/DfBAK0lznCsNfoIihgX/aYyZhaS9/5iIHivK/5LpaJnaI2uM/6vtRja0qw7
+4Q0W9uEKuJVrtl3pokL6yOwKSACVtt7UH8cdzxTCG+6PZSDi4Taumv4AI3/YlXEK
+G6RyeYTsHrFJIt3nwTxnG06G/YiRCXCPd6UEgFQBS+E1/iMPJYe0lFliQJ6ugnGd
+wn5alJ/nIPWtAGEOa6TOltVQN/1y2G+BkJpvIVVUU+a6vTp+M3QCX3Q25Sl3/bOv
+3uDtE08cWSQAzA+njfX6ySckMq4O3cbq633CPluIzWTT/YjG+s39mUsIXLfZEqqk
+e+U0rfOmRa1lcqQdWLNzHHp5HT150mEDSUVOgykEA8JYAzRdy8G2A2mcLwob8/iA
+yagwACE3UYB5s2jN1b24OfEZCgKLnbvMEsppJOYqiId5x2efGQKCAQEA2lxVbS/7
+W6P/0on9B+QvMS1xIATiGhgHjbk4UBuW4Y0cIbJoAeSJnOOteFYRMxvxkWCkTb6q
+V9IxNm+PZWHjpkbxSAVvX00X08Dqvp6OtWmxHU5dvCEUTa9HudBSZPBtWG7iXRJK
+SuERLFOeD7KnAhkdqROVtCz8YBdzEljnZ4j/YzjWpAMQBPurwRmOVQVz36Ukuveh
+qGLXm8s6YPP9CiCMxt4DHjef51AkqNqewpN6TK5KPSZjruTWbzhT8934WWqXSYuU
+BYJ6LgEpVWF0dA8MXbWdbzRsocAhwm6ABa5BRZyUmZyl9lH9DUxlJ5omzZ+2t4/i
+D4QbzBUM+dBtRwKCAQEA+h3yUDVW0FRu5gG/QfbPcOKukbxnbwTJZMgyvPxIoAzg
+LxJiSYBPo4osW+dPtg8P+Sta7JEYB6PovSJjuX6e5i7oRJTIhQPc+0R5vAaUlrOl
+Li+cU2/sxuFf5b+R5OqEjtSrS8oUZOn28ShZS9rCTZUn+bf6yZA7fI4CoFIXj7ar
+xBq/5jGs45LvXaaUPc9XzBTkhWacpd9Hinx5d9v6O0C9UsMGoYk+Y6LfW1QV2l19
+gp/oWOXOWde454hirae0n0peeEwv9Ep7LEbf+CCJU/RyWYm6O/zIK9jU2fZsmACw
+W2TggftuEJY2/I1CPxfDOXti9c9FRgLSiVHSDzK+7QKCAQByYuoRP5Bh6iBKDyTw
+rFUYYuCe0FANMUdLs5pPRJSeZQg2krmvPCPmftJRdmyeJGZALKsFWvrq9F35USmC
+B2x0nzcn7kjwWHdB4w5VesPxPoOcgX/S3FVBeK+PJBT1cYkmSTflX35xiUMwwEDN
+ol2gWL3T40GJ2AMA52fNVasq7nYyaQTvd/c9VQUdK6EG4stXfbAnDS+vANBHeYeE
+YGvOkUyNpKFng+YNC0uY9KUz8oOfhbG4JNMVPCUksBIybrX5SUAlM6v/0uDkFpGr
+e1jAr11f/ZKSPZkmhnpo2u/PigABUkv5yDicN0jjXYCj5Tmsf5z8va/DRwY0u1ZH
+yypjAoIBAHFoqrqbtOV5o18/Y41xb9XrsuP53ZyFOxwrenYenn+T1wMA/vf86h9p
+Q3vYglg2tDNy6SNjFtZACAPaWAV/2GTe2ApgvBs0CTsVbW1IPo+mnTs381YR5fa3
+slfmaSy2+awZ2iHfWyf2vjXS6cpvQrMS8rFULq6+a3qqmZ5AGtbbKT8eMe0akR4P
+PeHk6kqsfU7YGlYylMzRVQsCLcGfSPMdA7tHGvab2GItM8GhetcslQBpqVzFtq8e
+FYVGXhgHdurTOcqqIQRP0VHeQSes+RfMOx1GSd9xWwImqzy5c7vodA68yt+lNd7n
+fb89/c/F4otp2xFCDlMUbIo/Q1QI2nECggEAedXZSGOw3BLY8yDRoRczibN9AlwF
+AgQeBS1g0DS6Mz+9C9j1FP9I8oFqwrFVtuYITqaI9LL4Zdebbebd1r4AmoxVrv4u
+WtHH80hHpr5tkONhHxN4IG4BLoYQQiIAD0Hg94TEUUDAosHM5zKgdLkqEX75rj9p
+Sz2jWQyDoh809CJpUSyDqnEeb8/MZ0vwWUrYINIImFNjd3w1jasAxTdul7hmLQ2P
+e9dPRzQ/gIgmiUvbBJZ1ujSFVnhIqfoAGkqkoUQ9EIFhpuzAnNHPPwZzGDxDedE4
+KVgGvJrXP6eJfgqOMjaXHPEF2eZu3gBcFKE30Vg+QW8YvhYOCVxiBFua/g==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/docker-entrypoint.sh b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/docker-entrypoint.sh
new file mode 100755
index 00000000..a448be06
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/docker-entrypoint.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+set -e
+
+echo "Kong CI redis container..."
+
+if [ -d /workspace ] ; then
+ echo "Starting test server..."
+
+ redis-server \
+ --port 6379 \
+ --tls-port 6380 \
+ --tls-cert-file /workspace/spec/fixtures/redis/server.crt \
+ --tls-key-file /workspace/spec/fixtures/redis/server.key \
+ --tls-cluster no \
+ --tls-replication no \
+ --tls-auth-clients no
+fi
+
+tail -f /dev/null
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/server.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/server.crt
new file mode 100644
index 00000000..ce7dd4f6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/server.crt
@@ -0,0 +1,128 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 09:a1:b0:61:a9:f6:73:c8:f5:b4:1b:e3:e9:bc:ad:57:88:f5:cf:44
+ Signature Algorithm: sha512WithRSAEncryption
+ Issuer: C = US, ST = California, O = Kong, CN = Kong Testing Root CA
+ Validity
+ Not Before: Jan 4 06:45:00 2023 GMT
+ Not After : Dec 30 05:45:00 2042 GMT
+ Subject: C = US, ST = California, O = Kong, CN = test-redis.example.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:cc:c1:d2:98:49:98:4b:6a:8c:4a:40:1b:93:35:
+ 29:29:55:78:b9:b9:e2:3a:ac:25:07:4f:7c:0c:da:
+ bf:f0:18:5d:57:9f:03:90:9f:30:d6:ff:7d:31:e9:
+ 3a:0e:c5:f2:e6:d8:1f:af:d4:7f:8a:bd:72:1e:4d:
+ ba:75:9f:2b:f3:77:72:03:30:7b:ca:67:fc:76:7a:
+ 42:57:81:02:aa:bc:b6:2f:63:e8:72:ec:e6:85:73:
+ f5:1d:a9:4d:47:7f:d2:3c:e2:e9:40:cd:0d:5c:f8:
+ e1:e8:88:89:1a:0d:8f:68:8a:63:9f:62:6c:03:30:
+ 19:ad:db:34:5a:f5:65:85:c5:7c:32:13:24:e8:5f:
+ 30:93:27:ce:01:72:1e:b7:72:48:fc:a6:72:b4:8e:
+ 08:ec:b8:c3:f7:95:60:92:e2:b0:d1:9d:9c:76:41:
+ f4:96:1e:96:a6:ab:73:16:78:7e:6a:8b:27:43:0d:
+ 69:19:6d:b7:6d:c0:21:56:2e:32:6b:ef:dc:31:7b:
+ f0:bc:16:d2:50:3b:bf:fb:7f:65:96:e3:a5:2c:d2:
+ 35:a8:f4:06:82:85:5c:89:02:a0:2f:96:5f:75:f3:
+ 63:22:7c:f3:06:12:66:85:d4:9a:a9:54:d6:12:96:
+ 96:54:0e:da:f5:6f:ae:8c:5a:72:9a:85:d5:9c:63:
+ d1:14:5a:7a:62:44:a5:6f:8d:ed:67:86:e4:34:6f:
+ 26:03:e2:17:57:b8:ee:e9:e7:c0:7d:f1:4e:33:f6:
+ 7f:0a:5c:25:92:04:fb:b1:90:14:e4:dd:cf:16:20:
+ 15:12:87:29:b7:b0:e9:d2:96:4d:1a:16:36:f3:de:
+ dd:0f:e6:55:da:09:df:a1:1a:e7:d0:d8:d2:b6:90:
+ 01:25:24:eb:1a:73:c5:54:d7:75:1e:86:a2:1c:56:
+ 58:66:05:99:5b:bd:e2:8e:12:a6:16:cb:56:f2:16:
+ b2:23:80:1b:d3:5f:ca:17:ec:ad:aa:45:de:76:4b:
+ be:d1:57:94:45:a9:3e:2d:33:1d:ae:e1:ce:27:6b:
+ e5:cf:13:4b:8e:d9:bc:cd:52:a5:7c:bf:0b:eb:8b:
+ 0c:b3:fb:12:b2:44:21:43:d3:56:1f:16:35:09:5f:
+ f7:45:ac:c9:1a:4d:2d:eb:8a:12:9b:35:48:b0:d6:
+ e6:c9:2e:0d:cd:b1:c6:f3:7b:96:6c:cd:f8:82:c9:
+ 29:b3:28:d4:82:82:80:9a:de:b1:3e:67:00:99:1e:
+ 02:b1:15:13:0f:6b:7c:2f:e6:31:ef:13:34:20:89:
+ bf:56:fe:05:41:2d:53:63:a7:ab:d7:d4:fa:ec:81:
+ 23:c6:1f:42:e5:a6:de:0c:08:d0:b0:8c:1b:41:ec:
+ 56:b6:25
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Key Encipherment
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ D9:74:21:02:5A:95:A3:CF:F3:BD:1F:99:66:75:D7:69:B5:E1:3E:02
+ X509v3 Authority Key Identifier:
+ 13:6D:39:C0:7E:81:64:DE:4D:9F:5A:64:60:95:F7:44:37:5F:13:8E
+ X509v3 Subject Alternative Name:
+ DNS:test-redis.example.com
+ Signature Algorithm: sha512WithRSAEncryption
+ Signature Value:
+ 5e:3f:cd:02:be:1d:3e:e9:0a:25:98:a8:ae:a7:d0:23:2c:f8:
+ 46:e1:0c:25:d2:a3:73:19:01:08:f7:fb:18:da:65:f6:b3:4d:
+ 28:bb:37:b3:cc:cc:01:ef:c4:ed:18:92:06:78:9d:81:a4:5a:
+ bf:04:f4:c0:3d:0e:97:65:28:d1:8d:cb:1b:92:46:a1:3c:55:
+ 09:e8:b5:eb:4c:36:9f:5b:79:70:4d:bf:c0:6a:27:83:d2:b5:
+ c9:a2:af:f6:92:1c:f5:e9:1d:28:72:b3:7c:84:81:44:bf:e9:
+ cf:3c:73:3d:07:f4:c2:e5:fa:62:d7:5a:a7:87:e9:16:d4:f2:
+ 92:b0:22:b0:8a:1c:75:b0:f5:e9:91:28:55:1b:57:99:e0:d1:
+ 34:18:c2:11:d9:9a:9e:8b:32:c8:d0:5c:5b:20:eb:ac:7a:7b:
+ ee:05:8c:0c:5a:56:25:a2:c9:71:15:e5:07:c4:e7:99:a0:f7:
+ 38:dd:45:97:43:66:44:f9:d4:08:22:33:b6:ec:5b:09:25:d0:
+ 35:2f:00:3b:ef:05:93:36:d1:39:bf:66:77:ce:12:86:9f:22:
+ 12:53:a9:d2:8a:e3:6b:c2:d9:3a:ee:c6:9f:13:e1:34:15:d0:
+ a4:11:09:93:17:38:f7:e9:f7:d7:64:6a:9f:64:6a:28:50:b1:
+ 61:c6:ac:63:51:01:8c:e4:9c:c8:98:73:38:2c:ea:31:4b:b9:
+ 35:dc:26:08:58:f6:f8:fd:db:70:fb:b4:6c:be:ee:0c:da:87:
+ 90:01:66:c6:5c:08:f3:68:f4:8b:ea:55:54:9e:26:a0:4e:4d:
+ 37:7a:ff:85:22:9d:d8:ec:4e:e7:a9:5f:54:b8:16:73:af:7c:
+ fd:17:af:1f:87:92:b7:8b:c9:12:be:13:bd:0e:d0:6b:c9:df:
+ 6a:a4:e1:8d:87:de:b4:30:94:0a:26:98:23:88:8f:b0:eb:01:
+ 00:60:f0:63:bb:3b:c1:e6:92:0a:77:7b:c5:fa:3e:11:cc:04:
+ 21:48:bd:86:63:7f:ce:b7:be:b3:68:bb:b5:a0:50:ea:df:e9:
+ e8:9e:70:10:f8:10:ec:6c:8a:5e:7e:69:3e:eb:f3:9e:5b:a9:
+ d6:8b:39:40:37:79:47:74:15:aa:04:88:bd:76:a7:07:9b:2e:
+ b4:ee:cc:f0:db:55:94:33:93:fb:52:3c:75:8b:78:ec:eb:fe:
+ 83:f3:76:b5:87:c7:2c:45:65:11:67:9a:4b:ac:0d:46:89:13:
+ 96:56:44:0e:bb:dd:f9:b6:fc:99:d8:37:8d:33:aa:5b:c2:61:
+ c7:20:e1:e7:67:67:b9:79:da:95:8f:60:10:05:84:bb:f0:ab:
+ b4:0e:a7:d9:2e:ac:3a:38
+-----BEGIN CERTIFICATE-----
+MIIFyjCCA7KgAwIBAgIUCaGwYan2c8j1tBvj6bytV4j1z0QwDQYJKoZIhvcNAQEN
+BQAwUDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDTALBgNVBAoT
+BEtvbmcxHTAbBgNVBAMTFEtvbmcgVGVzdGluZyBSb290IENBMB4XDTIzMDEwNDA2
+NDUwMFoXDTQyMTIzMDA1NDUwMFowUjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
+bGlmb3JuaWExDTALBgNVBAoTBEtvbmcxHzAdBgNVBAMTFnRlc3QtcmVkaXMuZXhh
+bXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDMwdKYSZhL
+aoxKQBuTNSkpVXi5ueI6rCUHT3wM2r/wGF1XnwOQnzDW/30x6ToOxfLm2B+v1H+K
+vXIeTbp1nyvzd3IDMHvKZ/x2ekJXgQKqvLYvY+hy7OaFc/UdqU1Hf9I84ulAzQ1c
++OHoiIkaDY9oimOfYmwDMBmt2zRa9WWFxXwyEyToXzCTJ84Bch63ckj8pnK0jgjs
+uMP3lWCS4rDRnZx2QfSWHpamq3MWeH5qiydDDWkZbbdtwCFWLjJr79wxe/C8FtJQ
+O7/7f2WW46Us0jWo9AaChVyJAqAvll9182MifPMGEmaF1JqpVNYSlpZUDtr1b66M
+WnKahdWcY9EUWnpiRKVvje1nhuQ0byYD4hdXuO7p58B98U4z9n8KXCWSBPuxkBTk
+3c8WIBUShym3sOnSlk0aFjbz3t0P5lXaCd+hGufQ2NK2kAElJOsac8VU13UehqIc
+VlhmBZlbveKOEqYWy1byFrIjgBvTX8oX7K2qRd52S77RV5RFqT4tMx2u4c4na+XP
+E0uO2bzNUqV8vwvriwyz+xKyRCFD01YfFjUJX/dFrMkaTS3rihKbNUiw1ubJLg3N
+scbze5ZszfiCySmzKNSCgoCa3rE+ZwCZHgKxFRMPa3wv5jHvEzQgib9W/gVBLVNj
+p6vX1PrsgSPGH0Llpt4MCNCwjBtB7Fa2JQIDAQABo4GZMIGWMA4GA1UdDwEB/wQE
+AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
+BBTZdCECWpWjz/O9H5lmdddpteE+AjAfBgNVHSMEGDAWgBQTbTnAfoFk3k2fWmRg
+lfdEN18TjjAhBgNVHREEGjAYghZ0ZXN0LXJlZGlzLmV4YW1wbGUuY29tMA0GCSqG
+SIb3DQEBDQUAA4ICAQBeP80Cvh0+6QolmKiup9AjLPhG4Qwl0qNzGQEI9/sY2mX2
+s00ouzezzMwB78TtGJIGeJ2BpFq/BPTAPQ6XZSjRjcsbkkahPFUJ6LXrTDafW3lw
+Tb/AaieD0rXJoq/2khz16R0ocrN8hIFEv+nPPHM9B/TC5fpi11qnh+kW1PKSsCKw
+ihx1sPXpkShVG1eZ4NE0GMIR2ZqeizLI0FxbIOusenvuBYwMWlYloslxFeUHxOeZ
+oPc43UWXQ2ZE+dQIIjO27FsJJdA1LwA77wWTNtE5v2Z3zhKGnyISU6nSiuNrwtk6
+7safE+E0FdCkEQmTFzj36ffXZGqfZGooULFhxqxjUQGM5JzImHM4LOoxS7k13CYI
+WPb4/dtw+7Rsvu4M2oeQAWbGXAjzaPSL6lVUniagTk03ev+FIp3Y7E7nqV9UuBZz
+r3z9F68fh5K3i8kSvhO9DtBryd9qpOGNh960MJQKJpgjiI+w6wEAYPBjuzvB5pIK
+d3vF+j4RzAQhSL2GY3/Ot76zaLu1oFDq3+nonnAQ+BDsbIpefmk+6/OeW6nWizlA
+N3lHdBWqBIi9dqcHmy607szw21WUM5P7Ujx1i3js6/6D83a1h8csRWURZ5pLrA1G
+iROWVkQOu935tvyZ2DeNM6pbwmHHIOHnZ2e5edqVj2AQBYS78Ku0DqfZLqw6OA==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/server.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/server.key
new file mode 100644
index 00000000..e0fb26f7
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis/server.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAzMHSmEmYS2qMSkAbkzUpKVV4ubniOqwlB098DNq/8BhdV58D
+kJ8w1v99Mek6DsXy5tgfr9R/ir1yHk26dZ8r83dyAzB7ymf8dnpCV4ECqry2L2Po
+cuzmhXP1HalNR3/SPOLpQM0NXPjh6IiJGg2PaIpjn2JsAzAZrds0WvVlhcV8MhMk
+6F8wkyfOAXIet3JI/KZytI4I7LjD95VgkuKw0Z2cdkH0lh6WpqtzFnh+aosnQw1p
+GW23bcAhVi4ya+/cMXvwvBbSUDu/+39lluOlLNI1qPQGgoVciQKgL5ZfdfNjInzz
+BhJmhdSaqVTWEpaWVA7a9W+ujFpymoXVnGPRFFp6YkSlb43tZ4bkNG8mA+IXV7ju
+6efAffFOM/Z/ClwlkgT7sZAU5N3PFiAVEocpt7Dp0pZNGhY2897dD+ZV2gnfoRrn
+0NjStpABJSTrGnPFVNd1HoaiHFZYZgWZW73ijhKmFstW8hayI4Ab01/KF+ytqkXe
+dku+0VeURak+LTMdruHOJ2vlzxNLjtm8zVKlfL8L64sMs/sSskQhQ9NWHxY1CV/3
+RazJGk0t64oSmzVIsNbmyS4NzbHG83uWbM34gskpsyjUgoKAmt6xPmcAmR4CsRUT
+D2t8L+Yx7xM0IIm/Vv4FQS1TY6er19T67IEjxh9C5abeDAjQsIwbQexWtiUCAwEA
+AQKCAgEAjJGDwojDxQKgzVi1lZopZ/cFqnuylBUaVqp6v1ht7KbNbhn8mIyxOuir
+SliTQxEicNhu6Ic6CEWG0scJ+zYLNloKK6ZdVdeNusi0Qt6OtihX6rDsI/n/SB8T
+aAmSxEM8UhB1kcc0JV+3t6wEc55blalsOz+WZ5neBz019DwENpIdcUMzU1QGRQBO
+rS9rZwVOliSvGsVn2xv9bTtf0XdPbJiHkag2Adl+E24g1IxkPUDK832BabOo+e+s
+8z1D4FYLFO3Bl18Tg4GBi2cqlywxeVPXAuaEkZZ8sJLc5c6WOqOcq1Cchs6bE8Wh
+aB6V2K0JBywrpdPGQRTXGL5Ip9Te85+bDvyggQMT9FpT4Ebxau94YGXwFK/OWM04
+sPUbDntPJyMuTzSOxRykegChQIGWaOMSeaTnn0Ff6kHZ6rkF2VLV5KwcOzl92XS2
+OZEa/LrFPzNNylliFY0FICSgzP4zbvtH4of8tCvN+XccB+yM5+6BDik/d0KsCp50
+XSVRaerf/+epo1c5k1ED44UldF4zxxagojJabNt85afg7L3ASqUxFCkNtOQB8r+8
+VBDkz4P64Wnonl4CMdx4dzGX5wZdtiq3PzLRFMP219SVy1c08mnpQFqTv6fXRuHO
+bqZFZ4Hp4PaVI5pTZ7gkwzVW0Nx4T+INhMCTWenAE8x1vzbL1QECggEBANzutYKr
+3jN+hKFL51wNctuSNQcgPYJleKKKT6WDrd4s7TjoGs1fUKcQySqnMC26QZdlcVZs
+xBirJYz3mMS0RamKTTHZ5Ik08nT8i3nOK1gLd/kJO+AtljrrKG2wzawrg/YyRq+l
+ndhgKieJQxH3GCBN/l99wfRibkA/kuUgV/DFACFGsO0/wBZb0/UgiqjSpLaikpRh
+fZY2Wc5/Jb0PPeofSfHGb9MMCDyONrF16ydlJSAlvCuG2rwoPryxe14ZdjVhM2BO
+svhuJRwqe9PC9gwo9QkIAm4wvXHq9pcoubcxhzWPKlfEg1IV9rcIJ0uCHL6aLGvg
+79hMBjofWRbE1jMCggEBAO1B2ccqvfESzeBzMCdjTGChFcbVn3gXmnQsahB1VRTt
+KLLT8IIVYyuVBbzUkaxVACanLNxF6T1HGMd+hTnAytvge5d6lMpgHcOIKRptVU/J
+63o0jB93K4dttkE8ByftCpLasuK0/hKcDJ4A6GFet7jXIgJXDS69LSL9U5NrT6LK
+a1cNaKbnunGynhLOjO4OWlc080G0I4Pwu0zPhPfLv3TwpFWZio1RhAkSqpMZaHKz
+Q8Qrs2PvXvBH7PM/HPaUyBGFntx1YXH4z6QKxCPO4SoXZnL7+J82bKrHbFmElh7t
+YrdnAokjE0AHhZDfb1dyEb1Z2HeqS+j/CO3JlwKpekcCggEBAJzM6pP4SPbBF36m
+sWhavybpCKurDRyryceKZGazI0YpGqAl00fpGwPHXQ7ho8cAhybdP2g4P6DGbxsy
+awFIdJyUZJ855wIeSuoOhysG0Spm0Vo1XIKJuDLOzV20evRz2e901Ug6QeHctm5i
+8/AfL8dVs3Cwf2RkK517wVTO9LsUBjiXxGBNu5XizHcQBnk1LuPUVDXtT0W6A1kU
+UoNw+t9cH43x6VGfG4Vm5ZhjeWb3WTcMsRUvW7To10XyrP0nEwdlmiIDGPBKtBne
+aQ3tM9WDiA1F2vu7qejc+vBjXhOPmke/+Sxbc1xh7D0RE1p62M3J/DcAaRlZM54y
+u3b2cpMCggEBALcwgyBfBi2fYUsOZX3kE9MATboqs2icgOt2Z6axkbIIs8XwEuG9
+9cZu1/FHB/tR3j36Eo85g6+Gt8FBFUjUbU18dLEvOrdPo2uYNHRtOtPSinjfHdol
+v3xf37tayAOx6Noe9sRJD2v7BVryRHr6EU0s/ttjr5AJDVLY2rEWyHRfaqXaepV0
+kua7DYZj6Tjd6C8xeSmgF1QGiffyuy5BKWD3dUuKtAoNiK8gtIfDtHvrokVToL1m
+050fS/s9HfXeRuQQkeqSz1yaymhUz8D+OaiwTLA3kW4NLbZnKGeuEeNrUy9c3/5X
+EMP9ismjW2rfbocPWi57VQVf9dr0Lh8mEH0CggEARX6i78zZxQHLwePavxpOj0oX
+5xc0alYgTF5qALnX6OolV0aqVPgjDDbIK0SU4CVscgrKW40FZpwTuNYCvWcuj9HA
+bSlJ92MrUEjgwzuCwIkpldc6jIQEfvUIrlMWqdT0ewO6pA7PSDLjtCzTybGmfxUQ
+BDv3nEZsf30marG9jc8LO67a1XqZsQ1f4670zby/YcX6K7ipXVM7Ta4sMpYHiVTY
+PwEZl9+s8cxRzCvN2pGDqZs9BMp10moJz+ZrIWqK/RX2XB7HffVyVNeRMNJCG61h
+bVGx6LLdHYkhqM3HyPzV6/1+NzKco1TgiNx8bq9xU8+VkN5kHr9GaiF/ociuWg==
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/redis_cluster/redis-cluster-compose.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis_cluster/redis-cluster-compose.yaml
new file mode 100644
index 00000000..0b4e62a3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/redis_cluster/redis-cluster-compose.yaml
@@ -0,0 +1,67 @@
+version: '3.5'
+name: redis-cluster
+services:
+ rc-node-1:
+ image: redis:7.0.2
+ volumes:
+ - rc-node-1-data:/data
+ ports:
+ - "7000:7000"
+ command: redis-server --port 7000 --cluster-enabled yes --cluster-port 17000 --cluster-config-file rc-node-1.conf --cluster-node-timeout 5000 --appendonly yes --cluster-announce-port 7000 --cluster-announce-bus-port 17000 --cluster-preferred-endpoint-type hostname --cluster-announce-hostname localhost --daemonize no --loglevel debug
+ rc-node-2:
+ image: redis:7.0.2
+ volumes:
+ - rc-node-2-data:/data
+ ports:
+ - "7001:7001"
+ command: redis-server --port 7001 --cluster-enabled yes --cluster-port 17001 --cluster-config-file rc-node-2.conf --cluster-node-timeout 5000 --appendonly yes --cluster-announce-port 7001 --cluster-announce-bus-port 17001 --cluster-preferred-endpoint-type hostname --cluster-announce-hostname localhost --daemonize no --loglevel debug
+ rc-node-3:
+ image: redis:7.0.2
+ volumes:
+ - rc-node-3-data:/data
+ ports:
+ - "7002:7002"
+ command: redis-server --port 7002 --cluster-enabled yes --cluster-port 17002 --cluster-config-file rc-node-3.conf --cluster-node-timeout 5000 --appendonly yes --cluster-announce-port 7002 --cluster-announce-bus-port 17002 --cluster-preferred-endpoint-type hostname --cluster-announce-hostname localhost --daemonize no --loglevel debug
+ rc-node-4:
+ image: redis:7.0.2
+ volumes:
+ - rc-node-4-data:/data
+ ports:
+ - "7003:7003"
+ command: redis-server --port 7003 --cluster-enabled yes --cluster-port 17003 --cluster-config-file rc-node-4.conf --cluster-node-timeout 5000 --appendonly yes --cluster-announce-port 7003 --cluster-announce-bus-port 17003 --cluster-preferred-endpoint-type hostname --cluster-announce-hostname localhost --daemonize no --loglevel debug
+ rc-node-5:
+ image: redis:7.0.2
+ volumes:
+ - rc-node-5-data:/data
+ ports:
+ - "7004:7004"
+ command: redis-server --port 7004 --cluster-enabled yes --cluster-port 17004 --cluster-config-file rc-node-5.conf --cluster-node-timeout 5000 --appendonly yes --cluster-announce-port 7004 --cluster-announce-bus-port 17004 --cluster-preferred-endpoint-type hostname --cluster-announce-hostname localhost --daemonize no --loglevel debug
+ rc-node-6:
+ image: redis:7.0.2
+ volumes:
+ - rc-node-6-data:/data
+ ports:
+ - "7005:7005"
+ command: redis-server --port 7005 --cluster-enabled yes --cluster-port 17005 --cluster-config-file rc-node-6.conf --cluster-node-timeout 5000 --appendonly yes --cluster-announce-port 7005 --cluster-announce-bus-port 17005 --cluster-preferred-endpoint-type hostname --cluster-announce-hostname localhost --daemonize no --loglevel debug
+ create-cluster:
+ image: redis:7.0.2
+ command: redis-cli --cluster create rc-node-1:7000 rc-node-2:7001 rc-node-3:7002 rc-node-4:7003 rc-node-5:7004 rc-node-6:7005 --cluster-replicas 1 --cluster-yes
+ depends_on:
+ - rc-node-1
+ - rc-node-2
+ - rc-node-3
+ - rc-node-4
+ - rc-node-5
+ - rc-node-6
+
+volumes:
+ rc-node-1-data:
+ rc-node-2-data:
+ rc-node-3-data:
+ rc-node-4-data:
+ rc-node-5-data:
+ rc-node-6-data:
+
+networks:
+ default:
+ name: redis-cluster
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/reload.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/reload.conf
new file mode 100644
index 00000000..4f48721e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/reload.conf
@@ -0,0 +1,3 @@
+prefix = servroot
+nginx_main_worker_processes = 1
+proxy_listen = 0.0.0.0:9000
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/router_path_handling_tests.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/router_path_handling_tests.lua
new file mode 100644
index 00000000..d03c20da
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/router_path_handling_tests.lua
@@ -0,0 +1,212 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local cycle_aware_deep_copy = require("kong.tools.table").cycle_aware_deep_copy
+
+-- The following tests are used by unit and integration tests
+-- to test the router path handling. Putting them here avoids
+-- copy-pasting them in several places.
+--
+-- The tests can obtain this table by requiring
+-- "spec.fixtures.router_path_handling_tests"
+--
+-- The rows are sorted by service_path, route_path, strip_path, path_handling and request_path.
+--
+-- Notes:
+-- * The tests are parsed into a hash form at the end
+-- of this file before they are returned.
+-- * Before a test can be executed, it needs to be "expanded".
+-- For example, a test with {"v0", "v1"} must be converted
+-- into two tests, one with "v0" and one with "v1". Each line
+-- can be expanded using the `line:expand()` method.
+
+local tests = {
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/", "/", {false, true}, {"v0", "v1"}, "/", "/", },
+ { "/", "/", {false, true}, {"v0", "v1"}, "/route", "/route", },
+ { "/", "/", {false, true}, {"v0", "v1"}, "/route/", "/route/", },
+ { "/", "/", {false, true}, {"v0", "v1"}, "/routereq", "/routereq", },
+ { "/", "/", {false, true}, {"v0", "v1"}, "/route/req", "/route/req", }, -- 5
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/", "/route", false, {"v0", "v1"}, "/route", "/route", },
+ { "/", "/route", false, {"v0", "v1"}, "/route/", "/route/", },
+ { "/", "/route", false, {"v0", "v1"}, "/routereq", "/routereq", },
+ { "/", "/route", true, {"v0", "v1"}, "/route", "/", },
+ { "/", "/route", true, {"v0", "v1"}, "/route/", "/", },
+ { "/", "/route", true, {"v0", "v1"}, "/routereq", "/req", }, -- 11
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/", "/route/", false, {"v0", "v1"}, "/route/", "/route/", },
+ { "/", "/route/", false, {"v0", "v1"}, "/route/req", "/route/req", },
+ { "/", "/route/", true, {"v0", "v1"}, "/route/", "/", },
+ { "/", "/route/", true, {"v0", "v1"}, "/route/req", "/req", }, -- 15
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/srv", "/rou", false, "v0", "/roureq", "/srv/roureq", },
+ { "/srv", "/rou", false, "v1", "/roureq", "/srvroureq", },
+ { "/srv", "/rou", true, "v0", "/roureq", "/srv/req", },
+ { "/srv", "/rou", true, "v1", "/roureq", "/srvreq", }, -- 19
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/srv/", "/rou", false, {"v0", "v1"}, "/rou", "/srv/rou", },
+ { "/srv/", "/rou", true, "v0", "/rou", "/srv", },
+ { "/srv/", "/rou", true, "v1", "/rou", "/srv/", }, -- 22
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/", {false, true}, {"v0", "v1"}, "/", "/service", },
+ { "/service", "/", {false, true}, "v0", "/route", "/service/route", },
+ { "/service", "/", {false, true}, "v1", "/route", "/serviceroute", },
+ { "/service", "/", {false, true}, "v0", "/route/", "/service/route/", },
+ { "/service", "/", {false, true}, "v1", "/route/", "/serviceroute/", }, -- 27
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/", {false, true}, "v0", "/routereq", "/service/routereq", },
+ { "/service", "/", {false, true}, "v1", "/routereq", "/serviceroutereq", },
+ { "/service", "/", {false, true}, "v0", "/route/req", "/service/route/req", },
+ { "/service", "/", {false, true}, "v1", "/route/req", "/serviceroute/req", }, -- 31
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", false, "v0", "/route", "/service/route", },
+ { "/service", "/route", false, "v1", "/route", "/serviceroute", },
+ { "/service", "/route", false, "v0", "/route/", "/service/route/", },
+ { "/service", "/route", false, "v1", "/route/", "/serviceroute/", },
+ { "/service", "/route", false, "v0", "/routereq", "/service/routereq", },
+ { "/service", "/route", false, "v1", "/routereq", "/serviceroutereq", },
+ { "/service", "/route", true, {"v0", "v1"}, "/route", "/service", },
+ { "/service", "/route", true, {"v0", "v1"}, "/route/", "/service/", },
+ { "/service", "/route", true, "v0", "/routereq", "/service/req", },
+ { "/service", "/route", true, "v1", "/routereq", "/servicereq", }, -- 41
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route/", false, "v0", "/route/", "/service/route/", },
+ { "/service", "/route/", false, "v1", "/route/", "/serviceroute/", },
+ { "/service", "/route/", false, "v0", "/route/req", "/service/route/req", },
+ { "/service", "/route/", false, "v1", "/route/req", "/serviceroute/req", },
+ { "/service", "/route/", true, "v0", "/route/", "/service/", },
+ { "/service", "/route/", true, "v1", "/route/", "/service", },
+ { "/service", "/route/", true, "v0", "/route/req", "/service/req", },
+ { "/service", "/route/", true, "v1", "/route/req", "/servicereq", }, -- 49
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service/", "/", {false, true}, "v0", "/route/", "/service/route/", },
+ { "/service/", "/", {false, true}, "v1", "/route/", "/service/route/", },
+ { "/service/", "/", {false, true}, {"v0", "v1"}, "/", "/service/", },
+ { "/service/", "/", {false, true}, {"v0", "v1"}, "/route", "/service/route", },
+ { "/service/", "/", {false, true}, {"v0", "v1"}, "/routereq", "/service/routereq", },
+ { "/service/", "/", {false, true}, {"v0", "v1"}, "/route/req", "/service/route/req", }, -- 55
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service/", "/route", false, {"v0", "v1"}, "/route", "/service/route", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/route/", "/service/route/", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/routereq", "/service/routereq", },
+ { "/service/", "/route", true, "v0", "/route", "/service", },
+ { "/service/", "/route", true, "v1", "/route", "/service/", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/route/", "/service/", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/routereq", "/service/req", }, -- 62
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service/", "/route/", false, {"v0", "v1"}, "/route/", "/service/route/", },
+ { "/service/", "/route/", false, {"v0", "v1"}, "/route/req", "/service/route/req", },
+ { "/service/", "/route/", true, {"v0", "v1"}, "/route/", "/service/", },
+ { "/service/", "/route/", true, {"v0", "v1"}, "/route/req", "/service/req", }, -- 66
+ -- service_path route_path strip_path path_handling request_path expected_path
+ -- The following cases match on host (not paths)
+ { "/", nil, {false, true}, {"v0", "v1"}, "/", "/", },
+ { "/", nil, {false, true}, {"v0", "v1"}, "/route", "/route", },
+ { "/", nil, {false, true}, {"v0", "v1"}, "/route/", "/route/", }, -- 69
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", nil, {false, true}, {"v0", "v1"}, "/", "/service", },
+ { "/service", nil, {false, true}, "v0", "/route", "/service/route", },
+ { "/service", nil, {false, true}, "v1", "/route", "/serviceroute", },
+ { "/service", nil, {false, true}, "v0", "/route/", "/service/route/", },
+ { "/service", nil, {false, true}, "v1", "/route/", "/serviceroute/", }, -- 74
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service/", nil, {false, true}, {"v0", "v1"}, "/", "/service/", },
+ { "/service/", nil, {false, true}, {"v0", "v1"}, "/route", "/service/route", },
+ { "/service/", nil, {false, true}, {"v0", "v1"}, "/route/", "/service/route/", }, -- 77
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", true, "v0", "/route./req", "/service/req", },
+ { "/service", "/route", true, "v1", "/route./req", "/servicereq", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/route./req", "/service/req", }, -- 80
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", true, "v0", "/route%2E/req", "/service/req", },
+ { "/service", "/route", true, "v1", "/route%2E/req", "/servicereq", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/route%2E/req", "/service/req", },
+ { "/service", "/route", true, "v0", "/route%2e/req", "/service/req", },
+ { "/service", "/route", true, "v1", "/route%2e/req", "/servicereq", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/route%2e/req", "/service/req", }, -- 86
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", true, "v0", "/route../req", "/service/req", },
+ { "/service", "/route", true, "v1", "/route../req", "/servicereq", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/route../req", "/service/req", }, -- 89
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", true, "v0", "/route%2E%2E/req", "/service/req", },
+ { "/service", "/route", true, "v1", "/route%2E%2E/req", "/servicereq", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/route%2E%2E/req", "/service/req", },
+ { "/service", "/route", true, "v0", "/route%2e%2E/req", "/service/req", },
+ { "/service", "/route", true, "v1", "/route%2e%2E/req", "/servicereq", },
+ { "/service/", "/route", true, {"v0", "v1"}, "/route%2e%2E/req", "/service/req", }, -- 95
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", false, "v0", "/route./req", "/service/route./req", },
+ { "/service", "/route", false, "v1", "/route./req", "/serviceroute./req", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/route./req", "/service/route./req", }, -- 98
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", false, "v0", "/route%2E/req", "/service/route./req", },
+ { "/service", "/route", false, "v1", "/route%2E/req", "/serviceroute./req", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/route%2E/req", "/service/route./req", },
+ { "/service", "/route", false, "v0", "/route%2e/req", "/service/route./req", },
+ { "/service", "/route", false, "v1", "/route%2e/req", "/serviceroute./req", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/route%2e/req", "/service/route./req", }, -- 104
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", false, "v0", "/route../req", "/service/route../req", },
+ { "/service", "/route", false, "v1", "/route../req", "/serviceroute../req", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/route../req", "/service/route../req", }, -- 107
+ -- service_path route_path strip_path path_handling request_path expected_path
+ { "/service", "/route", false, "v0", "/route%2E%2E/req", "/service/route../req", },
+ { "/service", "/route", false, "v1", "/route%2E%2E/req", "/serviceroute../req", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/route%2E%2E/req", "/service/route../req", },
+ { "/service", "/route", false, "v0", "/route%2e%2E/req", "/service/route../req", },
+ { "/service", "/route", false, "v1", "/route%2e%2E/req", "/serviceroute../req", },
+ { "/service/", "/route", false, {"v0", "v1"}, "/route%2e%2E/req", "/service/route../req", }, -- 113
+}
+
+
+local function expand(root_test)
+ local expanded_tests = { root_test }
+
+ for _, field_name in ipairs({ "strip_path", "path_handling" }) do
+ local new_tests = {}
+ for _, test in ipairs(expanded_tests) do
+ if type(test[field_name]) == "table" then
+ for _, field_value in ipairs(test[field_name]) do
+ local et = cycle_aware_deep_copy(test)
+ et[field_name] = field_value
+ new_tests[#new_tests + 1] = et
+ end
+
+ else
+ new_tests[#new_tests + 1] = test
+ end
+ end
+ expanded_tests = new_tests
+ end
+
+ return expanded_tests
+end
+
+
+local tests_mt = {
+ __index = {
+ expand = expand
+ }
+}
+
+
+local parsed_tests = {}
+for i = 1, #tests do
+ local test = tests[i]
+ parsed_tests[i] = setmetatable({
+ service_path = test[1],
+ route_path = test[2],
+ strip_path = test[3],
+ path_handling = test[4],
+ request_path = test[5],
+ expected_path = test[6],
+ }, tests_mt)
+end
+
+return parsed_tests
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/.gitignore b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/.gitignore
new file mode 100644
index 00000000..4808264d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/.gitignore
@@ -0,0 +1,244 @@
+
+# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### OSX ###
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### PyCharm ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+
+# Sensitive or high-churn files:
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.xml
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+
+# Gradle:
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# CMake
+cmake-build-debug/
+
+# Mongo Explorer plugin:
+.idea/**/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Ruby plugin and RubyMine
+/.rakeTasks
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+### PyCharm Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+.idea/sonarlint
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+.pytest_cache/
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule.*
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# Build folder
+
+*/build/*
+
+# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/README.md b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/README.md
new file mode 100644
index 00000000..03499479
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/README.md
@@ -0,0 +1,130 @@
+# sam-app
+
+This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders.
+
+- hello_world - Code for the application's Lambda function.
+- events - Invocation events that you can use to invoke the function.
+- tests - Unit tests for the application code.
+- template.yaml - A template that defines the application's AWS resources.
+
+The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code.
+
+If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit.
+The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started.
+
+* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html)
+* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html)
+* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html)
+
+## Deploy the sample application
+
+The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API.
+
+To use the SAM CLI, you need the following tools.
+
+* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)
+* [Python 3 installed](https://www.python.org/downloads/)
+* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community)
+
+To build and deploy your application for the first time, run the following in your shell:
+
+```bash
+sam build --use-container
+sam deploy --guided
+```
+
+The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts:
+
+* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name.
+* **AWS Region**: The AWS region you want to deploy your app to.
+* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes.
+* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command.
+* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application.
+
+You can find your API Gateway Endpoint URL in the output values displayed after deployment.
+
+## Use the SAM CLI to build and test locally
+
+Build your application with the `sam build --use-container` command.
+
+```bash
+sam-app$ sam build --use-container
+```
+
+The SAM CLI installs dependencies defined in `hello_world/requirements.txt`, creates a deployment package, and saves it in the `.aws-sam/build` folder.
+
+Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project.
+
+Run functions locally and invoke them with the `sam local invoke` command.
+
+```bash
+sam-app$ sam local invoke HelloWorldFunction --event events/event.json
+```
+
+The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000.
+
+```bash
+sam-app$ sam local start-api
+sam-app$ curl http://localhost:3000/
+```
+
+The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path.
+
+```yaml
+ Events:
+ HelloWorld:
+ Type: Api
+ Properties:
+ Path: /hello
+ Method: get
+```
+
+## Add a resource to your application
+The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types.
+
+## Fetch, tail, and filter Lambda function logs
+
+To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug.
+
+`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM.
+
+```bash
+sam-app$ sam logs -n HelloWorldFunction --stack-name sam-app --tail
+```
+
+You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html).
+
+## Tests
+
+Tests are defined in the `tests` folder in this project. Use PIP to install the test dependencies and run tests.
+
+```bash
+sam-app$ pip install -r tests/requirements.txt --user
+# unit test
+sam-app$ python -m pytest tests/unit -v
+# integration test, requiring deploying the stack first.
+# Create the env variable AWS_SAM_STACK_NAME with the name of the stack we are testing
+sam-app$ AWS_SAM_STACK_NAME= python -m pytest tests/integration -v
+```
+
+## Cleanup
+
+To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following:
+
+```bash
+aws cloudformation delete-stack --stack-name sam-app
+```
+
+## Resources
+
+See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts.
+
+Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/)
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/__init__.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/events/event.json b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/events/event.json
new file mode 100644
index 00000000..a6197dea
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/events/event.json
@@ -0,0 +1,62 @@
+{
+ "body": "{\"message\": \"hello world\"}",
+ "resource": "/hello",
+ "path": "/hello",
+ "httpMethod": "GET",
+ "isBase64Encoded": false,
+ "queryStringParameters": {
+ "foo": "bar"
+ },
+ "pathParameters": {
+ "proxy": "/path/to/resource"
+ },
+ "stageVariables": {
+ "baz": "qux"
+ },
+ "headers": {
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+ "Accept-Encoding": "gzip, deflate, sdch",
+ "Accept-Language": "en-US,en;q=0.8",
+ "Cache-Control": "max-age=0",
+ "CloudFront-Forwarded-Proto": "https",
+ "CloudFront-Is-Desktop-Viewer": "true",
+ "CloudFront-Is-Mobile-Viewer": "false",
+ "CloudFront-Is-SmartTV-Viewer": "false",
+ "CloudFront-Is-Tablet-Viewer": "false",
+ "CloudFront-Viewer-Country": "US",
+ "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
+ "Upgrade-Insecure-Requests": "1",
+ "User-Agent": "Custom User Agent String",
+ "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
+ "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
+ "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
+ "X-Forwarded-Port": "443",
+ "X-Forwarded-Proto": "https"
+ },
+ "requestContext": {
+ "accountId": "123456789012",
+ "resourceId": "123456",
+ "stage": "prod",
+ "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
+ "requestTime": "09/Apr/2015:12:34:56 +0000",
+ "requestTimeEpoch": 1428582896000,
+ "identity": {
+ "cognitoIdentityPoolId": null,
+ "accountId": null,
+ "cognitoIdentityId": null,
+ "caller": null,
+ "accessKey": null,
+ "sourceIp": "127.0.0.1",
+ "cognitoAuthenticationType": null,
+ "cognitoAuthenticationProvider": null,
+ "userArn": null,
+ "userAgent": "Custom User Agent String",
+ "user": null
+ },
+ "path": "/prod/hello",
+ "resourcePath": "/hello",
+ "httpMethod": "POST",
+ "apiId": "1234567890",
+ "protocol": "HTTP/1.1"
+ }
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/__init__.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/app.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/app.py
new file mode 100644
index 00000000..f416e2d9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/app.py
@@ -0,0 +1,42 @@
+import json
+
+# import requests
+
+
+def lambda_handler(event, context):
+ """Sample pure Lambda function
+
+ Parameters
+ ----------
+ event: dict, required
+ API Gateway Lambda Proxy Input Format
+
+ Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
+
+ context: object, required
+ Lambda Context runtime methods and attributes
+
+ Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html
+
+ Returns
+ ------
+ API Gateway Lambda Proxy Output Format: dict
+
+ Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
+ """
+
+ # try:
+ # ip = requests.get("http://checkip.amazonaws.com/")
+ # except requests.RequestException as e:
+ # # Send some context about this error to Lambda Logs
+ # print(e)
+
+ # raise e
+
+ return {
+ "statusCode": 201,
+ "body": json.dumps({
+ "message": "hello world",
+ # "location": ip.text.replace("\n", "")
+ }),
+ }
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/requirements.txt b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/requirements.txt
new file mode 100644
index 00000000..663bd1f6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/hello_world/requirements.txt
@@ -0,0 +1 @@
+requests
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/template.yaml b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/template.yaml
new file mode 100644
index 00000000..5c2605ec
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/template.yaml
@@ -0,0 +1,41 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: >
+ sam-app
+
+ Sample SAM Template for sam-app
+
+# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
+Globals:
+ Function:
+ Timeout: 3
+
+Resources:
+ HelloWorldFunction:
+ Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
+ Properties:
+ CodeUri: hello_world/
+ Handler: app.lambda_handler
+ Runtime: python3.9
+ Architectures:
+ - x86_64
+ Events:
+ HelloWorld:
+ Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
+ Properties:
+ Path: /hello
+ Method: get
+
+Outputs:
+ # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
+ # Find out more about other implicit resources you can reference within SAM
+ # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
+ HelloWorldApi:
+ Description: "API Gateway endpoint URL for Prod stage for Hello World function"
+ Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
+ HelloWorldFunction:
+ Description: "Hello World Lambda Function ARN"
+ Value: !GetAtt HelloWorldFunction.Arn
+ HelloWorldFunctionIamRole:
+ Description: "Implicit IAM Role created for Hello World function"
+ Value: !GetAtt HelloWorldFunctionRole.Arn
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/__init__.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/integration/__init__.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/integration/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/integration/test_api_gateway.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/integration/test_api_gateway.py
new file mode 100644
index 00000000..b96e8033
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/integration/test_api_gateway.py
@@ -0,0 +1,45 @@
+import os
+
+import boto3
+import pytest
+import requests
+
+"""
+Make sure env variable AWS_SAM_STACK_NAME exists with the name of the stack we are going to test.
+"""
+
+
+class TestApiGateway:
+
+ @pytest.fixture()
+ def api_gateway_url(self):
+ """ Get the API Gateway URL from Cloudformation Stack outputs """
+ stack_name = os.environ.get("AWS_SAM_STACK_NAME")
+
+ if stack_name is None:
+ raise ValueError('Please set the AWS_SAM_STACK_NAME environment variable to the name of your stack')
+
+ client = boto3.client("cloudformation")
+
+ try:
+ response = client.describe_stacks(StackName=stack_name)
+ except Exception as e:
+ raise Exception(
+ f"Cannot find stack {stack_name} \n" f'Please make sure a stack with the name "{stack_name}" exists'
+ ) from e
+
+ stacks = response["Stacks"]
+ stack_outputs = stacks[0]["Outputs"]
+ api_outputs = [output for output in stack_outputs if output["OutputKey"] == "HelloWorldApi"]
+
+ if not api_outputs:
+ raise KeyError(f"HelloWorldAPI not found in stack {stack_name}")
+
+ return api_outputs[0]["OutputValue"] # Extract url from stack outputs
+
+ def test_api_gateway(self, api_gateway_url):
+ """ Call the API Gateway endpoint and check the response """
+ response = requests.get(api_gateway_url)
+
+ assert response.status_code == 200
+ assert response.json() == {"message": "hello world"}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/requirements.txt b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/requirements.txt
new file mode 100644
index 00000000..b9cf27ab
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/requirements.txt
@@ -0,0 +1,3 @@
+pytest
+boto3
+requests
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/unit/__init__.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/unit/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/unit/test_handler.py b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/unit/test_handler.py
new file mode 100644
index 00000000..d98ce574
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/sam-app/tests/unit/test_handler.py
@@ -0,0 +1,72 @@
+import json
+
+import pytest
+
+from hello_world import app
+
+
+@pytest.fixture()
+def apigw_event():
+ """ Generates API GW Event"""
+
+ return {
+ "body": '{ "test": "body"}',
+ "resource": "/{proxy+}",
+ "requestContext": {
+ "resourceId": "123456",
+ "apiId": "1234567890",
+ "resourcePath": "/{proxy+}",
+ "httpMethod": "POST",
+ "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
+ "accountId": "123456789012",
+ "identity": {
+ "apiKey": "",
+ "userArn": "",
+ "cognitoAuthenticationType": "",
+ "caller": "",
+ "userAgent": "Custom User Agent String",
+ "user": "",
+ "cognitoIdentityPoolId": "",
+ "cognitoIdentityId": "",
+ "cognitoAuthenticationProvider": "",
+ "sourceIp": "127.0.0.1",
+ "accountId": "",
+ },
+ "stage": "prod",
+ },
+ "queryStringParameters": {"foo": "bar"},
+ "headers": {
+ "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
+ "Accept-Language": "en-US,en;q=0.8",
+ "CloudFront-Is-Desktop-Viewer": "true",
+ "CloudFront-Is-SmartTV-Viewer": "false",
+ "CloudFront-Is-Mobile-Viewer": "false",
+ "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
+ "CloudFront-Viewer-Country": "US",
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+ "Upgrade-Insecure-Requests": "1",
+ "X-Forwarded-Port": "443",
+ "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
+ "X-Forwarded-Proto": "https",
+ "X-Amz-Cf-Id": "aaaaaaaaaae3VYQb9jd-nvCd-de396Uhbp027Y2JvkCPNLmGJHqlaA==",
+ "CloudFront-Is-Tablet-Viewer": "false",
+ "Cache-Control": "max-age=0",
+ "User-Agent": "Custom User Agent String",
+ "CloudFront-Forwarded-Proto": "https",
+ "Accept-Encoding": "gzip, deflate, sdch",
+ },
+ "pathParameters": {"proxy": "/examplepath"},
+ "httpMethod": "POST",
+ "stageVariables": {"baz": "qux"},
+ "path": "/examplepath",
+ }
+
+
+def test_lambda_handler(apigw_event):
+
+ ret = app.lambda_handler(apigw_event, "")
+ data = json.loads(ret["body"])
+
+ assert ret["statusCode"] == 200
+ assert "message" in ret["body"]
+ assert data["message"] == "hello world"
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/shared_dict.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/shared_dict.lua
new file mode 100644
index 00000000..a834e8a3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/shared_dict.lua
@@ -0,0 +1,68 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--- generate resty `--shdict` options executed by bin/busted
+
+local dicts = {
+ -- http shared dicts
+ "kong 5m",
+ "kong_locks 8m",
+ "kong_healthchecks 5m",
+ "kong_cluster_events 5m",
+ "kong_rate_limiting_counters 12m",
+ "kong_core_db_cache 16m",
+ "kong_core_db_cache_miss 16m",
+ "kong_db_cache 16m",
+ "kong_db_cache_2 16m",
+ "kong_db_cache_miss 12m",
+ "kong_db_cache_miss_2 12m",
+ "kong_mock_upstream_loggers 10m",
+ "kong_secrets 5m",
+ "test_vault 5m",
+ "prometheus_metrics 5m",
+ "lmdb_mlcache 5m",
+ "kong_test_cp_mock 1m",
+
+ --- XXX EE http shared dicts
+ "kong_counters 1m",
+ "kong_vitals 1m",
+ "kong_vitals_lists 1m",
+ "kong_vitals_counters 50m",
+ "kong_reports_consumers 10m",
+ "kong_reports_routes 1m",
+ "kong_reports_services 1m",
+ "kong_reports_workspaces 1m",
+ "kong_keyring 5m",
+
+ -- stream shared dicts
+ "stream_kong 5m",
+ "stream_kong_locks 8m",
+ "stream_kong_healthchecks 5m",
+ "stream_kong_cluster_events 5m",
+ "stream_kong_rate_limiting_counters 12m",
+ "stream_kong_core_db_cache 16m",
+ "stream_kong_core_db_cache_miss 16m",
+ "stream_kong_db_cache 16m",
+ "stream_kong_db_cache_2 16m",
+ "stream_kong_db_cache_miss 12m",
+ "stream_kong_db_cache_miss_2 12m",
+ "stream_kong_secrets 5m",
+ "stream_prometheus_metrics 5m",
+
+ --- XXX EE stream shared dicts
+ "stream_kong_counters 50m",
+ "stream_kong_vitals 1m",
+ "stream_kong_vitals_lists 1m",
+ "stream_kong_vitals_counters 50m",
+ "stream_kong_keyring 5m",
+}
+
+for i, v in ipairs(dicts) do
+ dicts[i] = " --shdict '" .. v .. "' "
+end
+
+return table.concat(dicts, " ")
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/shm-stub.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/shm-stub.lua
new file mode 100644
index 00000000..7295a44c
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/shm-stub.lua
@@ -0,0 +1,118 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+-- DICT Proxy
+-- https://github.com/bsm/fakengx/blob/master/fakengx.lua
+
+local SharedDict = {}
+
+local function set(data, key, value)
+ data[key] = {
+ value = value,
+ info = {expired = false}
+ }
+end
+
+function SharedDict:new()
+ return setmetatable({data = {}}, {__index = self})
+end
+
+function SharedDict:get(key)
+ return self.data[key] and self.data[key].value, nil
+end
+
+function SharedDict:set(key, value)
+ set(self.data, key, value)
+ return true, nil, false
+end
+
+SharedDict.safe_set = SharedDict.set
+
+function SharedDict:add(key, value)
+ if self.data[key] ~= nil then
+ return false, "exists", false
+ end
+
+ set(self.data, key, value)
+ return true, nil, false
+end
+
+function SharedDict:replace(key, value)
+ if self.data[key] == nil then
+ return false, "not found", false
+ end
+
+ set(self.data, key, value)
+ return true, nil, false
+end
+
+function SharedDict:delete(key)
+ if self.data[key] ~= nil then
+ self.data[key] = nil
+ end
+end
+
+function SharedDict:incr(key, value, init)
+ if not self.data[key] then
+ if not init then
+ return nil, "not found"
+ else
+ set(self.data, key, init)
+ end
+ elseif type(self.data[key]) ~= "table" then
+ return nil, "not a table"
+ end
+
+ self.data[key].value = self.data[key].value + value
+ return self.data[key].value, nil
+end
+
+function SharedDict:flush_all()
+ for _, item in pairs(self.data) do
+ item.info.expired = true
+ end
+end
+
+function SharedDict:flush_expired(n)
+ local data = self.data
+ local flushed = 0
+
+ for key, item in pairs(self.data) do
+ if item.info.expired then
+ data[key] = nil
+ flushed = flushed + 1
+ if n and flushed == n then
+ break
+ end
+ end
+ end
+
+ self.data = data
+
+ return flushed
+end
+
+function SharedDict:get_keys()
+ local keys = {}
+ for k, _ in pairs(self.data) do
+ table.insert(keys, k)
+ end
+
+ return keys
+end
+
+local shared_mt = {
+ __index = function(self, key)
+ if rawget(self, key) == nil then
+ self[key] = SharedDict:new()
+ end
+ return self[key]
+ end
+}
+
+return setmetatable({}, shared_mt)
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/ssl.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/ssl.lua
new file mode 100644
index 00000000..beaaec61
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/ssl.lua
@@ -0,0 +1,747 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return {
+ --[[
+ Version: 1 (0x0)
+ Issuer: C = US, ST = California, L = San Francisco, O = Kong, OU = Core, CN = ssl-example.com
+ Validity
+ Not Before: Apr 24 14:36:29 2020 GMT
+ Not After : Feb 7 14:36:29 2294 GMT
+
+ Note: Version 1 was accomplished by using a openssl.cnf file
+ with the x509_extensions line commented out.
+ See https://stackoverflow.com/questions/26788244/how-to-create-a-legacy-v1-or-v2-x-509-cert-for-testing
+ and this line's commit message for more info
+ --]]
+ cert = [[-----BEGIN CERTIFICATE-----
+MIIFbTCCA1UCFGjFyapVZYpvpKuYDJbLA1YJip++MA0GCSqGSIb3DQEBCwUAMHIx
+CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4g
+RnJhbmNpc2NvMQ0wCwYDVQQKEwRLb25nMQ0wCwYDVQQLEwRDb3JlMRgwFgYDVQQD
+Ew9zc2wtZXhhbXBsZS5jb20wIBcNMjAwNDI0MTQzNjI5WhgPMjI5NDAyMDcxNDM2
+MjlaMHIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
+Ew1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKEwRLb25nMQ0wCwYDVQQLEwRDb3JlMRgw
+FgYDVQQDEw9zc2wtZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDDd3IZRGgdL34RY1MiAE2ItnJNbZGPQD9XE32D/I1cDCAXjI/MQrvn
+iSJhnnc/8F2y7/tzJ2GZIk0C8Fz7MRWDXHCBt9byOHU0Jhppj/cRyWqlI7GrvyZy
+QAiyOh/97ZN52xwXqiFDRXCd43PIgPhie6rcJkdYbppg8ETwMbjZcslcX6EUJzLU
+q/I0BscqbqfiAa8viJ0a1bSWINABKWYr5KqpiRfLWsTJmMthWgh5kaLtMZS3m/AM
+IrrKJJY2klvyO6TeXkHuZwm1day4NxhInxZl5H8NNbLgHz+W/8g6vgo2Oafy0crA
+hS7iK0oJZMgK25p7VDbi8BXf9kqZeq17Vcj9uB8JxC4oTjy6XT9HGTAJ3yJ0TtLP
+p8dayJt6fbNgcuHKkiZJAJfg6ecl2jXxYW7aO9oqcIA3btsl1TJbnzpMK8zeQK1H
+Aw7ryQ8qdhKboZUe09iwcyjxyzxieymS387/O6Stnu3UXoPnig9exx8zOgkcZfFa
+yhrtEbS74lwcyWwnSmjq/tUw1+QbzoTEJDeMyFnZjOJT5GbauFadF7o/GSbCurWb
+yh2G/s5S6v+xa7zw7xdO8ECEUKXws4IiGSui3raZWt+B57uBFQqUefDV6H6MevPk
+8aMTC3f9fhp28lbbjfZBI3JiwtaVSV11kJfwmzOcj198nmZlRHotMQIDAQABMA0G
+CSqGSIb3DQEBCwUAA4ICAQC51hH6cZrn+n8LiHlDaT/JFys3kKOQ4OpdpCUyUYzI
+VYFnG/espH8LKzAiui4/LQjwygTkmNdp12GzIUsZItvpia2J4hsi7xNm/uKOhHwG
+B1FViDF8FKOEihyMsZVAHIBj54RjuQ+WLbuQCjajX4PrK2La6lhWMn4cyvFWXCYB
+A28Vrz/jXgXCXEct4+b2gZApOJ2H8qAyJv8JtFOptbB5mUZz3u3PW8/bTwG901/L
+P9rWLq4AXT+UyPwBNs/lG4XXGc5uBfQjHkvamNKQP3usZuxAygdOEz6vJh9i0nyX
+2b/+F/GLi8ZZwllapmp8c3WdsJkycBJ22VLS/LFNNvkz4sbT1dw5w1A7XJhiVDDZ
+Dt9HMqK5qb4GAbaWwS+HPC63vrP6Ltw4QiAhC5x3bRujJ9CscRTVHXxMNw9b1TkQ
+8AGgEFZKtbhirmv2/MQv+T57LQgnFPWNJWwv3YjJOIzDLEOeOxHMFV3Po5R5B2eP
+qhLqmwYS6tQ/ih5BnlbZPBrArdVvsVCWLjQRy9qgetBlh+c65cL4HUAe/BxpXQSK
+OoNpTQYMpSXlERwqm2/LN8rJl3XFlGtSH2xHucX8V3eN1bPURegkfplgPI+HDZDp
+LAhXzHSQgW+cvcEL9Jafm5e5kRqDei4VSJteBfo+X/eTp0WnGJOYv0uJqwUJheNe
+IQ==
+-----END CERTIFICATE-----]],
+ key = [[-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAw3dyGURoHS9+EWNTIgBNiLZyTW2Rj0A/VxN9g/yNXAwgF4yP
+zEK754kiYZ53P/Bdsu/7cydhmSJNAvBc+zEVg1xwgbfW8jh1NCYaaY/3EclqpSOx
+q78mckAIsjof/e2TedscF6ohQ0VwneNzyID4Ynuq3CZHWG6aYPBE8DG42XLJXF+h
+FCcy1KvyNAbHKm6n4gGvL4idGtW0liDQASlmK+SqqYkXy1rEyZjLYVoIeZGi7TGU
+t5vwDCK6yiSWNpJb8juk3l5B7mcJtXWsuDcYSJ8WZeR/DTWy4B8/lv/IOr4KNjmn
+8tHKwIUu4itKCWTICtuae1Q24vAV3/ZKmXqte1XI/bgfCcQuKE48ul0/RxkwCd8i
+dE7Sz6fHWsiben2zYHLhypImSQCX4OnnJdo18WFu2jvaKnCAN27bJdUyW586TCvM
+3kCtRwMO68kPKnYSm6GVHtPYsHMo8cs8Ynspkt/O/zukrZ7t1F6D54oPXscfMzoJ
+HGXxWsoa7RG0u+JcHMlsJ0po6v7VMNfkG86ExCQ3jMhZ2YziU+Rm2rhWnRe6Pxkm
+wrq1m8odhv7OUur/sWu88O8XTvBAhFCl8LOCIhkrot62mVrfgee7gRUKlHnw1eh+
+jHrz5PGjEwt3/X4advJW2432QSNyYsLWlUlddZCX8JsznI9ffJ5mZUR6LTECAwEA
+AQKCAgEAlp18ctHOM+b7imRxk3qOQ/DpxJXQGoTUCcG/7qr1Nd81/09VInytk6yM
+rJHRq5gIazAWHlZTEw9mLgSOcRQSUqUxIBNLcltknGlb4smHBNKh9Vu6tO9WraR/
+zu2Q5zZgc/4M+IMknFRugYrZFb+jJSfLsVVhllerZ1TcmgSGPi//zsj1MrU9qrhP
+qh0q7JxVioXnuoXXIO1Y+HGSNcLzspDBnF9B0XVAu2KcHIimjR5WX9Tbllt3LbK5
+Ibftc2F1rgKdeKdCwHPu/D8PduclNBg5xwu5hrFBAwexFSZE4Fa9QalNq4JSa+R0
+Ctx2cSSSLCOpNqzemiGLiYabVwY7k7tsYjgO/+t9wRYEIZ3SNm2sydK1+6Hebu2Q
+Ibagh78ysEW+3kM2Tto8njUgoYB4i8VJENfTyVVLLYOIbdRifZGCxXkkBjyF9H0L
+S3mfytKHH8Bjd0jpf0U6QiaY/5XXBljQ2yQFAk2Nk03eP3mncJoaiNhHeq9WiNir
+NL+uJo/F9FAxG3q4W1j0EwWszZdAhHZYRELuV7EX1tXgLM6tOPKa28i9MFVxK172
+hyiSQqWLhtvoU3exf5WZ4gWHPk6b7QKhyaeRjUkcClXIkbYSOL2FnrZRVDC4UHYD
+rEqpBzIGFF6tfchQ5r2/IbSvRldMW+btaehHK1K46onYFhmu61ECggEBAOzBBbxu
+92OQQO7G4KcRflJZ4gRve2BOHsqsDS90jfY42tESJEp04LfN5iBxr2PRIz/l0pbX
+14vHSxuaqZqAKcxn/cDIXbhMCPTvzCmF1y6pbG2hh+ki5531rZqUN3RKamRMPS4+
+9xbRo6wynIWMC0EX66Odw0cZKxW89U1avLR6Izy19aCO+/nMZ+qZztle5xprj1rG
+4eYyYfY4/R/ZdvHdcfR3Rp7MiCBxeQZo1nyLXgqoGvsppntKAD8p49VNrqqA92hO
+uCJ4+sUAU9VPzxk8SOACi56lSm3BkvnErIbG0b9WM5Kj5O01HoX3N4n6s7fefZ6X
+uCc3/3ZW43ZfUHUCggEBANNbNc1l3axheTnZbPPrhp8bQ67MjQZqSQ1pqNf/CoA8
+vmrZEDml2UnuqijI6DWWEtPP2+VoWt4CA8xLX11x+w27Ov9ho/CUnuvg+ufn6w4t
+czyiYmQK2nIBJfZAYuCYN0VOwZoOseu1mfIBvLHLwGgWe6lWMuC3wFgHDPz1AgYH
+jH7uUGKNCRXBkzUqSTS6VSX/056fY6xEGU0EgDfKLjlucDkdhqukDDHjOCCCLtCT
+tApc+LVnB0OyMojxnaArW7rgSEmz5CWm9TGCS6WdZ6aZtlgUNbGVp8znrabM3JuV
+k3ZtJ4TTb+nS32NPEgnFTOdvKbR4jOJTZMn/51iz0k0CggEANAGCfQ3zEc/SM6FE
+H+7bzMMpvseuVk1Svjpk+xOjS37ZsitGBYT/B+EWt/HBETATiim1xKTNGEtC2GF0
++BdHFzuQphRdYepx3Tv/oO9hgUJ+Kubcr/2W2z/oTphYRhdCn52PouT56e1LArxr
+XXqRzk6u2FYDW07QBApp6AASi6J4sxFVT1uZRhn8ibAXm/AY29jcuJgPbz2J/0gt
+ch2W3zJBoe3BeYh8LoQ+jYXh96G1mqmqo5lWlKaAv184SNazk4iY0DTahdgFXdjg
+kW7ikyyG5Y4plUPBwbJP0sW9EC4ETP9mfMO7uc99UPgpHwoPCEi7V2cEcLkASMJ1
+XL87MQKCAQAP8pTnr6T/SceM3uQCr6XTwYnk2ZhWgJSMh4lu6taPAIZZp5E62FHB
+61k5hJdI288F3mw4LhyVHc/NjW5fracEzTjRZjupfn1TTQGBmBU7V6CXVaVY6Ry8
+d2u23frX+YRKHXwsNKmmIGjCQvFK9RXKhM/F4jQqkpLX0YhgWdhSPiWSukakeQHT
+e2yxGUS2zsx39oqwMFSj/etuzUUjcIT2XmfnV1v+/XzSEtP8V/ZSVKLEGWnGvkRl
+DkAT5y8+k6bzPdMWhJVVZfigSLWOhIb6oiSJFtVKVgF4S47lxBVtQ+cBi5Dsy8to
++DlU0/WHeTSpTdD2xhXTSfmT6FQ7dC4NAoIBAF+V7Ntyb2mSHhJkSA4Sw/pKdz+I
+qL4jTdwm9Vjh9Vi4Gqcy1AoE/nzxIj0c+qaZzp+3L3ssAApKOS+BY3Y7Iqr82+X2
+1P3oTnJYZjG8mxyxeMguT824CjgWZvkd6Q0jSz8H4nLaeYlpG2jIov10voBJd5Zw
+NqY0sKkTzDVvl/l1k+Dp+hxUzAFfxIgU3x3qs/fPw9ahoI0oBH/+Xmg6vGg9T+te
+N7D4w5wpK/SHbjZBvSNp5kNlnqfDPjmfAwlGW1J6CBDcWWaEHWIcc4n3l2bCg3At
+LTLKB76sourXcEDVvZA6xrYv9GJukUqmc5SlHhJZQOhu48ITKXH18U7iuy8=
+-----END RSA PRIVATE KEY-----]],
+
+ key_encrypted = {
+ -- openssl pkcs8 -v1 PBE-SHA1-3DES -topk8 -in 1.crt -passout pass:foobar
+ standard = [[-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIJajAcBgoqhkiG9w0BDAEDMA4ECG8hq5qM1tTYAgIIAASCCUh/UgGOrRj5QQT6
+O1SyC87rmUohZPwutRqyPamsCiMH8PT8dfUrQfv4zjxWe/laxQIMxmdH1+o4bSTG
+HbnexX0QO+ZNVmEuq1AxBtK1VU1SQJ3smPun9foTb83haIDFcFdKtrDup0cr9tri
+HCEnVelGlYdUXwz8HDH54IApdeF/z4U5W0IFON+T1zqDtWkfuBdz8ZGTLjoTdNmy
+loI7s0ydi8m4BONkSACTkuHH78vdC9yf1u1nAcVKYHy6jVjdVNT8lCrSKT4L4HGJ
+WLLROFQimvT0QxJCH2T2jPPEPZa1MNLowilzctNt/8XkJNnh51Jb7AFDLzwTh+oB
+eA/93IS6ShejqBo08XdVf2XRM837zufOYktqXjBvF6XGCHuL++vcD+mvJI5omVJQ
+KpeCwPSjJ0xpup9edmTsPlmeCPzqYtgOWHqzaiH3lb6f6IC1MieJX88UxJol0sCs
+mNesymP0IYcEMEtwwTIU7Hwig4vUsd93M0sSRZVc/qHe5lM8rT0tjWkl9hyQxPUA
+VvDOXInuLmTJB/2nyz2r5/2uioVJ6o4yYML7mVo7IW/UJTyFQwYRnyYNbvNJ5k+0
+vZEob1FIF91o1Uiv9cmJj4cCudIEcki8XpdFK5Zviyrw2gMXGrdinS72f8WptDOX
+dbioJfWl5FIdEUZoRUkiBZfS2Zzy5hdWiyVm4zTSXvzPDa0a+GpbZrXPbAmwq+vq
+e/HOZWicZu3rVyNLU1dytuLHQ9LB6nbSATcSQOCJqe+fVKUkYkv8O3F1FOmOebOy
+v3sY9TmFAZYQtNd4D8ObP6qDMvCMMAD+/JGcpOdo48rQI2CWnwu4LfSqbFN1eCJu
+6E9oKITDZTfbCDKZDCJgWyquJqLLPJADcuB08Gl9w+Gao0E/1ED5kUlplE+ak+8R
+1yGLY6Jcdn2v0NoOVPEGTvhfnoKSrL6B6+Q9zuK0w7upcM3HjlXpDqnSeUf+neSq
+MftvIkvxZ3rXLhj4qs2PuvjPfVuceYBxndLcX+nDICt8nbsrYcYg2dpDGHwOuTkA
++b6k050NTBtYblv4xY8aspCpaSPyne3VDOFkxXvmu3EpxiVFJxCu3l6aDT/NpoU8
+UG+xDI03o/bOdyvc7J6bgXKSjr97u24eATuin9ifCPkAmQ8IdHsFupNZyC0WEKvS
+YSGG40aZ84to4GfbYfvT9MojE6Ejc5PCOpAvhh4LJJX5KF7gCb66B8Nno54k5qwU
+0niCIwoytq1SedHWvxsbVhSiAMnRkmLzuZZ/6KR8iDnSy0bhOkXw+9zJDyxPGcsJ
+q6u4GVe8ogpcT0f2sOfRPsVxAA7cIxDEUuIQbEXAb+3ROEE689wHTkDxkKxShDSj
+pw8xSH3TSf9hMbFjhTX8mIVSrSlo5sWcSalCGLGE+3jaG/o6vqsHFGsh7KVagWmh
+Ad+UzeCvC6NDX9h4KZyPNjcUganFNCWJZ6HHqkTUqLY3/qupwJN1wOe/62bU652+
+4r1JY2GQJQHjI1sFU/cJ61ylxSVSzUpQjFUxOJ4gTGhhjPAGzEpmvDFXIbDiW4cS
+3CRpottd3tTzc3RGSDck7aVKl7R38fuWzsFdDug7+Duac/jafqhVp7wL11643ztP
+/zskQnMwOQPtXb2Gopw0vSMHIcqXF/TTARddqYahICpygys60WslN4FfnRgoz+se
+X6ot2SZM25Z6EhHhYaNVLTWQuEzU+iXMqgVOZ+KIui2ue+DgCFBPdk9Gp4Q5kbWo
+mUw+/6kjoTCzbJBX/KLyCG4MDoTgpnxOznWfWxKvtTEKIokKRQYpJuXdl1pgEp1p
+DTE+lVmhl2dWYEMkbWv5omJine4QbOjt21lL2WZjsMiqy0kAjOIh7tz39zOrN43W
+RqviyRe0FvT8tGLba2EaGkYfWLuiKVNTSWOrdSNus4cK+Q78THZL+d/agJgXrLHw
+/COY6yKmOmjYEXkQl7Fb2JJrygO+PLdARzk27RwZtIhfJyD01ZabVuasMce8BhNh
+bU4ulJPu0bJ0xzDfo0ikHpVCGF6W0tl+zl3aILT02coJ11GpBi8Se69PlwdctHMS
+ZbGaaetCK1xXstzdcs9cELI8FQ1LS/n82KqZtnTXKy8g1O2JXoK+9xFiERftpiCi
+P3Fb8KYcMcKHisWCB8BfJ3Qodb125u0YkezqJgpqvtClRT862qb8kvamMoB8NzGT
+OZa8uwgwoqt4S32ewenaSLwzuvOziEkVLedfWgbKeAHxuiZhLi5S9qt3hFGORQTi
+KUtVlSM7v42IP8RuD3GKrZ1uV3x6RY2vTkGuT45n9UDn++Q2RFdrpuI/w7xYNzMA
+FZxVrpoZeMHiXl195fFYXgUffyY2ibLJiUb2+ItecFGSzLDL5pxo3oKqKpIZuJEL
+QGp+MMpoKcxPUGfv78NTG7hCmpLf99k52i0kYQJBcLGSmnLTM6X3nIl7PEUHTUFU
+VlLALjf5aPZcFOAidhHm+feOTNOPMm5MMInf1k2ibPzxQBOZxnf4Ux57/x/lL7PN
+XG0CDDHbiEJ117pd8tpVHbkY/FnqaqjV/VZHnpg7r69+iTH6ipnokUnv3TmTywsG
+0FlytnqlSc+qi9zpg3igwzpkJ2G3HityojgHH2eNNDAOyYqW788DdIC8X3FTnxcH
+iAuXslNWNOkjabn/vCGUHNj5zyfKydIeOsMfaG2H7rSYBRl4XsQCbnx7QtofO/WR
+n85YNSfvSwBHpAg6UPAB772mf43dSJnptWwar4uH/xqaAqeH/hgmMQyzl399HYfw
+5ug95OGRv8a1NuDHoVAI7UozOnY5OqQ2N+OEl993OiZJoFuX9SqIeM9Y67TWs40/
+/GmQfIaq4hVzSGuPavRmkhymOOjxcm0LFVatEXBe/iyGpwB4WiGarSFxeIeGrHTT
+3xi+D0OMdwyG5XHQQy2LJnmhFBOU53+CPQ0ecJrLPbHdzqgEmz2d3mRwZFvBSi56
+srwdhdiFQcEdIh+CessZXIGa1ZHKFphOd93fF7bfCQk0Z9+K+kwWIfUz72hu/Dr4
+xTcKT5qkvIHl7shuWK8QC8EpDubvViM14YGns6EtR4JttY1BLZxHyndEe0p9XlJQ
+s29ceQZQEs5DYKLB9YefjxANOh8EZTmJ+BKfA/xvanySnHNRi3NojxjdbJiPKBsd
+sSy16GdFKZJctce6KPU=
+-----END ENCRYPTED PRIVATE KEY-----]],
+ --openssl rsa -aes256 -in 1.crt -passout pass:foobar
+ legacy = [[-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,1D2DCF88CB6CDBE43D63D794511BABA6
+
+qaDyxn/sXGGouYjFTEu3vlq1lshJPR0mvBhVbuSPA04fkGFXOSg0Gykg4WFyEb84
+fuWhG7WPzNeWRsBEJQavn6R2faM4Axhe6xov4hY7u7++axCTUUe+25b9lNSsKHUL
+MakYwwUPbMFzcbEIQzyAgBqH0Uorvd9Fp8co1yYMZiVtOHI/vlTNwYXX0T8/bWxN
+62X85R3G0aFj+WkQUh4irahG+J6cg/EwKfz4TvEjVrO0sz544qd6iZDoct5juHuW
++rNSrpc3LRxmFTDn9IgjO6JvwK+ik5Hc1X3NMTPhfbnFPRiPuaAaxOX9dLYo1DOf
+ZlgwQH6gsZDfIDnxkNtyOOzgdtdnCpy8E/3robL3iZ7gqlNeLpX75D7OQR6jJe6O
+EmCjWYPtmp4Bcs71BpJGwDZOplpDEaxgJRnYE+Er3IgQ0o6eb2zpeDt6RSCk/FTu
+j045Mjrd/kf8jofLdzUNt/6ZvjliRV/FYl7I8lfLrycLev9aG52m81f7qenUCmMA
+GgaotKQK+X04ohq0Di0hKfglP5vW1aw5Z7I0qOKPC1Wr1S+7UNCWUrFZ7nXf6nAn
+SiwcaOoKWm8CikaVtWoPqD28OC6Kb3R5pIXTa7VBM4nIf9+Am/3M74wneZkUG00t
+tbVk5Rh+a/46SlBsVLaZ/A7/mChW7pobKlbnIob7xvDlWbjVe7iDH7jBZfTn6mmh
+mBdrpPO2LZ5wrWNr6m7AxhU9WqufVQqyaLQ8qPG6C6j3l7I4nTamwfde1/gXGeiO
+/BAhXvW/onOvLhGsTIj/rXjk/unix+UtbH7GxEb8RSupOn733f5qGoGPLocMtFud
+/WsUuHK0fBOs6LjuDEgL7Rrcm+U2N5wwEKbqNxhMuT4Di5gSPwRYbEFLMCqENlJ5
+6CsLdkDc1f4GWNsdguoZOBtYT5zuaD7xbl8Z3M7ZxI8JEJzhC0IhhFJX8CvumyeG
+rxVrcEVgQ1B22iZfDa1mwZBPdeJ3Ncknx6+RwpIFlfr8wQIABbVIIKa1SkHCxlib
+OxqrKO6WzH1x1KLRgUbiC3Ql+kzdXVqqc/2vg0imY1xRz+rWq/fxro5dweAlz2y9
++1ZIy58PsVFOYJ2VqP4v7Z2ydpIlP//ZJe+y2xypxMgxQ/kG41Oyerw+bB2wNNS+
+jkANfMnwHQMwQEGvCZJUGnP80utija5nDmCb5Js6G3qzkibnuN/iyvg0VPAVMB0p
+4vbQGUe+Y5tuBY1WqtYw9p+b5kGCFu+rLaHweqDcROGKRdWn99CiU//SNT5wmT50
+WCdv8YzTFWvmE0Nv9oAP00cjjsLGyhQNbj4lhfKEyKSmModfIJ5wUOEcEZUZEBj7
+8USqRpPQR2Ih5Z9e92pmi/OlmzEKuARZ6f2YMpFyuDei6IvPQD0BKV5Ca4Uvm8gh
+wLCb84Gw2N4NoziOjyDVOuGWhffpWIRFJ6wupnY8hly6t4rg/4NRKOBmnDl4SU1c
+I/qj+00Ow43blYFyGwvtuCt7GnquNJR1bPUnv32S0tMK6RS45wTVH/85MpmmF70Z
+ikh+LyRO0C/31EBWAc/KQms3KfUrXxkbvwfMrqnYL3M+Co8H+t95Fax96wAF0cd0
+JoOzSomL5HPRwbiLbXMufo5DKkfhdCNe+a6YRkxP9wmR2H1/H0c1Yjj1acnq8p11
+MxfJ2vHk1yV9MLXHsRgs3nCEqmqnJs/I5LeGEYn2Tw2A7jt7tpDkc96gEINeCCX6
+2V2BEiBWBOfWaKQzg2iALQzsPXMDgveKeaT3dAuANNJyrR80oRl8Q+HVL3REcucH
+i8A6UCuD2WmZKU6oWW3cY1ropRbNUXJLQ3w6WfPkrVDNbd+/iXbFyT5Q+losWEJk
+6fPV/vIm5MPtnfNsCEjDgwbF9WtBw6+UCAbBqKWLoPSCIshduSWmGnzgvJFMaEMO
+/6zb5p7qroN5OgpeHX07/xnUi0H+hxArAPmGovPktoDZXdBkVNxmT9a80hJphupE
+fNW9/GtvNTTXsPs9HVp3CogKWoa7hnbzQbYIOtx6NoghZr4oCS46cr+iPgT/v9Km
+sFmkTjrljtp8J9+Ywqm406gClhpMDRjj3D0z5H6nMZW6Z/aA7oECCDOos+CdRE9F
+guaNEgG8d6NrmzQVs1+mW0HTUim1UuxjC1Yn0LXZ+SkYfKx16tvbV+eil0O9xxX0
+dM9gq/cJpmMGl22Epy5TpbpPqUliqRQMkkdZzax+1AAgAU1X5S5KnJqZaAZ/tJnU
+Icv/halkNyY5e1VqXMY/pMWtVbkA9zHCPAEAkJ0LV4Y+HotIjri8maGPVa7F9nZn
+hto9G6UYfJ33Fe/+5xUPm6hBn3Z28eqephnnDLJfLIARl3t0/4VHka86L+nvZ/ka
+/luhMCRXlyzzYI6ItDShpNe0xXfd5L3GijXmblgrlaAVg7MUMNcH5f4+MEiKkBqu
+3KJaoQ+MbbWOgPtW2WzQTgXImD+h9JMxStcXym29sv98SziCpLgEwy2D85yLaSqz
+X9N5oo6rlij/eHfpiMKfIwLGryFwVylO1JuPYOievxZWgT3t2dRpHEisgdI0jjmk
+ZvgnuOq5gw7+yjGB/1slA2D3A+pztu9yfX3xOf1oTOyF3ntoFvoZtQGWHvzqxaNf
+lJ3NFOV7HHJqJ897YtdMRjMgL/mv4Wgzw8YQpzsom3u0cufUDeqDMAMXNMNEN0nL
+D9gQDTFKyy0Y3kfULmjUwHo4r0mwYMSocM4/cajJzQs68Qpy1yFLVVryjBfP6l45
+aqjwGyZFOgbdz3BdVuYq9uU3PHiV/Xi+9oaNzbhPKwiwe+tpaNM4NdJ7EdbVb3i5
+/CtEyKK147HdOAYGcbqDYFU7hcdLbA95tRX8zkz5NG684b1i5SCSbln+4iwZXWBZ
+loKPqZHrVD4qqSBFHBfvdaiUtNZIZH2bwUO5qt5goYD3egSrizNtNgzJ7nKl4PD1
+y7tnTMtP11OKqgrnls9s0LcuMNtv5Euq1o+U+vnMet6AZkC7+6g6UqPhoY6UTtB9
+X7w/9lDdh4lb64T24N6JxYlDo41gZ6r+4AA3bkr2LCDDx0mRpZzhZljXKhZvdRed
+tGJP909NhlRM6C3t8qZkFDiFvGBI+wK4Bf5bdazt/n3i4powjlgUEorwB1fDBYSK
+-----END RSA PRIVATE KEY-----]],
+ },
+ key_passphrase = "foobar",
+
+ -- Issuer: C = US, ST = California, O = Kong Testing, CN = Kong Testing Intermidiate CA
+ -- Validity
+ -- Not Before: May 2 20:03:11 2019 GMT
+ -- Not After : Apr 28 20:03:11 2029 GMT
+ -- Subject: C = US, ST = California, O = Kong Testing, CN = foo@example.com
+ -- X509v3 Key Usage: critical
+ -- Digital Signature, Non Repudiation, Key Encipherment
+ -- X509v3 Extended Key Usage:
+ -- TLS Web Client Authentication, E-mail Protection
+ -- X509v3 Subject Alternative Name:
+ -- email:foo@example.com, email:bar@example.com
+ -- Issuer: C = US, ST = California, O = Kong Testing, CN = Kong Testing Intermidiate CA
+ -- Validity
+ -- Not Before: May 2 20:03:11 2019 GMT
+ -- Not After : Apr 28 20:03:11 2029 GMT
+ -- Subject: C = US, ST = California, O = Kong Testing, CN = foo@example.com
+ -- X509v3 Key Usage: critical
+ -- Digital Signature, Non Repudiation, Key Encipherment
+ -- X509v3 Extended Key Usage:
+ -- TLS Web Client Authentication, E-mail Protection
+ -- X509v3 Subject Alternative Name:
+ -- email:foo@example.com, email:bar@example.com
+ --[[
+ Signature Algorithm: ecdsa-with-SHA256
+ Issuer: C = US, ST = California, L = San Francisco, O = Kong, OU = Core, CN = ssl-example.com
+ Validity
+ Not Before: Nov 18 16:17:30 2020 GMT
+ Not After : Sep 3 16:17:30 2294 GMT
+ --]]
+ cert_ecdsa = [[-----BEGIN CERTIFICATE-----
+MIICPDCCAeGgAwIBAgIUOIK1sCtPyUL5h7vHdxpN5PhpukMwCgYIKoZIzj0EAwIw
+cjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh
+biBGcmFuY2lzY28xDTALBgNVBAoMBEtvbmcxDTALBgNVBAsMBENvcmUxGDAWBgNV
+BAMMD3NzbC1leGFtcGxlLmNvbTAgFw0yMDExMTgxNjE3MzBaGA8yMjk0MDkwMzE2
+MTczMFowcjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV
+BAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEtvbmcxDTALBgNVBAsMBENvcmUx
+GDAWBgNVBAMMD3NzbC1leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH
+A0IABDFm7D+CfVzbkRyRTR/2DI4o1sOxDCdc1UEdbQkA5e6j5b4smyuW4xlZjVwV
+CXeADYvtpBaykzZ+NC5Zlf3EAkWjUzBRMB0GA1UdDgQWBBQcXSBVifOMnYaC632X
+NzdazHkuEjAfBgNVHSMEGDAWgBQcXSBVifOMnYaC632XNzdazHkuEjAPBgNVHRMB
+Af8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDbSwXZ15UJ0hX/7KTKxd/mER7b
+s5oBurNijw1iPMyi+wIhALixa/LN3i+AykB4Jxj89scpXilIH+6q5fJI9exuaLtv
+-----END CERTIFICATE-----]],
+ key_ecdsa = [[-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIDCpckzH9Z6YpE48cmSIqcNXDZ29peoeMkFP2NqZb/MUoAoGCCqGSM49
+AwEHoUQDQgAEMWbsP4J9XNuRHJFNH/YMjijWw7EMJ1zVQR1tCQDl7qPlviybK5bj
+GVmNXBUJd4ANi+2kFrKTNn40LlmV/cQCRQ==
+-----END EC PRIVATE KEY-----]],
+
+ --[[
+ Issuer: C = US, ST = California, O = Kong Testing, CN = Kong Testing Intermidiate CA
+ Validity
+ Not Before: May 2 20:03:11 2019 GMT
+ Not After : Apr 28 20:03:11 2029 GMT
+ Subject: C = US, ST = California, O = Kong Testing, CN = foo@example.com
+ X509v3 Key Usage: critical
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication, E-mail Protection
+ X509v3 Subject Alternative Name:
+ email:foo@example.com, email:bar@example.com
+ --]]
+ cert_client = [[-----BEGIN CERTIFICATE-----
+MIIFIjCCAwqgAwIBAgICIAEwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzElMCMG
+A1UEAwwcS29uZyBUZXN0aW5nIEludGVybWlkaWF0ZSBDQTAeFw0xOTA1MDIyMDAz
+MTFaFw0yOTA0MjgyMDAzMTFaMFMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxGDAWBgNVBAMMD2Zvb0BleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJldMxsZHDxA
+RpbSXdIFZiTf8D0dYgsPnsmx5tVjA/zrVBSVBPO9KunaXNm4Z6JWmUwenzFGbzWP
+NLfbLn4khuoczzqSru5XfbyH1HrD0cd5lkf44Dw1/otfIFDBleiR/OWEiAxwS4zi
+xIajNyvLr3gC5dv+F+JuWpW1yVQxybIDQWoI25xpd3+ZkXO+OLkToo+YpuwIDlUj
+6Rkm5kbqoxDpaDihA2bsAqjNG7G+SHthaNyACsQsU/t6BHSWzHumScN0CxJ+TeVH
+fTZklelItZ6YP0B0RQjzvSGA423UgALzqJglGPe8UDjm3BMlg2xhTfnfy1J6Vmbt
+5jx6FOXUARsCAwEAAaOB8jCB7zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF
+oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBRTzNOmhGRXaZamxVfnlKXarIOEmDAfBgNVHSMEGDAW
+gBQLDgQOl/htYk8k8DvGb9IKO40RETAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw
+FAYIKwYBBQUHAwIGCCsGAQUFBwMEMCsGA1UdEQQkMCKBD2Zvb0BleGFtcGxlLmNv
+bYEPYmFyQGV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBziDuVjU0I1CwO
+b1Cx2TJpzi3l5FD/ozrMZT6F3EpkJFGZWgXrsXHz/0qKTrsbB2m3/fcyd0lwQ5Lh
+fz8X1HPrwXa3BqZskNu1vOUNiqAYWvQ5gtbpweJ96LzMSYVGLK78NigYTtK+Rgq3
+As5CVfLXDBburrQNGyRTsilCQDNBvIpib0eqg/HJCNDFMPrBzTMPpUutyatfpFH2
+UwTiVBfA14YYDxZaetYWeksy28XH6Uj0ylyz67VHND+gBMmQNLXQHJTIDh8JuIf2
+ec6o4HrtyyuRE3urNQmcPMAokacm4NKw2+og6Rg1VS/pckaSPOlSEmNnKFiXStv+
+AVd77NGriUWDFCmnrFNOPOIS019W0oOk6YMwTUDSa86Ii6skCtBLHmp/cingkTWg
+7KEbdT1uVVPgseC2AFpQ1BWJOjjtyW3GWuxERIhuab9/ckTz6BuIiuK7mfsvPBrn
+BqjZyt9WAx8uaWMS/ZrmIj3fUXefaPtl27jMSsiU5oi2vzFu0xiXJb6Jr7RQxD3O
+XRnycL/chWnp7eVV1TQS+XzZ3ZZQIjckDWX4E+zGo4o9pD1YC0eytbIlSuqYVr/t
+dZmD2gqju3Io9EXPDlRDP2VIX9q1euF9caz1vpLCfV+F8wVPtZe5p6JbNugdgjix
+nDZ2sD2xGXy6/fNG75oHveYo6MREFw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFmjCCA4KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzEdMBsG
+A1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcNMTkwNTAyMTk0MDQ4WhcNMjkw
+NDI5MTk0MDQ4WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEV
+MBMGA1UECgwMS29uZyBUZXN0aW5nMSUwIwYDVQQDDBxLb25nIFRlc3RpbmcgSW50
+ZXJtaWRpYXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0dnj
+oHlJmNM94vQnK2FIIQJm9OAVvyMtAAkBKL7Cxt8G062GHDhq6gjQ9enuNQE0l3Vv
+mSAh7N9gNlma6YbRB9VeG54BCuRQwCxveOBiwQvC2qrTzYI34kF/AeflrDOdzuLb
+zj5cLADKXGCbGDtrSPKUwdlkuLs3pRr/YAyIQr7zJtlLz+E0GBYp0GWnLs0FiLSP
+qSBWllC9u8gt2MiKyNlXw+kZ8lofOehCJzfFr6qagVklPw+8IpU6OGmRLFQVwVhp
+zdAJmAGmSo/AGNKGqDdjzC4N2l4uYGH6n2KmY2yxsLBGZgwtLDst3fK4a3Wa5Tj7
+cUwCcGLGtfVTaIXZYbqQ0nGsaYUd/mhx3B3Jk1p3ILZ72nVYowhpj22ipPGal5hp
+ABh1MX3s/B+2ybWyDTtSaspcyhsRQsS6axB3DwLOLRy5Xp/kqEdConCtGCsjgm+U
+FzdupubXK+KIAmTKXDx8OM7Af/K7kLDfFTre40sEB6fwrWwH8yFojeqkA/Uqhn5S
+CzB0o4F3ON0xajsw2dRCziiq7pSe6ALLXetKpBr+xnVbUswH6BANUoDvh9thVPPx
+1trkv+OuoJalkruZaT+38+iV9xwdqxnR7PUawqSyvrEAxjqUo7dDPsEuOpx1DJjO
+XwRJCUjd7Ux913Iks24BqpPhEQz/rZzJLBApRVsCAwEAAaNmMGQwHQYDVR0OBBYE
+FAsOBA6X+G1iTyTwO8Zv0go7jRERMB8GA1UdIwQYMBaAFAdP8giF4QLaR0HEj9N8
+apTFYnD3MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4ICAQAWzIvIVM32iurqM451Amz0HNDG9j84cORnnaRR5opFTr3P
+EqI3QkgCyP6YOs9t0QSbA4ur9WUzd3c9Ktj3qRRgTE+98JBOPO0rv+Kjj48aANDV
+5tcbI9TZ9ap6g0jYr4XNT+KOO7E8QYlpY/wtokudCUDJE9vrsp1on4Bal2gjvCdh
+SU0C1lnj6q6kBdQSYHrcjiEIGJH21ayVoNaBVP/fxyCHz472w1xN220dxUI/GqB6
+pjcuy9cHjJHJKJbrkdt2eDRAFP5cILXc3mzUoGUDHY2JA1gtOHV0p4ix9R9AfI9x
+snBEFiD8oIpcQay8MJH/z3NLEPLoBW+JaAAs89P+jcppea5N9vbiAkrPi687BFTP
+PWPdstyttw6KrvtPQR1+FsVFcGeTjo32/UrckJixdiOEZgHk+deXpp7JoRdcsgzD
++okrsG79/LgS4icLmzNEp0IV36QckEq0+ALKDu6BXvWTkb5DB/FUrovZKJgkYeWj
+GKogyrPIXrYi725Ff306124kLbxiA+6iBbKUtCutQnvut78puC6iP+a2SrfsbUJ4
+qpvBFOY29Mlww88oWNGTA8QeW84Y1EJbRkHavzSsMFB73sxidQW0cHNC5t9RCKAQ
+uibeZgK1Yk7YQKXdvbZvXwrgTcAjCdbppw2L6e0Uy+OGgNjnIps8K460SdaIiA==
+-----END CERTIFICATE-----]],
+ key_client = [[-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAmV0zGxkcPEBGltJd0gVmJN/wPR1iCw+eybHm1WMD/OtUFJUE
+870q6dpc2bhnolaZTB6fMUZvNY80t9sufiSG6hzPOpKu7ld9vIfUesPRx3mWR/jg
+PDX+i18gUMGV6JH85YSIDHBLjOLEhqM3K8uveALl2/4X4m5albXJVDHJsgNBagjb
+nGl3f5mRc744uROij5im7AgOVSPpGSbmRuqjEOloOKEDZuwCqM0bsb5Ie2Fo3IAK
+xCxT+3oEdJbMe6ZJw3QLEn5N5Ud9NmSV6Ui1npg/QHRFCPO9IYDjbdSAAvOomCUY
+97xQOObcEyWDbGFN+d/LUnpWZu3mPHoU5dQBGwIDAQABAoIBAQCLqQzeM3q7/4iI
+1l+r31DKacgbz4M2MW5XnJNqZTX/f8pcx+vvjqfiuADwH/b4JcaKRCSSOeMSMiw+
+9fGb2+WkksHARE3bLH+LTWKYvXRvI/FP73s8Oato/iKuh+vdE/zqgktmkGisjuGK
+/l1Cm8VaE8GBGh5kDDyfsyD5dDGJ0fYzJkfQqygd5B5TSaWflQsB//AXvHzkNy+G
+RHbrMl7t9rDCTtwnefSEJIEwAZerGKV0p+VlRy23mQLwxTxJ5jEjVvcFIMalnD4R
+nKaZYb3LgkCCTQ5Lw/xrkdAEJwfafhdu1CmvKelv1qpcz1vJdrFSfX5NOYS/93jI
+aKJT8Nm5AoGBAMmOOUTvbUd9nlbZXYGLTsoy+qA+OhLkB59krddH4mFoRvbggD6U
+Y/h7O/fA4spqtts+aVoEh/jyuwGTxMn0NPLjD0G8YZr1A4GUx1hgEQ1NIWvRlXrX
+s1bgIlaOc14hOpKf0to3mIovyhRm8PaDbQfHWfyl4yKtFgKiO4OCMK0/AoGBAMLK
+e9C5sedDKt8ESycaXnui1hA4WQkeMdXFflnabuhE29dkC7kDFEVurlmsQb3zFa0V
+dF40niT7xYqzdEJIbaZ3JZIrSFhnPSSBna+B1FjMhTVb/5sjPJS87BvjVYiZd5GY
+5Az4RgSlU3PlsaiuR95NH1vDxHXb5GcMs/EfnEklAoGBAIVFe2yve+yXjUkT9RYh
+TPm596pZOwEeskOcyK3epDuQPcwj6eh3Khs1MRPDALKjGUGi5PpWoKnlpe2HDcoT
+pacsp/vpWgiiFa1q+NzguKW46G5oaJSPZ8/75/ifvHzzL82fzEXqGPzWWKJg5te5
+UzCfikraTXOySyl2qC9uuEz1AoGBAILH8eNMmb48YW9EcbS6Ro9Z38EaI+U0SZ9O
+LqvjNS1q9fMiL6CzCYwoaJS6S5VdvMLtsaiCSV9pTtL182uBN2VZf3co6jS4c9ur
+zpQEZe6Mui7+KpodSVJPmXKL6mSBLT8q2IpAsrnxyhr5L5OiF4yQWSqCQMgkr6/k
+XnfYklSlAoGBAKBePjIdBGLy3ckdlTfbuTeO3kp2eZFBDtGzxP515+LcoRfOjd8T
+ZDX/porUMcgbtIF/B4SNso+8D/aHTCg3QAo6nDjFFjUBHhftgy+GP3BFfMvjqou6
+utJFRkc3FvrrkkeWHnyDQrPmAHjar94/xq1k1Vo+KQHQVQOrvtQt6KXK
+-----END RSA PRIVATE KEY-----]],
+
+ --[[
+ Issuer: C = US, ST = California, O = Kong Testing, CN = Kong Testing Root CA
+ Validity
+ Not Before: Apr 13 23:48:41 2020 GMT
+ Not After : Apr 11 23:48:41 2030 GMT
+ Subject: C = US, ST = CA, O = Kong Testing, CN = example2.com
+ --]]
+ cert_client2 = [[-----BEGIN CERTIFICATE-----
+MIIEJzCCAg8CFAQ6oTnLBUHbumx1bxyY9kV0W21BMA0GCSqGSIb3DQEBCwUAMFgx
+CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxLb25n
+IFRlc3RpbmcxHTAbBgNVBAMMFEtvbmcgVGVzdGluZyBSb290IENBMB4XDTIwMDQx
+MzIzNDg0MVoXDTMwMDQxMTIzNDg0MVowSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
+AkNBMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxFTATBgNVBAMMDGV4YW1wbGUyLmNv
+bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK+kZhxdN8PA3SW9cXiv
+xgtANq57PIWNnSDsg9Yxn0/+JKR45pSU+SKYtZUJJJuOdkv4IIJLm6uG6LbOPUDO
+g9EaV0Zw7RQtbY6EDFDFzeyq0/Mwl9wLJtXf0fPsXGyFIdeelBjzoSVsGGJKPWbP
+rlUtSHCrpFX53NTPnNVUJz9V6CdzZJgbyoiWP7ggKJeRPq6jCW5pt+cd+sR4+EPh
+daBmEVWeifLEKCbBvsQaOGfU1aVG1AlX0RpLBkTxOOFIIk/3dgWOsrek2ofjku4F
+g0MeWmD8oXOHUX2JxO77/BbLDQt0lzD27y/EkDoqy6mMAH85/LTYrU+P0WsEyexg
+CHcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAwWAxQjQOoGxU5LQu4ZmsCkps9y0B
+kNj8MUpLcFmK+02VIUh3hM4vuB6Gct2Ph+6zqCge3oqTXltU0Bs2MTwAKs/scsxq
+Mtanz4W00UlmG3QdgHaEs196tYQ8cKIaGZsNBv15VgBBduUG478pKqYE8bJKBbw7
+1Ym390hSPo0dNe7jLFXx6AaJvlEYh09P4FgfkXuY5VVTGXfN7XgJI073pLRY6iGH
+Qd+Egymh86AQtnoNpmqSCMNcjRVAyR8Ti3qnyro8ruZCnNYHieGeh/ZsZvhGDeWX
+v4YXjW2NDQ5+Ok6Gtvhf/l6RSrnXLbZtv8NStqwQJ+ydu05BJglZ/7Sb0uQYVNq2
+H8V+MtApFT6fG6ANM6hadNFG+p8Hwz6k4BLrc9ZxeWYKWIIusqExR9JIlGzEjvFJ
+6NmNjm3eZE9Ue4YgURj1KTr53wAso4LbJpz/zuZS+m9PMz7n8fRL25/Z5b/92L3a
+w0vsxUJyTDeMvYf8oT6OkxNVJ0zBRZNtEg5AJKdP6nU53V998jHP9vUisrU2ALhu
+Jw3QiWiDKnRtx8PiiRx7dWo+Xwn9+xVypytqNz3w/XJlOjMwOg73q399w+vMiFTl
+qdr7eYvaQBGOZVc3OdiP8afyVxlhHBowUoi8G+iPbgOsARHv/j4UeMVyIThzxv73
+a2EQ5BzyOzQ81H4=
+-----END CERTIFICATE-----]],
+ key_client2 = [[-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAr6RmHF03w8DdJb1xeK/GC0A2rns8hY2dIOyD1jGfT/4kpHjm
+lJT5Ipi1lQkkm452S/gggkubq4bots49QM6D0RpXRnDtFC1tjoQMUMXN7KrT8zCX
+3Asm1d/R8+xcbIUh156UGPOhJWwYYko9Zs+uVS1IcKukVfnc1M+c1VQnP1XoJ3Nk
+mBvKiJY/uCAol5E+rqMJbmm35x36xHj4Q+F1oGYRVZ6J8sQoJsG+xBo4Z9TVpUbU
+CVfRGksGRPE44UgiT/d2BY6yt6Tah+OS7gWDQx5aYPyhc4dRfYnE7vv8FssNC3SX
+MPbvL8SQOirLqYwAfzn8tNitT4/RawTJ7GAIdwIDAQABAoIBAHMJzgdN1rRToYSS
+a7uMBL5htG7bMGyYsA1cW4zyu1F9NyqyNPOkDvjl5ChU8LEhwcFIJqKwOqlBlzIE
+KoJDwHo4MmlklSLeDh+FxTsyEwmraV6iuRPaCfmSusR0TqSVHfFHX+Bn0WfdQKs/
+zK+F3rzTB9sj0GKvYD/SKvpeP8Zuu9EBqo4N7PU3VHwDq5t32Ut/+M5XWtulsQcs
+qXr2R3agj/DnODANT6Dn/mJboTrYOSV18S/Yw/+OnWBcLzlT5sj0aLgrtXvIputv
+9caux4HklAQr29+lKB8nBTfjhXnBntMaEgqCVJ3ri83MuEfVDhmjwo6PnX22/J0h
+2XbCyUECgYEA2v8m+CTBTjdAqOuje34+UiWRzT2P9OFONV8nYgzEcQW5JkUoFCun
+KgQQIvjCsX4jY6/8w/IPF1ieTconZYJUWSyMZFtBBDCVif1GZRiiM2C4Zcero1KV
+U0V3wZcnYkzafzIHkqFUYZwamvdKWVI4c6F5MhSEKCgcbgKKI52TEokCgYEAzVHr
+KjQt+dqNkbipYoGH2ywLdcogDwKoyUFbgcvz/q625gu4am025wF25yRKExm7Dyjx
+eCQC+KOsBfJSc78fG5R6KPIDK1JrpUEGSCeqFICiqGv9kUzPf5zeGZVf9cU4tyPT
+5wYUEM7NX8VRoasZ4OUvYyYBw1Cx8vMdvQn/gv8CgYAIhxcFYqkEWrJx4XskO+5B
+VKUw0MziREO/YE0wTD76B7cF/ntpDaocwLvAIN+z+a13HEtDdhGQXysK7GxMT57p
+OgrdfZAykZHBJdOv7B2k0odbr0LHwVd/Pp1DNJecBFId0dzpoM6gXmvKzQZgJAt+
+tTL6+EGNLsKspfyrFl+7wQKBgQDAt2VuJbAJ1xQOdS+4IDCujfbrxp60uCBJVylW
++WK56LAP2WxtqLlhtsQuTKeiqgIkRp/vzo1jZ+0tX7f4oKnIL2NCT3aeESys3g3R
+aDmCKQOD5mkJGvmgpFLr3INHoqiLbfuV2uS2qgWnIQRwJLOTnksOWzxIYdPFYGDH
+cTz9bQKBgQDGv929DUinrKXe/uKJHLAcq+MjmF/+kZU9yn+svq6SSdplqp7xbXX4
+3T5HCWqD4Sy+PVzGaDg5YfXC8yaFPPfY0/35T2FoQEiCAPQO+07Smg6RqJ3yVpIm
+LTsbLleJTc8CX0bI4SukQ7MVQsiHimzyEzx3eyLt1S8aBdJuRFZ2mg==
+-----END RSA PRIVATE KEY-----]],
+
+ --[[
+ Issuer: C=US, ST=California, L=San Francisco, O=Mashape, OU=Kong, CN=ssl-alt.com
+ Validity
+ Not Before: May 24 23:46:58 2017 GMT
+ Not After : Jun 23 23:46:58 2017 GMT
+ Subject: C=US, ST=California, L=San Francisco, O=Mashape, OU=Kong, CN=ssl-alt.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ --]]
+ cert_alt = [[-----BEGIN CERTIFICATE-----
+MIIFXjCCA0YCCQCsb6B5OWdHXDANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJV
+UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEQ
+MA4GA1UECgwHTWFzaGFwZTENMAsGA1UECwwES29uZzEUMBIGA1UEAwwLc3NsLWFs
+dC5jb20wHhcNMTcwNTI0MjM0NjU4WhcNMTcwNjIzMjM0NjU4WjBxMQswCQYDVQQG
+EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNj
+bzEQMA4GA1UECgwHTWFzaGFwZTENMAsGA1UECwwES29uZzEUMBIGA1UEAwwLc3Ns
+LWFsdC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4SEsrn5zj
+f2w/mT9zxw9RZJxIo325HPHUV27U1gX5YFnCGdA6znvzd+GzUwohJMFhH7X7k3+g
+NhXZlUaxJ01N98QBmNH6GfzAoZexny+QGRU7/jkziQMH1A94yXBxQa5cfg3WA4ti
+JaIDZHRTVLDTvONm4a1GUuU6p6aUarZznCzzvkCqPzSBamHMu2jMWmozdVwPQufH
+mzJLbNLa7TVjVnalKgAdnXz8Rdnivb8pRVj8ASJU+iGQBCaXRAtRm4Hd0PAdJPXH
+6ObregxF0uhwDlHr2FbmdYjWKf6WB5xz18LVreWF07VN5AHsa+H/wMYOpuInmiYt
+9Xzl3h8dt8KiFwaMf4jYyNRRyLZhJEM8F7pSAuTLXTcluK79hWhliwO7DFKGbrJY
+YDQoLQFX6E0xyfYXXnaNTVuiu9503wwX2+IoomUEFXQ0TJ1+9gBg855Jx6Xw5/Zb
+njQS9ayYl5HlGzWKR+Dul13JgDDMYSqvI69tTmL1JOdufopmFUr50C0TPdp2cQlP
+gHXhqMbAua+EcpPiBSk9mQ0jY2CpRczos5Ais4TV8r71VEqJgaTVd39tG63/q/ig
+XFvoPRzi53/tBWy3cVroEvsajNzObuQcwW7b70AULnp0VFDhrRuAsQ9ULXOi6gMP
+K0hyONTdXijtHk6ndEFH9qpYMXQOmpCYtQIDAQABMA0GCSqGSIb3DQEBCwUAA4IC
+AQCvuEsqWpgRqLgvfJXgoLRpoeADG3OlY0ikuOaW+dPapbC9WC02Sa0f7Iy6gED7
+8UFGUorZVYKKTuR8SJk27Q/vVKV7Ljtj9mkIRcBPWpePr08qzVBd3xrSONPVwRgU
+cORI2xNFaz0NwtKOHZ7MIQ8SJbvlqCHYUrl165eIJIiUF2hyzMv4Ymg8Vb0gwGtG
+DEibtQweiMZ17STlZ/q1qW5q9nlEvIUNHb8uno77r+l7LdBNWfSwwme+TQHXfR8q
+UzMzLvgKJQzkayaNQI9BD+Ztm0BcYzC72MrHfpTyM3PndbL5Kkws40XZ5bmwgbYn
+JsIUW1cGxBUc3+lVVQUO4zpihO5oQXwV5LcMkIfYEc1VA1LfqmveXB8/5bGTsHVX
+HVWGNNB6Eb5t+jlGRWE61KU79kw3jBuhG/MXSsjeR3rPX6mqoVMNtPzMuG7V9f+a
+bbsKp5oLfDPw1D7x7HgBDH7wlbovXavbF/7gqApdssYqtPqVC9MekYAOhC63tuuP
+oBthw0f9wa7vQc50VLSIOAUZbgg3Qpic+abkUpLLqPH2nIp0ffBAZ054+u7GkMwQ
+daRqH/ssQmfPQxd1l4x8mynx876bgWrg2y6EJoM1N5zCoA2C1hloDL4EApJHCmP2
+OoLwADN8ov3ctgXUAv9wgYkJHkQ7TO1dRpvuIzXc/oJA1w==
+-----END CERTIFICATE-----]],
+
+ key_alt = [[-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAuEhLK5+c439sP5k/c8cPUWScSKN9uRzx1Fdu1NYF+WBZwhnQ
+Os5783fhs1MKISTBYR+1+5N/oDYV2ZVGsSdNTffEAZjR+hn8wKGXsZ8vkBkVO/45
+M4kDB9QPeMlwcUGuXH4N1gOLYiWiA2R0U1Sw07zjZuGtRlLlOqemlGq2c5ws875A
+qj80gWphzLtozFpqM3VcD0Lnx5syS2zS2u01Y1Z2pSoAHZ18/EXZ4r2/KUVY/AEi
+VPohkAQml0QLUZuB3dDwHST1x+jm63oMRdLocA5R69hW5nWI1in+lgecc9fC1a3l
+hdO1TeQB7Gvh/8DGDqbiJ5omLfV85d4fHbfCohcGjH+I2MjUUci2YSRDPBe6UgLk
+y103Jbiu/YVoZYsDuwxShm6yWGA0KC0BV+hNMcn2F152jU1borvedN8MF9viKKJl
+BBV0NEydfvYAYPOeScel8Of2W540EvWsmJeR5Rs1ikfg7pddyYAwzGEqryOvbU5i
+9STnbn6KZhVK+dAtEz3adnEJT4B14ajGwLmvhHKT4gUpPZkNI2NgqUXM6LOQIrOE
+1fK+9VRKiYGk1Xd/bRut/6v4oFxb6D0c4ud/7QVst3Fa6BL7Gozczm7kHMFu2+9A
+FC56dFRQ4a0bgLEPVC1zouoDDytIcjjU3V4o7R5Op3RBR/aqWDF0DpqQmLUCAwEA
+AQKCAgBn/HtWaWHJSdzWYm5YsYnmPuSlZIQMEdYwIQosVXzXhFQB4DkNBfkRoKMe
+YoxDuY7ZdGBnTork58AaoE5cprXLejUDRa2u+D0UodqMYywentjJmqHCf9zS7Qmx
++dFWR17RWFwMWMGtJ1ktmuC9KPwC7wJOyqfRF/O7zmCEPVcpE4aWH9QzfSjuog3/
+zfzL23U0BlRlVDaf/uY5g3XUDahjnqWie3nHPFgLrorNlI6rBjO5OBacZuzLbFwu
+XToZ2atFdKIZgAKkxLqRQ7RrLiD1Ik99yvz2XHpThyzekfrpr2WE1/S9OIKFKlUf
+iJzliWz5VZgmCqjipDTPLTDXvxq3EJDGbOlsD3eLXGeIufbQY4SqV73rth+0gER4
+m0S7cGjUuUASvtoX0pyxFwlZ3/DISMDKvGDGvx3FlYhDs/8D54C9nuuYXpCflw/g
+8+C6uIF4zIcQ4JNI9RKQDlUYCTKiz8klCzXQxVy4HVyPVYWQxXfNYVkyQjf7PxQi
+Xr1TwgE2snGjxqfWbuEnqTz2LADGUjtTwwZcOkur3CAAKqd86Keka6ZUaAe8IM+D
+xuUDH/v3cH8qb5PDZ2OIoVTJk3DDr2NZ3JOX55UV7AutWFYTCRhX3qNY9aQ8iol7
+AjfMIRzB6uupMgbpCuuAYlvDtSxNK2hPFWlO24ybqh15F8NkoQKCAQEA6UWA5Lgc
+E4+b7Yr9vqFqoTlpWmPBvPR1F4GolOOxs2R3pL45Uqc8afgQPXSB/vGH9o4asF6X
+8XfqRNyObTzSvbwuOMw1G1lTMPiWVHY2MNs4fZwEoCdVJJSYJorEJlyPRxsZ86xD
+ltGsa7RiLUk2IoYrocgrMz6MN03eNET5mVu1eLb59fMSwEoer6CY2n8kE56SvsaK
+3ulwaJeKBUWWXsoZ+e7NWx55sPQ/95F2exk9aCGdRUsFMM+vjunIq+7jq5DyL/O0
+Jqe9g4QTGLTT9DydKZhjvPlnW51Ehl42nwoQNSpCwHmR9qfV/lqjU21tnwJ0lAEd
+AuVaRyMfyLE/3QKCAQEAyjzalPsVBNL+/9Tl4dabR0VpP00oTl1g8fguke8sE+ay
+BKwipAQHoCvFufB/wmTR/H2U7ogGhwUWxkjeJIk44BAqI2wbq4HdKE1Yo2k2QB6m
+VkWOqdf47OKupTOhxIeNrRapCQ5r44LVjK7jgvMvbOJ9OGZm+/5IgkoYgRK4jOYk
+h89iM3g5bEQ4zgugQP85ghQQ9pwwAy3d/06V2auCJo4pitjluKctB5HtXubTJSMl
+axRpVQ8fhtlC8MUpDx1v8OZRkxQIa82VR9v8cUM0Zlw8t1vOz/W8ECzyfJtsFMUC
+ykMTTBmpt9+sAXaCR0Mwo3GJABrsSn2z5vGmiKEauQKCAQBoxisKkBcsQgiLPS6T
+fPTjzWGgk8XlFPeywy3xEgQyyyFiAX7FvQ/JmP3SXI428E1dVJ5wMUyVzIKQw2/F
+aNhPGEK6iB4iVpCjIkSDU0Ur1IsfAACj3obDk2pzhUhs4o4IJggWBn2lNC/5gF/I
+b2W9Q/49ACdHMQTRokv3tjNVyndL7QOAkNkPPTtjLwL4wLp3hXXr8klVrgwrLkVz
+8LmFgckBFV1vW9TUwiApFlDdIY0PRGnbQcLnFaGI00Cq2PWxjbz6BMAZzKW2eJAL
+PM0mmkMM98F0k6D06UJqB88IyABXoM+ym+gPnXrkb9mEE1Z1YSjzgTyCnHlcEk6o
+WdSRAoIBACg1bSgNu6IX6UcoJwR9zKWg8Un6pzbdbfbt5yRwrwzN4zr2lnSRsIpx
+6YAMhvo5XV6cAU5jkRirNFn8Bt6wwbQawqYESoQQ782hgywKMRUSgvPIJJM0M3O1
+pg+GcnVGli6BwN3iFiVHz/mGlAlnHFjDty3NflA/wF7XKIQRGsw4va6a1uVw08no
+znp46UXC+MYaAz1k1E7tgaPPFhAO/3N29F98vD+4TUWiB9XEgtpxYHEnv8F/nj8L
+VznTmVQ+ABGfWTyq0PnOCA4feNN0DykC0beK81V5gAifRx7rL9P5T5KzP1T0DUBX
+ojUwQLq2aycz3Y89yxkMYQZbqUak8VECggEBAMj0qzxSPrboxNOwKRcpGGhRFscJ
+K/4iNG165/q2btZiDZ9BiUO8WZGR8Ww50fZb21fIJBaU+znLaDiY6b4UDc0H4d0H
+vt9bkm9BmNkdbHlDR++7XcRIuq1w4EDtgvDqT8oCuW8mWOt+oqnQof3MOO0Z0AbI
+Ksbcnvk0qf75vZUVPoPCoIAuex23PxWhewb3BM9ifeRr4EUjWWH9+iJ0ucnGHndq
+MRz5bk6HBaxa2Twpa6yra+pobyWhRyU/X40wV7nUs3wd1vZNMjn0i8vXwnT6zdNv
+11htsJRdoyo2gXnVNMWN1D+8QTozSXFTTyM2LG4k06Dw3dAl5KHQjKWX+ac=
+-----END RSA PRIVATE KEY-----]],
+
+ --[[
+ Signature Algorithm: ecdsa-with-SHA256
+ Issuer: C = US, ST = California, L = San Francisco, O = Kong, OU = Core, CN = ssl-example.com
+ Validity
+ Not Before: Nov 18 16:49:53 2020 GMT
+ Not After : Sep 3 16:49:53 2294 GMT
+ --]]
+ cert_alt_ecdsa = [[-----BEGIN CERTIFICATE-----
+MIICOjCCAeGgAwIBAgIUbsAIMsTeD3F1oNLKOyRabSN6O9EwCgYIKoZIzj0EAwIw
+cjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh
+biBGcmFuY2lzY28xDTALBgNVBAoMBEtvbmcxDTALBgNVBAsMBENvcmUxGDAWBgNV
+BAMMD3NzbC1leGFtcGxlLmNvbTAgFw0yMDExMTgxNjQ5NTNaGA8yMjk0MDkwMzE2
+NDk1M1owcjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV
+BAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEtvbmcxDTALBgNVBAsMBENvcmUx
+GDAWBgNVBAMMD3NzbC1leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH
+A0IABCwjAZ7WZIwBJQOER5LB6g554ecpBVUnHKjYq8xiWU2+giX5pg4ros6rf3tv
+MMkc3aPYz87B7bwQlZ0Z2NC7iUujUzBRMB0GA1UdDgQWBBQzR6or+QaEVZxXrX5/
+BhgA7y5mjTAfBgNVHSMEGDAWgBQzR6or+QaEVZxXrX5/BhgA7y5mjTAPBgNVHRMB
+Af8EBTADAQH/MAoGCCqGSM49BAMCA0cAMEQCIEDzO105JmNu3RLib3DyIZ4TqDTF
+/iEr+t+W6+rZqiHuAiBvhIxGlLfkypQa9p4iNKRLmFcEk/S/shQ4d0hzd9SDbg==
+-----END CERTIFICATE-----]],
+ key_alt_ecdsa = [[-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIECo5oNJH83ZUFUm3SfjRHyPyRU5pJ5D1V0zk4KtrlNZoAoGCCqGSM49
+AwEHoUQDQgAELCMBntZkjAElA4RHksHqDnnh5ykFVSccqNirzGJZTb6CJfmmDiui
+zqt/e28wyRzdo9jPzsHtvBCVnRnY0LuJSw==
+-----END EC PRIVATE KEY-----]],
+
+ --[[
+ Issuer: C = US, ST = California, L = San Francico, O = Kong Inc., CN = ssl-alt-alt.com
+ Validity
+ Not Before: Nov 18 07:28:56 2018 GMT
+ Not After : Dec 18 07:28:56 2018 GMT
+ Subject: C = US, ST = California, L = San Francico, O = Kong Inc., CN = ssl-alt-alt.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ --]]
+ cert_alt_alt = [[-----BEGIN CERTIFICATE-----
+MIIDpDCCAoygAwIBAgIJAIAQMZH+2V26MA0GCSqGSIb3DQEBCwUAMGcxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW4gRnJhbmNp
+Y28xEjAQBgNVBAoMCUtvbmcgSW5jLjEYMBYGA1UEAwwPc3NsLWFsdC1hbHQuY29t
+MB4XDTE4MTExODA3Mjg1NloXDTE4MTIxODA3Mjg1NlowZzELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbiBGcmFuY2ljbzESMBAG
+A1UECgwJS29uZyBJbmMuMRgwFgYDVQQDDA9zc2wtYWx0LWFsdC5jb20wggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCngZ5grKAV5bR/FRSkIiR9Y54OWMt
+endztlp3scp4vTVci4mMOhsSMkTJSpxOieOWACCKpHksux8kUJjOgJ12p9FrWII4
+SYj3+5R+I8ujD3zmx3IDol8UIFsCuibVhN9FKDcVHySPtNGwIM0NnjuVm2fl79hU
+ILNmtOq/GoUbScecaMnKzFZy+VUPXRIdQtHOUUteVdppFrx2EPP+Az9l2CN75CRi
+VjwxTA8REkYm9C2okVdj38JodiBnkO1z0aEIlQD8qKgG86C+YF7qTlSzUtoOHvkt
+Km+PoKoMlDvcZ/ItXz7b2I+x3jhsnVETGTXV8jHIRncl+o7jKV5H/gxDAgMBAAGj
+UzBRMB0GA1UdDgQWBBR2h+rYF8B91WE7tUpeCST8TOQrUjAfBgNVHSMEGDAWgBR2
+h+rYF8B91WE7tUpeCST8TOQrUjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
+CwUAA4IBAQARP3bVbTHYlB/V6Ws0jvjJtRGs6RkIB8Hvwcn9hltoKpgdRcrkpkCI
+50jmipO2ssP5uEhhBuS3D53K3GEYWJJh35MW7iAzJw1+Yn+/00/1nwhoaYvjbxv+
+k5rtGHIR8oj8Uf0ijh+ah508cavls/jotQXqkGkkg3QNDJ/2H+QmxMtAM5VJ/dU9
+66IeVk1e/y4wBMFyCzMQQ/HluLmOXlrhttEgwpcCfm4/dksRPav5nPi75OqabI0V
+bpTBKoI0Llp45GWNeQyF9kPV3wTDF0UmzZnkD95hoe4CHZV7dsUGS8YfM9Wv6Cvh
+wcqsNL5LjIUJiNuykV2pruOsf5IoCKlG
+-----END CERTIFICATE-----]],
+
+ key_alt_alt = [[-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCngZ5grKAV5bR
+/FRSkIiR9Y54OWMtendztlp3scp4vTVci4mMOhsSMkTJSpxOieOWACCKpHksux8k
+UJjOgJ12p9FrWII4SYj3+5R+I8ujD3zmx3IDol8UIFsCuibVhN9FKDcVHySPtNGw
+IM0NnjuVm2fl79hUILNmtOq/GoUbScecaMnKzFZy+VUPXRIdQtHOUUteVdppFrx2
+EPP+Az9l2CN75CRiVjwxTA8REkYm9C2okVdj38JodiBnkO1z0aEIlQD8qKgG86C+
+YF7qTlSzUtoOHvktKm+PoKoMlDvcZ/ItXz7b2I+x3jhsnVETGTXV8jHIRncl+o7j
+KV5H/gxDAgMBAAECggEBAJhVXTgLZ3EyHimrWs1tuJiXHrdYJBta+tkl6VY7YgJ0
+B6qyxi1u5fWuR01QC10mbW/iFZav+vFaXpvsQk+ROK/B2BgwJW2tkXqZ/7dkiWbP
+HrL9dm8Fz2pPkS1nTDJhWOom+kacI+AgZul4I0j/jCAkjoTa4fenyQUho4WGWp3q
+GIVcawNfprrXObMUgs+31eps3A/iza7w3eBuH4aR2npW6DtBr3KPwH9H1yBLpOqq
+E7vY/oyfndKyMMdJGk9Ql8AN5SA4B/r2t5nw+liRUUxIj+yWf5q5pj2FftBo2Ldn
+0JGWKXDUTdniLzK+akynRv5o1GFR0SeK7TLRzASfavECgYEA+nojt54CTglUOPCY
+rpoM898SypItYJOhlvE/HlfmsLmRMpkCAIYo7yCtCTdRC9gd9FPxEJIAC/P3MzTa
+KlVfwXqlFIC+oflfIkF8I5Zw4JsqqF+aqEY4b2xTLfxnfK5gMID2rRstUVQO1BiE
+Jb6KzB/67e1yokGgKLIeGsOkqdcCgYEAxuiTVt1RZzqOKTvKYLd8tANzm+lwD6Dd
+kpVl3xZcHEVd/nWfdtR8VZ+V+mbe71Hn7ZX/UBBktLeB6djd0cdd8jIOCkvhW/aw
+vLm2VAM7J6H5Up92q9Jtpid+993fbd07d7peO+boMYzPW0f7wqU0PL+ZK9R9FdS2
+cHaTcFvrW3UCgYEA6DRa8FqXoGidn7vMj/FYmKzw6sLhNmsmnpw/+41Z2/PsW4/l
+fz7gq+8K+0RA6k4MVvmKveXcDTi6rsoMhrpm7yMX7w37rIVWYJd80jEhq9etkDIx
+Wlbe8szlv/gCqF+v5Mdp58kOFhtrM80WlTczzVDIe9JpN2rHY3Lc3csJ2DkCgYEA
+hCAHhyLaKNT3i6JAyz/24OiOCdnlaywzImSE18xVgR3+0sE1HM0GjiXEjSF6IsRo
+aCREBN3u1zAyZrB8oBVrbS8crnA7EUhrm+FMoL/IsongQKbWQEo2NbF0oJdMDAFx
+uBUe9XFreUaMkpjdPi1Y5qVqzHeIY1D5ovjQ9UjJrOUCgYA/GJPhcZbEz4MgokqE
+CCWCLF6Ia5fehznO6VrbKZUUklURFIaw6Uc+DIbGpJZZwVt9l7R3x2pJlF1NlaCJ
+IqGzqSWne1tW86drBcfSip714wsZOoF8PT6iUCa0LC1sum1P4vS2cnRw8jXwIL6g
+gEuhDrQHJ5V1U/Qc1HrqWYH4cA==
+-----END PRIVATE KEY-----]],
+
+ --[[
+ Signature Algorithm: ecdsa-with-SHA256
+ Issuer: C = US, ST = California, L = San Francisco, O = Kong, OU = Core, CN = ssl-alt-alt.com
+ Validity
+ Not Before: Nov 25 14:47:53 2020 GMT
+ Not After : Sep 10 14:47:53 2294 GMT
+ Subject: C = US, ST = California, L = San Francisco, O = Kong, OU = Core, CN = ssl-alt-alt.com
+ --]]
+ cert_alt_alt_ecdsa = [[-----BEGIN CERTIFICATE-----
+MIICPDCCAeGgAwIBAgIUUN+dYLgQkk8az6KLufNic5LFKrYwCgYIKoZIzj0EAwIw
+cjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh
+biBGcmFuY2lzY28xDTALBgNVBAoMBEtvbmcxDTALBgNVBAsMBENvcmUxGDAWBgNV
+BAMMD3NzbC1hbHQtYWx0LmNvbTAgFw0yMDExMjUxNDQ3NTNaGA8yMjk0MDkxMDE0
+NDc1M1owcjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV
+BAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEtvbmcxDTALBgNVBAsMBENvcmUx
+GDAWBgNVBAMMD3NzbC1hbHQtYWx0LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH
+A0IABKnmBOy/odm9rUNVTz2vMzGtXVnodngWFY7wf2W99aLcDLz32WNg10oYdGKW
+MuPCtO6vwWGgOi+/mYSToEU7U0qjUzBRMB0GA1UdDgQWBBQtbY0EZpt9Nlf2spRC
+IfphGjYmijAfBgNVHSMEGDAWgBQtbY0EZpt9Nlf2spRCIfphGjYmijAPBgNVHRMB
+Af8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQC7MFmBMdan3DIsgzLDDwTOLkOI
++Vj2qMdBL4XRWt9c6gIhAMAbZ8M3kMTxPuI+bjZ31Zuu+bGg0Quo4EgU8yMmhJLt
+-----END CERTIFICATE-----]],
+
+ key_alt_alt_ecdsa = [[-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEILefTUI90Vsu3JV1WZVrYgl82HbAICC/9/sMIL6j1RThoAoGCCqGSM49
+AwEHoUQDQgAEqeYE7L+h2b2tQ1VPPa8zMa1dWeh2eBYVjvB/Zb31otwMvPfZY2DX
+Shh0YpYy48K07q/BYaA6L7+ZhJOgRTtTSg==
+-----END EC PRIVATE KEY-----]],
+
+ --[[
+ Issuer: C = US, ST = California, O = Kong Testing, CN = Kong Testing Root CA
+ Validity
+ Not Before: May 2 19:34:42 2019 GMT
+ Not After : Apr 27 19:34:42 2039 GMT
+ Subject: C = US, ST = California, O = Kong Testing, CN = Kong Testing Root CA
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ --]]
+ cert_ca = [[-----BEGIN CERTIFICATE-----
+MIIFoTCCA4mgAwIBAgIUQDBLwIychoRbVRO44IzBBk9R4oYwDQYJKoZIhvcNAQEL
+BQAwWDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoM
+DEtvbmcgVGVzdGluZzEdMBsGA1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcN
+MTkwNTAyMTkzNDQyWhcNMzkwNDI3MTkzNDQyWjBYMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMS29uZyBUZXN0aW5nMR0wGwYDVQQD
+DBRLb25nIFRlc3RpbmcgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
+AgoCggIBAMp6IggUp3aSNRbLAac8oOkrbUnFuxtlKGYgg8vfA2UU71qTktigdwO6
+Kod0/M+daO3RDqJJXQL2rD14NDO3MaextICanoQSEe+nYyMFUIk+QplXLD3fbshU
+nHoJcMS2w0x4cm1os4ebxR2Evndo6luz39ivcjau+BL+9iBAYL1g6+eGOjcSy7ft
+1nAMvbxcQ7dmbAH2KP6OmF8cok+eQWVqXEjqtVx5GDMDlj1BjX6Kulmh/vhNi3Hr
+NEi+kPrw/YtRgnqnN0sv3NnAyKnantxy7w0TDicFjiBsSIhjB5aUfWYErBR+Nj/m
+uumwc/kRJcHWklqDzxrZKCIyOyWcE5Dyjjr46cnF8HxhYwgZcwkmgTtaXOLpBMlo
+XUTgOQrWpm9HYg2vOJMMA/ZPUJ2tJ34/4RgiA00EJ5xG8r24suZmT775l+XFLFzp
+Ihxvs3BMbrWsXlcZkI5neNk7Q/1jLoBhWeTYjMpUS7bJ/49YVGQZFs3xu2IcLqeD
+5WsB1i+EqBAI0jm4vWEynsyX+kS2BqAiDtCsS6WYT2q00DTeP5eIHh/vHsm75jJ+
+yUEb1xFxGnNevLKNTcHUeXxPUnowdC6wqFnaJm7l09qVGDom7tLX9i6MCojgpAP0
+hMpBxzh8jLxHh+zZQdiORSFdYxNnlnWwbic2GUJruiQVLuhpseenAgMBAAGjYzBh
+MB0GA1UdDgQWBBQHT/IIheEC2kdBxI/TfGqUxWJw9zAfBgNVHSMEGDAWgBQHT/II
+heEC2kdBxI/TfGqUxWJw9zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQsFAAOCAgEAqXZjy4EltJCRtBmN0ohAHPWqH4ZJQCI2HrM3
+wHB6c4oPWcJ+M2PfmYPUJo9VMjvn4S3sZuAysyoHduvRdGDnElW4wglL1xxpoUOx
+FqoZUoYWV8hDFmUTWM5b4CtJxOPdTAd8VgypulM3iUEzBQrjR6tnMOdkiFMOmVag
+0/Nnr+Tcfk/crMCx3xsVnisYjJoQBFBH4UY+gWE/V/MS1Sya4/qTbuuCUq+Qym5P
+r8TkWAJlg7iVVLbZ2j94VUdpiQPWJEGMtJck/NEmOTruhhQlT7c1u/lqXCGj7uci
+LmhLsBVmdtWT9AWS8Rl7Qo5GXbjxKIaP3IM9axhDLm8WHwPRLx7DuIFEc+OBxJhz
+wkr0g0yLS0AMZpaC6UGbWX01ed10U01mQ/qPU5uZiB0GvruwsYWZsyL1QXUeqLz3
+/KKrx3XsXjtBu3ZG4LAnwuxfeZCNw9ofg8CqF9c20ko+7tZAv6DCu9UL+2oZnEyQ
+CboRDwpnAlQ7qJVSp2xMgunO3xxVMlhD5LZpEJz1lRT0nQV3uuLpMYNM4FS9OW/X
+MZSzwHhDdCTDWtc/iRszimOnYYV8Y0ubJcb59uhwcsHmdfnwL9DVO6X5xyzb8wsf
+wWaPbub8SN2jKnT0g6ZWuca4VwEo1fRaBkzSZDqXwhkBDWP8UBqLXMXWHdZaT8NK
+0NEO74c=
+-----END CERTIFICATE-----]],
+
+ key_ca = [[-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAynoiCBSndpI1FssBpzyg6SttScW7G2UoZiCDy98DZRTvWpOS
+2KB3A7oqh3T8z51o7dEOokldAvasPXg0M7cxp7G0gJqehBIR76djIwVQiT5CmVcs
+Pd9uyFSceglwxLbDTHhybWizh5vFHYS+d2jqW7Pf2K9yNq74Ev72IEBgvWDr54Y6
+NxLLt+3WcAy9vFxDt2ZsAfYo/o6YXxyiT55BZWpcSOq1XHkYMwOWPUGNfoq6WaH+
++E2Lces0SL6Q+vD9i1GCeqc3Sy/c2cDIqdqe3HLvDRMOJwWOIGxIiGMHlpR9ZgSs
+FH42P+a66bBz+RElwdaSWoPPGtkoIjI7JZwTkPKOOvjpycXwfGFjCBlzCSaBO1pc
+4ukEyWhdROA5Ctamb0diDa84kwwD9k9Qna0nfj/hGCIDTQQnnEbyvbiy5mZPvvmX
+5cUsXOkiHG+zcExutaxeVxmQjmd42TtD/WMugGFZ5NiMylRLtsn/j1hUZBkWzfG7
+Yhwup4PlawHWL4SoEAjSObi9YTKezJf6RLYGoCIO0KxLpZhParTQNN4/l4geH+8e
+ybvmMn7JQRvXEXEac168so1NwdR5fE9SejB0LrCoWdombuXT2pUYOibu0tf2LowK
+iOCkA/SEykHHOHyMvEeH7NlB2I5FIV1jE2eWdbBuJzYZQmu6JBUu6Gmx56cCAwEA
+AQKCAgBh8MQHbp42r7B4bwhEsgIP5868kaXZMYxiIjY+ZojI22CQSrQMj0oihmnO
+Dhu//Z9k8ewHOj+AkHtuXHe70FB3knECiEhHEEqWxzwgE5EKYhBrBgzDfRGkW7E5
+ItnmfZVopxaKr8uvu/yUM8LCFgDPDOopcWxo4SfkYGoD3cAtuvVBj98XBsN+G9DP
+cIpS07p5u1RheoYH5Ef2Me6dXqq5eMJdDxNdQMIg4wpIZS4hWM+dTcv8pd3e4+vt
+iCivCeVK/8mCtOH9P5Cv0B4Ac1zGu93AUEhXPcurCVXoiyZ/gyJJN9dZLlflfyFI
+qu7eOpot8jHnEL0cepB8Qhn0LlQTuv6rjJqmnl3tJA3S6rcM/mOjihkk1uo7JdDK
+vH498XR5qZPDlXZ8PVu3nI5EgXpmFIcCBuuVFS5QI63NZ32YqoGYXK37K7C9lQsL
+L/iR+YpwuQqDmM+UEETjBCIMKvxghFH0ICR041yg9tkjRhNKCAGc6n70wQDUq57s
+jjZmTQ4ZydxCsWVjLo7fCcoyQ9B7IUGPUUn8WavPUwtz1kG6VK7RDGa0KtgCD0vc
+iEwbWi9uwkZdoZdHcB8qLgCPjMGgRJLOyJ67xQ0RP+r+WkhUAjYcaucFonyyBhtv
+OrqNyEM3SEpgrzgttyyg+dP/cDvPbp4NXoxKAMyd8c7mjPploQKCAQEA+BL/qxLe
+LTKwe3TKpjAeyTB2iOxoWjtCqe3/xKbTS32Tn/VGwqhXyNeh+FTRhQu7fg5iL2C2
+JCOdYXWxRYIBwUh4HfApkgYzznUAU2vOh653MzW6LsOtDdgYF2cijN1ZFcbRTGpw
+eoA6U/cijuglwpTHF7zmRd9rSsv+PZ/fTDgY82MOdeaOUwyKuVyPUpNWfqSwxPd9
+tWEdOYjgq1llPbl1mktR0gYHIdHcSr1By7kmFw3/TQuic5Nm+FDidtfJYO36xFI1
+/CfwGVYeH42iul+KzdlITLAMRm2PAcWFjvxpw78T+xeUNpZlnZSgCIjtpfjywmXb
+uQvJoMMEX5PN1wKCAQEA0PIx4sgXiwqASa/foBB0Tk5jG3QWxucpqnLJURZeRqLQ
+BmF4WRrjs5V2y6iizegIcNmL0ZfwFBU79HwtAgFiTELLQL2xivhpMVjXL7QHeE4r
+A/9+49iO8wu8W/hwKxCDdGqXKyCKtW9b1yfUVB09j29GtApcV9v8KCTmDwYGrHI0
+DcEMtNLUbJvUeWFYFadJNFKxbsBtJPJIrYaiIyv2sL+Y3tZrYES72tTAYhMFwd0r
+9ooL5Ufrpuh4pHOxxA0Sh0EVUhNmyoq/ZJZ5wia+WB5NXBSD9JbciC5M4J8BMl/u
+Bx5RZbJSoAktYiOzev//6NHUmXsDjg3Kh9P48JVasQKCAQBVjt/k1bYQ6pmZirdV
+x+TmSLOpF7gJ3sRoLTB4V30qXR4sHgEQo9Ta7RvstPwqIdjBah6M7pMDNdFSyq+g
+JG2Mhvz+flUoCsGVZB7/pn/tpctwuwgClvQ5gR0V/TkaUkEmVJLdAxzV8yGq0eJ2
+XTSgvoVH95uH3712Z5LBGEGAXRyl3LUhDqpplDrIIVdBCJXdSdm5pQ4TH3Jf5Ihw
+MH3NYwhfdbi7cd7F2EZc9Jcbtzie3PH/VZLqv5zU6bihelz29Dz3ts7tr6yMYHo1
+Mbk9BDSwOE9KO7GQHLskxkYBAadMnrs6b3Brv0U+qwLizq7//jNjvpOgZ6Nbscbx
+W92zAoIBAQCNCK17cavSgggNtNSw6epXYLmssjMdlrKdBlW0kfCYpRTc+bWOD4Ra
+lyxUU0Nw0InCAlVJ59B4/cw2PgrzK5P5/avLyz6nmv0F/f1hiZbxMXH/hNlVWbtD
+ekxtl8e+iarxTXEz/wchaEUJeSzsicAfrPCAXe3ur+IIBr/yrBKdG4jfL8sv0o7n
+sFc+huI522yiEJ8LLn99TLyZxCJ0sxwUOX8qCnj3xe02zBv/Fu/v5yXhh1R4Mo9x
+XcDw39bBikFTYi7N86KSXAzMDHWrAxO/ztRQrthSo/G/SeFCTJE2O2IjE+fFSRRU
+SV2EvKxM/bbyo49o+YtwuwZVoFKLsYRBAoIBADaL9sx49XTHIIFGqEQP7NLEhs7D
+eJgSKP5oQ54J0iaoVpsoxng8DrTBkMVW75hiWzTW75EJnMXrauo/YfAbvsMM//3e
+BfRWvYpS5xKcHmXg2QJxy2VpvElHLg5Y2lligEZhO+5Sm2OG/hixBmiFvEvxPEB8
+8YIvYKcRAGA/HgDY9hGWSNsBP7qDXWP5kRm8qnB6zn33TVZMsXwUv6TP0cwsBKf7
+XDbnPBpOQK9nicehY7oscy9yTB9Q3bUHecYLY822ueCwaJgwJWFUH+Xe4u6xIH5l
+A/IyIfyOqxjUc34Me+37ehNmbTIxZ1BqLddppm9QsSAD7cDMurfb3pRpju4=
+-----END RSA PRIVATE KEY-----]],
+
+ dhparam = [[-----BEGIN DH PARAMETERS-----
+MIIBDAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAgICAOE=
+-----END DH PARAMETERS-----]],
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/stress_generator.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/stress_generator.lua
new file mode 100644
index 00000000..dab93e6d
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/stress_generator.lua
@@ -0,0 +1,151 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local stress_generator = {}
+stress_generator.__index = stress_generator
+
+
+local cjson = require "cjson"
+local pl_file = require "pl.file"
+local pl_path = require "pl.path"
+local uuid = require "resty.jit-uuid"
+
+
+local fmt = string.format
+local tmp_root = os.getenv("TMPDIR") or "/tmp"
+
+
+-- we need this to get random UUIDs
+math.randomseed(os.time())
+
+
+local attack_cmds = {
+ ["http"] = "GET http://%s:%d%s",
+}
+
+
+function stress_generator.is_running(self)
+ if self.finish_time == nil or self.finish_time <= os.time() then
+ return false
+ end
+
+ return true
+end
+
+
+function stress_generator.get_results(self)
+ if self.results ~= nil then
+ return self.results
+ end
+
+ if self.results_filename == nil then
+ return nil, "stress_generator was not run yet"
+ end
+
+ if stress_generator:is_running() then
+ return nil, "stress_generator results not available yet"
+ end
+
+ local report_cmd = fmt("vegeta report -type=json %s 2>&1", self.results_filename)
+ local report_pipe = io.popen(report_cmd)
+ local output = report_pipe:read('*all')
+ report_pipe:close()
+
+ if pl_path.exists(self.results_filename) then
+ pl_file.delete(self.results_filename)
+ end
+
+
+ local vegeta_results = cjson.decode(output)
+ local results = {
+ ["successes"] = 0,
+ ["remote_failures"] = 0,
+ ["proxy_failures"] = 0,
+ ["failures"] = 0,
+ }
+
+ vegeta_results.status_codes = vegeta_results.status_codes or {}
+
+ for status, count in pairs(vegeta_results.status_codes) do
+ if status == "200" then
+ results.successes = count
+ elseif status == "502" or status == "504" then
+ results.remote_failures = results.remote_failures + count
+ results.failures = results.failures + count
+ elseif status == "500" or status == "503" then
+ results.proxy_failures = results.proxy_failures + count
+ results.failures = results.failures + count
+ else
+ results.failures = results.failures + count
+ end
+ end
+
+ self.results = results
+
+ if self.debug then
+ -- show pretty results
+ local report_cmd = fmt("vegeta report %s 2>&1", self.results_filename)
+ local report_pipe = io.popen(report_cmd)
+ local output = report_pipe:read('*all')
+ report_pipe:close()
+ print(output)
+ end
+
+ return self.results
+end
+
+
+function stress_generator.run(self, uri, headers, duration, rate)
+ if stress_generator:is_running() then
+ return nil, "already running"
+ end
+
+ self.results_filename = fmt("%s/vegeta_%s", tmp_root, uuid())
+
+ duration = duration or 1
+ rate = rate or 100
+ local attack_cmd = fmt(attack_cmds[self.protocol], self.host, self.port, uri)
+ local req_headers = ""
+
+ for name, value in pairs(headers) do
+ req_headers = fmt("-header=%s:%s %s", name, value, req_headers)
+ end
+
+ local vegeta_cmd = fmt(
+ "echo %s | vegeta attack %s -rate=%d -duration=%ds -workers=%d -timeout=5s -output=%s",
+ attack_cmd, req_headers, rate, duration, self.workers, self.results_filename)
+
+ self.pipe = io.popen(vegeta_cmd)
+ -- we will rely on vegeta's duration
+ self.finish_time = os.time() + duration
+end
+
+
+function stress_generator.new(protocol, host, port, workers, debug)
+ if io.popen == nil then
+ error("stress_generator is not supported in this platform", 2)
+ end
+
+ local self = setmetatable({}, stress_generator)
+
+ protocol = protocol or "http"
+
+ if protocol ~= "http" then
+ error("stress_generator supports only http")
+ end
+
+ self.debug = debug == true
+ self.host = host or "127.0.0.1"
+ self.port = port or "80"
+ self.protocol = protocol
+ self.workers = workers or 10
+
+ return self
+end
+
+
+return stress_generator
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_custom_inject_http.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_custom_inject_http.lua
new file mode 100644
index 00000000..02ea74e8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_custom_inject_http.lua
@@ -0,0 +1,271 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return [[
+lua_shared_dict kong_mock_upstream_loggers 10m;
+
+> if role ~= "data_plane" then
+ server {
+ server_name mock_upstream;
+
+ listen 15555 reuseport;
+ listen 15556 ssl reuseport;
+
+> for i = 1, #ssl_cert do
+ ssl_certificate $(ssl_cert[i]);
+ ssl_certificate_key $(ssl_cert_key[i]);
+> end
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ set_real_ip_from 127.0.0.1;
+
+ location / {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ ngx.status = 404
+ return mu.send_default_json_response()
+ }
+ }
+
+ location = / {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({
+ valid_routes = {
+ ["/ws"] = "Websocket echo server",
+ ["/get"] = "Accepts a GET request and returns it in JSON format",
+ ["/xml"] = "Returns a simple XML document",
+ ["/post"] = "Accepts a POST request and returns it in JSON format",
+ ["/response-headers?:key=:val"] = "Returns given response headers",
+ ["/cache/:n"] = "Sets a Cache-Control header for n seconds",
+ ["/anything"] = "Accepts any request and returns it in JSON format",
+ ["/request"] = "Alias to /anything",
+ ["/delay/:duration"] = "Delay the response for seconds",
+ ["/basic-auth/:user/:pass"] = "Performs HTTP basic authentication with the given credentials",
+ ["/status/:code"] = "Returns a response with the specified ",
+ ["/stream/:num"] = "Stream chunks of JSON data via chunked Transfer Encoding",
+ },
+ })
+ }
+ }
+
+ location = /ws {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.serve_web_sockets()
+ }
+ }
+
+ location = /ws/log {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.retrieve_ws_log()
+ }
+ }
+
+ location /get {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_method("GET")
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response()
+ }
+ }
+
+ location /xml {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local xml = [=[
+
+
+ Kong, Monolith destroyer.
+
+ ]=]
+ return mu.send_text_response(xml, "application/xml")
+ }
+ }
+
+ location /post {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_method("POST")
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response()
+ }
+ }
+
+ location = /response-headers {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_method("GET")
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({}, ngx.req.get_uri_args(0))
+ }
+ }
+
+ location = /hop-by-hop {
+ content_by_lua_block {
+ local header = ngx.header
+ header["Keep-Alive"] = "timeout=5, max=1000"
+ header["Proxy"] = "Remove-Me"
+ header["Proxy-Connection"] = "close"
+ header["Proxy-Authenticate"] = "Basic"
+ header["Proxy-Authorization"] = "Basic YWxhZGRpbjpvcGVuc2VzYW1l"
+ header["Content-Length"] = nil
+ header["TE"] = "trailers, deflate;q=0.5"
+ header["Trailer"] = "Expires"
+ header["Upgrade"] = "example/1, foo/2"
+
+ ngx.print("hello\r\n\r\nExpires: Wed, 21 Oct 2015 07:28:00 GMT\r\n\r\n")
+ ngx.exit(200)
+ }
+ }
+
+ location ~ "^/cache/(?\d+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({}, {
+ ["Cache-Control"] = "public, max-age=" .. ngx.var.n,
+ })
+ }
+ }
+
+ location ~ "^/basic-auth/(?[a-zA-Z0-9_]+)/(?.+)$" {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_basic_auth(ngx.var.username,
+ ngx.var.password)
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response({
+ authenticated = true,
+ user = ngx.var.username,
+ })
+ }
+ }
+
+ location ~ "^/(request|anything)" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.send_default_json_response()
+ }
+ }
+
+ location ~ "^/delay/(?\d{1,3})$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local delay_seconds = tonumber(ngx.var.delay_seconds)
+ if not delay_seconds then
+ return ngx.exit(ngx.HTTP_NOT_FOUND)
+ end
+
+ ngx.sleep(delay_seconds)
+
+ return mu.send_default_json_response({
+ delay = delay_seconds,
+ })
+ }
+ }
+
+ location ~ "^/status/(?\d{3})$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local code = tonumber(ngx.var.code)
+ if not code then
+ return ngx.exit(ngx.HTTP_NOT_FOUND)
+ end
+ ngx.status = code
+ return mu.send_default_json_response({
+ code = code,
+ })
+ }
+ }
+
+ location ~ "^/stream/(?\d+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ local rep = tonumber(ngx.var.num)
+ local res = require("cjson").encode(mu.get_default_json_response())
+
+ ngx.header["X-Powered-By"] = "mock_upstream"
+ ngx.header["Content-Type"] = "application/json"
+
+ for i = 1, rep do
+ ngx.say(res)
+ end
+ }
+ }
+
+ location ~ "^/post_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.store_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/post_auth_log/(?[a-z0-9_]+)/(?[a-zA-Z0-9_]+)/(?.+)$" {
+ access_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.filter_access_by_basic_auth(ngx.var.username,
+ ngx.var.password)
+ }
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.store_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/read_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.retrieve_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/count_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.count_log(ngx.var.logname)
+ }
+ }
+
+ location ~ "^/reset_log/(?[a-z0-9_]+)$" {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.reset_log(ngx.var.logname)
+ }
+ }
+
+ location = /echo_sni {
+ return 200 'SNI=$ssl_server_name\n';
+ }
+
+ location = /ocsp {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.handle_ocsp()
+ }
+ }
+
+ location = /set_ocsp {
+ content_by_lua_block {
+ local mu = require "spec.fixtures.mock_upstream"
+ return mu.set_ocsp(ngx.var.arg_status)
+ }
+ }
+ }
+> end -- role ~= "data_plane"
+
+ include '*.http_mock';
+]]
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_custom_inject_stream.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_custom_inject_stream.lua
new file mode 100644
index 00000000..92748b52
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_custom_inject_stream.lua
@@ -0,0 +1,61 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return [[
+server {
+ listen 15557;
+ listen 15558 ssl;
+ listen 15557 udp;
+
+> for i = 1, #ssl_cert do
+ ssl_certificate $(ssl_cert[i]);
+ ssl_certificate_key $(ssl_cert_key[i]);
+> end
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ content_by_lua_block {
+ local sock = assert(ngx.req.socket())
+ local data = sock:receive() -- read a line from downstream
+
+ if string.find(data, "get_sni") then
+ sock:send(ngx.var.ssl_server_name)
+ sock:send("\n")
+ return
+ end
+
+ if ngx.var.protocol == "TCP" then
+ ngx.say(data)
+
+ else
+ sock:send(data) -- echo whatever was sent
+ end
+ }
+}
+
+include '*.stream_mock';
+
+> if cluster_ssl_tunnel then
+server {
+ listen unix:${{PREFIX}}/cluster_proxy_ssl_terminator.sock;
+
+ proxy_pass ${{cluster_ssl_tunnel}};
+ proxy_ssl on;
+ # as we are essentially talking in HTTPS, passing SNI should default turned on
+ proxy_ssl_server_name on;
+> if proxy_server_ssl_verify then
+ proxy_ssl_verify on;
+> if lua_ssl_trusted_certificate_combined then
+ proxy_ssl_trusted_certificate '${{LUA_SSL_TRUSTED_CERTIFICATE_COMBINED}}';
+> end
+ proxy_ssl_verify_depth 5; # 5 should be sufficient
+> else
+ proxy_ssl_verify off;
+> end
+ proxy_socket_keepalive on;
+}
+> end -- cluster_ssl_tunnel
+]]
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_tcp_echo_server_custom_inject_stream.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_tcp_echo_server_custom_inject_stream.lua
new file mode 100644
index 00000000..2479a426
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/template_inject/nginx_kong_test_tcp_echo_server_custom_inject_stream.lua
@@ -0,0 +1,30 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+return [[
+server {
+ listen 8188;
+ listen 8189 ssl;
+
+> for i = 1, #ssl_cert do
+ ssl_certificate $(ssl_cert[i]);
+ ssl_certificate_key $(ssl_cert_key[i]);
+> end
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ content_by_lua_block {
+ local sock = assert(ngx.req.socket())
+ local data = sock:receive() -- read a line from downstream
+ if data then
+ sock:send(data.."\n") -- echo whatever was sent
+ ngx.log(ngx.INFO, "received data: " .. data)
+ else
+ ngx.log(ngx.WARN, "Nothing received")
+ end
+ }
+}
+]]
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/bad_client.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/bad_client.crt
new file mode 100644
index 00000000..3f43c148
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/bad_client.crt
@@ -0,0 +1,26 @@
+openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -keyout spec/fixtures/bad_client.key -out spec/fixtures/bad_client.crt -subj '/C=US/ST=California/L=San Francisco/O=Kong Inc/OU=Kong Inc/CN=test.com/emailAddress=test@test.test/'
+
+-----BEGIN CERTIFICATE-----
+MIIEBzCCAu+gAwIBAgIUEBt2Elq2RETO+maonV9goQDotrEwDQYJKoZIhvcNAQEL
+BQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1TYW4gRnJhbmNpc2NvMREwDwYDVQQKDAhLb25nIEluYzERMA8GA1UECwwIS29u
+ZyBJbmMxETAPBgNVBAMMCHRlc3QuY29tMR0wGwYJKoZIhvcNAQkBFg50ZXN0QHRl
+c3QudGVzdDAeFw0yNDAxMDkwMzAzNTVaFw0zNDAxMDYwMzAzNTVaMIGSMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j
+aXNjbzERMA8GA1UECgwIS29uZyBJbmMxETAPBgNVBAsMCEtvbmcgSW5jMREwDwYD
+VQQDDAh0ZXN0LmNvbTEdMBsGCSqGSIb3DQEJARYOdGVzdEB0ZXN0LnRlc3QwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjryLxJf9xMRPXZTkV0S0Lylx7
+FCv/R5KD7p8gvnl+C/jgfZmguuUWhSpqOcmbQyeAS7hHB6p/J1mcCZK0M0hClQIg
+/76Tfc446yMfq8R35QBxb4ziMOBJUxYSc/PGl3jZaW1OqzCYGVMfszi3Nc1WCl5Q
+7cZbf5oPnmkMvKVWq8tlMFkjMekqJhzY3mdikCZY8rhu2ASqDDEYNrYWDPV2WJUa
+y0JnQgbyPu8Mb2idSASmS3eylPirr98/RqiVZ2qRW4qhlk/5iELN0CQ/c/jcLz4/
+MIahnPdBqvJk6heQ3/wIgf6X6hPXsDHZHO4h5zVM+WBdYBy1hQot83S3lOYpAgMB
+AAGjUzBRMB0GA1UdDgQWBBSa8yH5acw8T2Sd1PyiGzlAXBJEOjAfBgNVHSMEGDAW
+gBSa8yH5acw8T2Sd1PyiGzlAXBJEOjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQBPSaq+7ZlIsBomQHretkhp/bXgpUmtJHmWY/p0Ct/jzol19QKn
+9Dpi63LWCmt5qXYvr55k8QhCg+wHHpq4NrX1yZxK29mFnTV9ErhyeWBWs3Um+F6D
+QVdJWH9LGwWffE571shyirvLekrqQbt9h1jKB8a4MEg6ZzrbeCU4UcHrXGq8Nouv
+9jkgUttXj80lArisduvqxDCEJLlVEPlVeYt7TGNSbC1bMjqHU7LkrDiLIQ9B62CH
+H+qzk/f5I4yrEwb3vORdojLald3XG+RWZ4IgxqHlYesNvN0kmuWoSv96r9mVrFc2
+xYfbZi1AB9kQjVZtlsUct5NCFVk5/MTvPgpy
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/bad_client.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/bad_client.key
new file mode 100644
index 00000000..71b741fa
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/bad_client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjryLxJf9xMRPX
+ZTkV0S0Lylx7FCv/R5KD7p8gvnl+C/jgfZmguuUWhSpqOcmbQyeAS7hHB6p/J1mc
+CZK0M0hClQIg/76Tfc446yMfq8R35QBxb4ziMOBJUxYSc/PGl3jZaW1OqzCYGVMf
+szi3Nc1WCl5Q7cZbf5oPnmkMvKVWq8tlMFkjMekqJhzY3mdikCZY8rhu2ASqDDEY
+NrYWDPV2WJUay0JnQgbyPu8Mb2idSASmS3eylPirr98/RqiVZ2qRW4qhlk/5iELN
+0CQ/c/jcLz4/MIahnPdBqvJk6heQ3/wIgf6X6hPXsDHZHO4h5zVM+WBdYBy1hQot
+83S3lOYpAgMBAAECggEAGXqK3Iam+x5B5xFhOr1+CYvNAXY5CFpCZY717h7iwnVn
+9BRuSXqja5ym5t9qkQ8kTZrRFcgNCLs9/kVEihBBezu+l8G3ZItSAmzM4YKB6TfC
+7eVd71ze92BGMJvOomcJmgNdzxUOp15UdhFruU5X/BX5FcKCpyjMTl9rcrQaoyGH
+3LiO0l77INChxH3+SBsam+oZh8WEbhtOkbRqocTy+D3FUDW7jTu1VvMEQoBfvivR
+/xAv6E3itY7CwvrHKfFcgX54wAB4U1R+hb/jWxlQ+zHquQhnkbVZGvQhxCzglEXj
+ebiDnuIyVINapJtkOx7eTh4wrqczYKC+DPe5srkOEQKBgQC16OOsJJfMKLeQR1h7
+wvU/5ZsTCaoO/DQM21P9QN86atcxa4jqI4nGbwniFhdy6nub0TYf1vMw8OGg4s7h
+mBEkoywQzSPAC+aQeIhcyPi6bzMAuXM/KWxrlbrD4dOcRlvPmQy/QBOO1VaGYaTg
+RkJGzpt/qRUUemw4lhb6DMwK+wKBgQDmWe0GqO72f6XTm80esnmbSpIY6wI5Cvgc
+rxvlacAIAXt+tCsQFaGAdEZ4MidR6pYzW7y7/YxsmXnzXitNNjSNILw3yQpk3uOI
+QF/gNwZmkIOAzX+ywU85dCE/vHAzl3HxwcThmq2ihQBjGCh/2+atdOsstBhj4Zbc
+bLh6AN/KKwKBgEOEtoSdbG5Nqpx1rsT4/tiBm4Z8bs9zEsq22bzFSFXBdDAWrtHg
+8PFjI3L0Ki3wFIMoKwO0X72JmuWBabJYI/zKR7/QtHVCtYhoBI0T1yYzweWB7+HA
+uUpRX+n2tvlpMAik0RNncBry2lO0iGLqEQ2BADxpALcCHYr/QnX7K/zjAoGAYVqM
+NI5dQEy+lup4DIs9ngYsLN/V9qnOJsFTNug0wq1ttiULNicONK3vwojf2V0d0eLp
+p0IHgTZ0NgkMn/tSxnqZUbJj9DHHVCRw4OAgkmRtT5iKadANXajZDIjlmshc3eIZ
+BYhL7i3r9fWV2EPmAFUzt5b93+jNy6cfaGDKNDUCgYEAiIu32O9htG7kdLMB8Ejs
+O8MIbmRQNN5BUQHS3BBnQWpTsseX/1+MSuba7pO7//tn0zekXbwOxe4lB05JAfcZ
+zQK9Hzfu60oA2TTC6kaAFgHpVe7eJ265uzn12RFol8/NYJofOp3FD80pDLRZY6Ri
+HmiFMd2gGD/DWoJagi6ZMYM=
+-----END PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/client_example.com.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/client_example.com.crt
new file mode 100644
index 00000000..71d9e236
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/client_example.com.crt
@@ -0,0 +1,62 @@
+-----BEGIN CERTIFICATE-----
+MIIFIjCCAwqgAwIBAgICIAEwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzElMCMG
+A1UEAwwcS29uZyBUZXN0aW5nIEludGVybWlkaWF0ZSBDQTAeFw0xOTA1MDIyMDAz
+MTFaFw0yOTA0MjgyMDAzMTFaMFMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxGDAWBgNVBAMMD2Zvb0BleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJldMxsZHDxA
+RpbSXdIFZiTf8D0dYgsPnsmx5tVjA/zrVBSVBPO9KunaXNm4Z6JWmUwenzFGbzWP
+NLfbLn4khuoczzqSru5XfbyH1HrD0cd5lkf44Dw1/otfIFDBleiR/OWEiAxwS4zi
+xIajNyvLr3gC5dv+F+JuWpW1yVQxybIDQWoI25xpd3+ZkXO+OLkToo+YpuwIDlUj
+6Rkm5kbqoxDpaDihA2bsAqjNG7G+SHthaNyACsQsU/t6BHSWzHumScN0CxJ+TeVH
+fTZklelItZ6YP0B0RQjzvSGA423UgALzqJglGPe8UDjm3BMlg2xhTfnfy1J6Vmbt
+5jx6FOXUARsCAwEAAaOB8jCB7zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF
+oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBRTzNOmhGRXaZamxVfnlKXarIOEmDAfBgNVHSMEGDAW
+gBQLDgQOl/htYk8k8DvGb9IKO40RETAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw
+FAYIKwYBBQUHAwIGCCsGAQUFBwMEMCsGA1UdEQQkMCKBD2Zvb0BleGFtcGxlLmNv
+bYEPYmFyQGV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBziDuVjU0I1CwO
+b1Cx2TJpzi3l5FD/ozrMZT6F3EpkJFGZWgXrsXHz/0qKTrsbB2m3/fcyd0lwQ5Lh
+fz8X1HPrwXa3BqZskNu1vOUNiqAYWvQ5gtbpweJ96LzMSYVGLK78NigYTtK+Rgq3
+As5CVfLXDBburrQNGyRTsilCQDNBvIpib0eqg/HJCNDFMPrBzTMPpUutyatfpFH2
+UwTiVBfA14YYDxZaetYWeksy28XH6Uj0ylyz67VHND+gBMmQNLXQHJTIDh8JuIf2
+ec6o4HrtyyuRE3urNQmcPMAokacm4NKw2+og6Rg1VS/pckaSPOlSEmNnKFiXStv+
+AVd77NGriUWDFCmnrFNOPOIS019W0oOk6YMwTUDSa86Ii6skCtBLHmp/cingkTWg
+7KEbdT1uVVPgseC2AFpQ1BWJOjjtyW3GWuxERIhuab9/ckTz6BuIiuK7mfsvPBrn
+BqjZyt9WAx8uaWMS/ZrmIj3fUXefaPtl27jMSsiU5oi2vzFu0xiXJb6Jr7RQxD3O
+XRnycL/chWnp7eVV1TQS+XzZ3ZZQIjckDWX4E+zGo4o9pD1YC0eytbIlSuqYVr/t
+dZmD2gqju3Io9EXPDlRDP2VIX9q1euF9caz1vpLCfV+F8wVPtZe5p6JbNugdgjix
+nDZ2sD2xGXy6/fNG75oHveYo6MREFw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFmjCCA4KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzEdMBsG
+A1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcNMTkwNTAyMTk0MDQ4WhcNMjkw
+NDI5MTk0MDQ4WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEV
+MBMGA1UECgwMS29uZyBUZXN0aW5nMSUwIwYDVQQDDBxLb25nIFRlc3RpbmcgSW50
+ZXJtaWRpYXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0dnj
+oHlJmNM94vQnK2FIIQJm9OAVvyMtAAkBKL7Cxt8G062GHDhq6gjQ9enuNQE0l3Vv
+mSAh7N9gNlma6YbRB9VeG54BCuRQwCxveOBiwQvC2qrTzYI34kF/AeflrDOdzuLb
+zj5cLADKXGCbGDtrSPKUwdlkuLs3pRr/YAyIQr7zJtlLz+E0GBYp0GWnLs0FiLSP
+qSBWllC9u8gt2MiKyNlXw+kZ8lofOehCJzfFr6qagVklPw+8IpU6OGmRLFQVwVhp
+zdAJmAGmSo/AGNKGqDdjzC4N2l4uYGH6n2KmY2yxsLBGZgwtLDst3fK4a3Wa5Tj7
+cUwCcGLGtfVTaIXZYbqQ0nGsaYUd/mhx3B3Jk1p3ILZ72nVYowhpj22ipPGal5hp
+ABh1MX3s/B+2ybWyDTtSaspcyhsRQsS6axB3DwLOLRy5Xp/kqEdConCtGCsjgm+U
+FzdupubXK+KIAmTKXDx8OM7Af/K7kLDfFTre40sEB6fwrWwH8yFojeqkA/Uqhn5S
+CzB0o4F3ON0xajsw2dRCziiq7pSe6ALLXetKpBr+xnVbUswH6BANUoDvh9thVPPx
+1trkv+OuoJalkruZaT+38+iV9xwdqxnR7PUawqSyvrEAxjqUo7dDPsEuOpx1DJjO
+XwRJCUjd7Ux913Iks24BqpPhEQz/rZzJLBApRVsCAwEAAaNmMGQwHQYDVR0OBBYE
+FAsOBA6X+G1iTyTwO8Zv0go7jRERMB8GA1UdIwQYMBaAFAdP8giF4QLaR0HEj9N8
+apTFYnD3MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4ICAQAWzIvIVM32iurqM451Amz0HNDG9j84cORnnaRR5opFTr3P
+EqI3QkgCyP6YOs9t0QSbA4ur9WUzd3c9Ktj3qRRgTE+98JBOPO0rv+Kjj48aANDV
+5tcbI9TZ9ap6g0jYr4XNT+KOO7E8QYlpY/wtokudCUDJE9vrsp1on4Bal2gjvCdh
+SU0C1lnj6q6kBdQSYHrcjiEIGJH21ayVoNaBVP/fxyCHz472w1xN220dxUI/GqB6
+pjcuy9cHjJHJKJbrkdt2eDRAFP5cILXc3mzUoGUDHY2JA1gtOHV0p4ix9R9AfI9x
+snBEFiD8oIpcQay8MJH/z3NLEPLoBW+JaAAs89P+jcppea5N9vbiAkrPi687BFTP
+PWPdstyttw6KrvtPQR1+FsVFcGeTjo32/UrckJixdiOEZgHk+deXpp7JoRdcsgzD
++okrsG79/LgS4icLmzNEp0IV36QckEq0+ALKDu6BXvWTkb5DB/FUrovZKJgkYeWj
+GKogyrPIXrYi725Ff306124kLbxiA+6iBbKUtCutQnvut78puC6iP+a2SrfsbUJ4
+qpvBFOY29Mlww88oWNGTA8QeW84Y1EJbRkHavzSsMFB73sxidQW0cHNC5t9RCKAQ
+uibeZgK1Yk7YQKXdvbZvXwrgTcAjCdbppw2L6e0Uy+OGgNjnIps8K460SdaIiA==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/client_example.com.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/client_example.com.key
new file mode 100644
index 00000000..fb823431
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-handshake-modifier/client_example.com.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAmV0zGxkcPEBGltJd0gVmJN/wPR1iCw+eybHm1WMD/OtUFJUE
+870q6dpc2bhnolaZTB6fMUZvNY80t9sufiSG6hzPOpKu7ld9vIfUesPRx3mWR/jg
+PDX+i18gUMGV6JH85YSIDHBLjOLEhqM3K8uveALl2/4X4m5albXJVDHJsgNBagjb
+nGl3f5mRc744uROij5im7AgOVSPpGSbmRuqjEOloOKEDZuwCqM0bsb5Ie2Fo3IAK
+xCxT+3oEdJbMe6ZJw3QLEn5N5Ud9NmSV6Ui1npg/QHRFCPO9IYDjbdSAAvOomCUY
+97xQOObcEyWDbGFN+d/LUnpWZu3mPHoU5dQBGwIDAQABAoIBAQCLqQzeM3q7/4iI
+1l+r31DKacgbz4M2MW5XnJNqZTX/f8pcx+vvjqfiuADwH/b4JcaKRCSSOeMSMiw+
+9fGb2+WkksHARE3bLH+LTWKYvXRvI/FP73s8Oato/iKuh+vdE/zqgktmkGisjuGK
+/l1Cm8VaE8GBGh5kDDyfsyD5dDGJ0fYzJkfQqygd5B5TSaWflQsB//AXvHzkNy+G
+RHbrMl7t9rDCTtwnefSEJIEwAZerGKV0p+VlRy23mQLwxTxJ5jEjVvcFIMalnD4R
+nKaZYb3LgkCCTQ5Lw/xrkdAEJwfafhdu1CmvKelv1qpcz1vJdrFSfX5NOYS/93jI
+aKJT8Nm5AoGBAMmOOUTvbUd9nlbZXYGLTsoy+qA+OhLkB59krddH4mFoRvbggD6U
+Y/h7O/fA4spqtts+aVoEh/jyuwGTxMn0NPLjD0G8YZr1A4GUx1hgEQ1NIWvRlXrX
+s1bgIlaOc14hOpKf0to3mIovyhRm8PaDbQfHWfyl4yKtFgKiO4OCMK0/AoGBAMLK
+e9C5sedDKt8ESycaXnui1hA4WQkeMdXFflnabuhE29dkC7kDFEVurlmsQb3zFa0V
+dF40niT7xYqzdEJIbaZ3JZIrSFhnPSSBna+B1FjMhTVb/5sjPJS87BvjVYiZd5GY
+5Az4RgSlU3PlsaiuR95NH1vDxHXb5GcMs/EfnEklAoGBAIVFe2yve+yXjUkT9RYh
+TPm596pZOwEeskOcyK3epDuQPcwj6eh3Khs1MRPDALKjGUGi5PpWoKnlpe2HDcoT
+pacsp/vpWgiiFa1q+NzguKW46G5oaJSPZ8/75/ifvHzzL82fzEXqGPzWWKJg5te5
+UzCfikraTXOySyl2qC9uuEz1AoGBAILH8eNMmb48YW9EcbS6Ro9Z38EaI+U0SZ9O
+LqvjNS1q9fMiL6CzCYwoaJS6S5VdvMLtsaiCSV9pTtL182uBN2VZf3co6jS4c9ur
+zpQEZe6Mui7+KpodSVJPmXKL6mSBLT8q2IpAsrnxyhr5L5OiF4yQWSqCQMgkr6/k
+XnfYklSlAoGBAKBePjIdBGLy3ckdlTfbuTeO3kp2eZFBDtGzxP515+LcoRfOjd8T
+ZDX/porUMcgbtIF/B4SNso+8D/aHTCg3QAo6nDjFFjUBHhftgy+GP3BFfMvjqou6
+utJFRkc3FvrrkkeWHnyDQrPmAHjar94/xq1k1Vo+KQHQVQOrvtQt6KXK
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/bad_tls_client.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/bad_tls_client.crt
new file mode 100644
index 00000000..3f43c148
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/bad_tls_client.crt
@@ -0,0 +1,26 @@
+openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -keyout spec/fixtures/bad_client.key -out spec/fixtures/bad_client.crt -subj '/C=US/ST=California/L=San Francisco/O=Kong Inc/OU=Kong Inc/CN=test.com/emailAddress=test@test.test/'
+
+-----BEGIN CERTIFICATE-----
+MIIEBzCCAu+gAwIBAgIUEBt2Elq2RETO+maonV9goQDotrEwDQYJKoZIhvcNAQEL
+BQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1TYW4gRnJhbmNpc2NvMREwDwYDVQQKDAhLb25nIEluYzERMA8GA1UECwwIS29u
+ZyBJbmMxETAPBgNVBAMMCHRlc3QuY29tMR0wGwYJKoZIhvcNAQkBFg50ZXN0QHRl
+c3QudGVzdDAeFw0yNDAxMDkwMzAzNTVaFw0zNDAxMDYwMzAzNTVaMIGSMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j
+aXNjbzERMA8GA1UECgwIS29uZyBJbmMxETAPBgNVBAsMCEtvbmcgSW5jMREwDwYD
+VQQDDAh0ZXN0LmNvbTEdMBsGCSqGSIb3DQEJARYOdGVzdEB0ZXN0LnRlc3QwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjryLxJf9xMRPXZTkV0S0Lylx7
+FCv/R5KD7p8gvnl+C/jgfZmguuUWhSpqOcmbQyeAS7hHB6p/J1mcCZK0M0hClQIg
+/76Tfc446yMfq8R35QBxb4ziMOBJUxYSc/PGl3jZaW1OqzCYGVMfszi3Nc1WCl5Q
+7cZbf5oPnmkMvKVWq8tlMFkjMekqJhzY3mdikCZY8rhu2ASqDDEYNrYWDPV2WJUa
+y0JnQgbyPu8Mb2idSASmS3eylPirr98/RqiVZ2qRW4qhlk/5iELN0CQ/c/jcLz4/
+MIahnPdBqvJk6heQ3/wIgf6X6hPXsDHZHO4h5zVM+WBdYBy1hQot83S3lOYpAgMB
+AAGjUzBRMB0GA1UdDgQWBBSa8yH5acw8T2Sd1PyiGzlAXBJEOjAfBgNVHSMEGDAW
+gBSa8yH5acw8T2Sd1PyiGzlAXBJEOjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQBPSaq+7ZlIsBomQHretkhp/bXgpUmtJHmWY/p0Ct/jzol19QKn
+9Dpi63LWCmt5qXYvr55k8QhCg+wHHpq4NrX1yZxK29mFnTV9ErhyeWBWs3Um+F6D
+QVdJWH9LGwWffE571shyirvLekrqQbt9h1jKB8a4MEg6ZzrbeCU4UcHrXGq8Nouv
+9jkgUttXj80lArisduvqxDCEJLlVEPlVeYt7TGNSbC1bMjqHU7LkrDiLIQ9B62CH
+H+qzk/f5I4yrEwb3vORdojLald3XG+RWZ4IgxqHlYesNvN0kmuWoSv96r9mVrFc2
+xYfbZi1AB9kQjVZtlsUct5NCFVk5/MTvPgpy
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/bad_tls_client.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/bad_tls_client.key
new file mode 100644
index 00000000..71b741fa
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/bad_tls_client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjryLxJf9xMRPX
+ZTkV0S0Lylx7FCv/R5KD7p8gvnl+C/jgfZmguuUWhSpqOcmbQyeAS7hHB6p/J1mc
+CZK0M0hClQIg/76Tfc446yMfq8R35QBxb4ziMOBJUxYSc/PGl3jZaW1OqzCYGVMf
+szi3Nc1WCl5Q7cZbf5oPnmkMvKVWq8tlMFkjMekqJhzY3mdikCZY8rhu2ASqDDEY
+NrYWDPV2WJUay0JnQgbyPu8Mb2idSASmS3eylPirr98/RqiVZ2qRW4qhlk/5iELN
+0CQ/c/jcLz4/MIahnPdBqvJk6heQ3/wIgf6X6hPXsDHZHO4h5zVM+WBdYBy1hQot
+83S3lOYpAgMBAAECggEAGXqK3Iam+x5B5xFhOr1+CYvNAXY5CFpCZY717h7iwnVn
+9BRuSXqja5ym5t9qkQ8kTZrRFcgNCLs9/kVEihBBezu+l8G3ZItSAmzM4YKB6TfC
+7eVd71ze92BGMJvOomcJmgNdzxUOp15UdhFruU5X/BX5FcKCpyjMTl9rcrQaoyGH
+3LiO0l77INChxH3+SBsam+oZh8WEbhtOkbRqocTy+D3FUDW7jTu1VvMEQoBfvivR
+/xAv6E3itY7CwvrHKfFcgX54wAB4U1R+hb/jWxlQ+zHquQhnkbVZGvQhxCzglEXj
+ebiDnuIyVINapJtkOx7eTh4wrqczYKC+DPe5srkOEQKBgQC16OOsJJfMKLeQR1h7
+wvU/5ZsTCaoO/DQM21P9QN86atcxa4jqI4nGbwniFhdy6nub0TYf1vMw8OGg4s7h
+mBEkoywQzSPAC+aQeIhcyPi6bzMAuXM/KWxrlbrD4dOcRlvPmQy/QBOO1VaGYaTg
+RkJGzpt/qRUUemw4lhb6DMwK+wKBgQDmWe0GqO72f6XTm80esnmbSpIY6wI5Cvgc
+rxvlacAIAXt+tCsQFaGAdEZ4MidR6pYzW7y7/YxsmXnzXitNNjSNILw3yQpk3uOI
+QF/gNwZmkIOAzX+ywU85dCE/vHAzl3HxwcThmq2ihQBjGCh/2+atdOsstBhj4Zbc
+bLh6AN/KKwKBgEOEtoSdbG5Nqpx1rsT4/tiBm4Z8bs9zEsq22bzFSFXBdDAWrtHg
+8PFjI3L0Ki3wFIMoKwO0X72JmuWBabJYI/zKR7/QtHVCtYhoBI0T1yYzweWB7+HA
+uUpRX+n2tvlpMAik0RNncBry2lO0iGLqEQ2BADxpALcCHYr/QnX7K/zjAoGAYVqM
+NI5dQEy+lup4DIs9ngYsLN/V9qnOJsFTNug0wq1ttiULNicONK3vwojf2V0d0eLp
+p0IHgTZ0NgkMn/tSxnqZUbJj9DHHVCRw4OAgkmRtT5iKadANXajZDIjlmshc3eIZ
+BYhL7i3r9fWV2EPmAFUzt5b93+jNy6cfaGDKNDUCgYEAiIu32O9htG7kdLMB8Ejs
+O8MIbmRQNN5BUQHS3BBnQWpTsseX/1+MSuba7pO7//tn0zekXbwOxe4lB05JAfcZ
+zQK9Hzfu60oA2TTC6kaAFgHpVe7eJ265uzn12RFol8/NYJofOp3FD80pDLRZY6Ri
+HmiFMd2gGD/DWoJagi6ZMYM=
+-----END PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/ca.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/ca.crt
new file mode 100644
index 00000000..a60fd5d9
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/ca.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFoTCCA4mgAwIBAgIUQDBLwIychoRbVRO44IzBBk9R4oYwDQYJKoZIhvcNAQEL
+BQAwWDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoM
+DEtvbmcgVGVzdGluZzEdMBsGA1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcN
+MTkwNTAyMTkzNDQyWhcNMzkwNDI3MTkzNDQyWjBYMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMS29uZyBUZXN0aW5nMR0wGwYDVQQD
+DBRLb25nIFRlc3RpbmcgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
+AgoCggIBAMp6IggUp3aSNRbLAac8oOkrbUnFuxtlKGYgg8vfA2UU71qTktigdwO6
+Kod0/M+daO3RDqJJXQL2rD14NDO3MaextICanoQSEe+nYyMFUIk+QplXLD3fbshU
+nHoJcMS2w0x4cm1os4ebxR2Evndo6luz39ivcjau+BL+9iBAYL1g6+eGOjcSy7ft
+1nAMvbxcQ7dmbAH2KP6OmF8cok+eQWVqXEjqtVx5GDMDlj1BjX6Kulmh/vhNi3Hr
+NEi+kPrw/YtRgnqnN0sv3NnAyKnantxy7w0TDicFjiBsSIhjB5aUfWYErBR+Nj/m
+uumwc/kRJcHWklqDzxrZKCIyOyWcE5Dyjjr46cnF8HxhYwgZcwkmgTtaXOLpBMlo
+XUTgOQrWpm9HYg2vOJMMA/ZPUJ2tJ34/4RgiA00EJ5xG8r24suZmT775l+XFLFzp
+Ihxvs3BMbrWsXlcZkI5neNk7Q/1jLoBhWeTYjMpUS7bJ/49YVGQZFs3xu2IcLqeD
+5WsB1i+EqBAI0jm4vWEynsyX+kS2BqAiDtCsS6WYT2q00DTeP5eIHh/vHsm75jJ+
+yUEb1xFxGnNevLKNTcHUeXxPUnowdC6wqFnaJm7l09qVGDom7tLX9i6MCojgpAP0
+hMpBxzh8jLxHh+zZQdiORSFdYxNnlnWwbic2GUJruiQVLuhpseenAgMBAAGjYzBh
+MB0GA1UdDgQWBBQHT/IIheEC2kdBxI/TfGqUxWJw9zAfBgNVHSMEGDAWgBQHT/II
+heEC2kdBxI/TfGqUxWJw9zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQsFAAOCAgEAqXZjy4EltJCRtBmN0ohAHPWqH4ZJQCI2HrM3
+wHB6c4oPWcJ+M2PfmYPUJo9VMjvn4S3sZuAysyoHduvRdGDnElW4wglL1xxpoUOx
+FqoZUoYWV8hDFmUTWM5b4CtJxOPdTAd8VgypulM3iUEzBQrjR6tnMOdkiFMOmVag
+0/Nnr+Tcfk/crMCx3xsVnisYjJoQBFBH4UY+gWE/V/MS1Sya4/qTbuuCUq+Qym5P
+r8TkWAJlg7iVVLbZ2j94VUdpiQPWJEGMtJck/NEmOTruhhQlT7c1u/lqXCGj7uci
+LmhLsBVmdtWT9AWS8Rl7Qo5GXbjxKIaP3IM9axhDLm8WHwPRLx7DuIFEc+OBxJhz
+wkr0g0yLS0AMZpaC6UGbWX01ed10U01mQ/qPU5uZiB0GvruwsYWZsyL1QXUeqLz3
+/KKrx3XsXjtBu3ZG4LAnwuxfeZCNw9ofg8CqF9c20ko+7tZAv6DCu9UL+2oZnEyQ
+CboRDwpnAlQ7qJVSp2xMgunO3xxVMlhD5LZpEJz1lRT0nQV3uuLpMYNM4FS9OW/X
+MZSzwHhDdCTDWtc/iRszimOnYYV8Y0ubJcb59uhwcsHmdfnwL9DVO6X5xyzb8wsf
+wWaPbub8SN2jKnT0g6ZWuca4VwEo1fRaBkzSZDqXwhkBDWP8UBqLXMXWHdZaT8NK
+0NEO74c=
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example.com.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example.com.crt
new file mode 100644
index 00000000..71d9e236
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example.com.crt
@@ -0,0 +1,62 @@
+-----BEGIN CERTIFICATE-----
+MIIFIjCCAwqgAwIBAgICIAEwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzElMCMG
+A1UEAwwcS29uZyBUZXN0aW5nIEludGVybWlkaWF0ZSBDQTAeFw0xOTA1MDIyMDAz
+MTFaFw0yOTA0MjgyMDAzMTFaMFMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxGDAWBgNVBAMMD2Zvb0BleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJldMxsZHDxA
+RpbSXdIFZiTf8D0dYgsPnsmx5tVjA/zrVBSVBPO9KunaXNm4Z6JWmUwenzFGbzWP
+NLfbLn4khuoczzqSru5XfbyH1HrD0cd5lkf44Dw1/otfIFDBleiR/OWEiAxwS4zi
+xIajNyvLr3gC5dv+F+JuWpW1yVQxybIDQWoI25xpd3+ZkXO+OLkToo+YpuwIDlUj
+6Rkm5kbqoxDpaDihA2bsAqjNG7G+SHthaNyACsQsU/t6BHSWzHumScN0CxJ+TeVH
+fTZklelItZ6YP0B0RQjzvSGA423UgALzqJglGPe8UDjm3BMlg2xhTfnfy1J6Vmbt
+5jx6FOXUARsCAwEAAaOB8jCB7zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF
+oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBRTzNOmhGRXaZamxVfnlKXarIOEmDAfBgNVHSMEGDAW
+gBQLDgQOl/htYk8k8DvGb9IKO40RETAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw
+FAYIKwYBBQUHAwIGCCsGAQUFBwMEMCsGA1UdEQQkMCKBD2Zvb0BleGFtcGxlLmNv
+bYEPYmFyQGV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBziDuVjU0I1CwO
+b1Cx2TJpzi3l5FD/ozrMZT6F3EpkJFGZWgXrsXHz/0qKTrsbB2m3/fcyd0lwQ5Lh
+fz8X1HPrwXa3BqZskNu1vOUNiqAYWvQ5gtbpweJ96LzMSYVGLK78NigYTtK+Rgq3
+As5CVfLXDBburrQNGyRTsilCQDNBvIpib0eqg/HJCNDFMPrBzTMPpUutyatfpFH2
+UwTiVBfA14YYDxZaetYWeksy28XH6Uj0ylyz67VHND+gBMmQNLXQHJTIDh8JuIf2
+ec6o4HrtyyuRE3urNQmcPMAokacm4NKw2+og6Rg1VS/pckaSPOlSEmNnKFiXStv+
+AVd77NGriUWDFCmnrFNOPOIS019W0oOk6YMwTUDSa86Ii6skCtBLHmp/cingkTWg
+7KEbdT1uVVPgseC2AFpQ1BWJOjjtyW3GWuxERIhuab9/ckTz6BuIiuK7mfsvPBrn
+BqjZyt9WAx8uaWMS/ZrmIj3fUXefaPtl27jMSsiU5oi2vzFu0xiXJb6Jr7RQxD3O
+XRnycL/chWnp7eVV1TQS+XzZ3ZZQIjckDWX4E+zGo4o9pD1YC0eytbIlSuqYVr/t
+dZmD2gqju3Io9EXPDlRDP2VIX9q1euF9caz1vpLCfV+F8wVPtZe5p6JbNugdgjix
+nDZ2sD2xGXy6/fNG75oHveYo6MREFw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFmjCCA4KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzEdMBsG
+A1UEAwwUS29uZyBUZXN0aW5nIFJvb3QgQ0EwHhcNMTkwNTAyMTk0MDQ4WhcNMjkw
+NDI5MTk0MDQ4WjBgMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEV
+MBMGA1UECgwMS29uZyBUZXN0aW5nMSUwIwYDVQQDDBxLb25nIFRlc3RpbmcgSW50
+ZXJtaWRpYXRlIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0dnj
+oHlJmNM94vQnK2FIIQJm9OAVvyMtAAkBKL7Cxt8G062GHDhq6gjQ9enuNQE0l3Vv
+mSAh7N9gNlma6YbRB9VeG54BCuRQwCxveOBiwQvC2qrTzYI34kF/AeflrDOdzuLb
+zj5cLADKXGCbGDtrSPKUwdlkuLs3pRr/YAyIQr7zJtlLz+E0GBYp0GWnLs0FiLSP
+qSBWllC9u8gt2MiKyNlXw+kZ8lofOehCJzfFr6qagVklPw+8IpU6OGmRLFQVwVhp
+zdAJmAGmSo/AGNKGqDdjzC4N2l4uYGH6n2KmY2yxsLBGZgwtLDst3fK4a3Wa5Tj7
+cUwCcGLGtfVTaIXZYbqQ0nGsaYUd/mhx3B3Jk1p3ILZ72nVYowhpj22ipPGal5hp
+ABh1MX3s/B+2ybWyDTtSaspcyhsRQsS6axB3DwLOLRy5Xp/kqEdConCtGCsjgm+U
+FzdupubXK+KIAmTKXDx8OM7Af/K7kLDfFTre40sEB6fwrWwH8yFojeqkA/Uqhn5S
+CzB0o4F3ON0xajsw2dRCziiq7pSe6ALLXetKpBr+xnVbUswH6BANUoDvh9thVPPx
+1trkv+OuoJalkruZaT+38+iV9xwdqxnR7PUawqSyvrEAxjqUo7dDPsEuOpx1DJjO
+XwRJCUjd7Ux913Iks24BqpPhEQz/rZzJLBApRVsCAwEAAaNmMGQwHQYDVR0OBBYE
+FAsOBA6X+G1iTyTwO8Zv0go7jRERMB8GA1UdIwQYMBaAFAdP8giF4QLaR0HEj9N8
+apTFYnD3MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4ICAQAWzIvIVM32iurqM451Amz0HNDG9j84cORnnaRR5opFTr3P
+EqI3QkgCyP6YOs9t0QSbA4ur9WUzd3c9Ktj3qRRgTE+98JBOPO0rv+Kjj48aANDV
+5tcbI9TZ9ap6g0jYr4XNT+KOO7E8QYlpY/wtokudCUDJE9vrsp1on4Bal2gjvCdh
+SU0C1lnj6q6kBdQSYHrcjiEIGJH21ayVoNaBVP/fxyCHz472w1xN220dxUI/GqB6
+pjcuy9cHjJHJKJbrkdt2eDRAFP5cILXc3mzUoGUDHY2JA1gtOHV0p4ix9R9AfI9x
+snBEFiD8oIpcQay8MJH/z3NLEPLoBW+JaAAs89P+jcppea5N9vbiAkrPi687BFTP
+PWPdstyttw6KrvtPQR1+FsVFcGeTjo32/UrckJixdiOEZgHk+deXpp7JoRdcsgzD
++okrsG79/LgS4icLmzNEp0IV36QckEq0+ALKDu6BXvWTkb5DB/FUrovZKJgkYeWj
+GKogyrPIXrYi725Ff306124kLbxiA+6iBbKUtCutQnvut78puC6iP+a2SrfsbUJ4
+qpvBFOY29Mlww88oWNGTA8QeW84Y1EJbRkHavzSsMFB73sxidQW0cHNC5t9RCKAQ
+uibeZgK1Yk7YQKXdvbZvXwrgTcAjCdbppw2L6e0Uy+OGgNjnIps8K460SdaIiA==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example.com.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example.com.key
new file mode 100644
index 00000000..fb823431
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example.com.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAmV0zGxkcPEBGltJd0gVmJN/wPR1iCw+eybHm1WMD/OtUFJUE
+870q6dpc2bhnolaZTB6fMUZvNY80t9sufiSG6hzPOpKu7ld9vIfUesPRx3mWR/jg
+PDX+i18gUMGV6JH85YSIDHBLjOLEhqM3K8uveALl2/4X4m5albXJVDHJsgNBagjb
+nGl3f5mRc744uROij5im7AgOVSPpGSbmRuqjEOloOKEDZuwCqM0bsb5Ie2Fo3IAK
+xCxT+3oEdJbMe6ZJw3QLEn5N5Ud9NmSV6Ui1npg/QHRFCPO9IYDjbdSAAvOomCUY
+97xQOObcEyWDbGFN+d/LUnpWZu3mPHoU5dQBGwIDAQABAoIBAQCLqQzeM3q7/4iI
+1l+r31DKacgbz4M2MW5XnJNqZTX/f8pcx+vvjqfiuADwH/b4JcaKRCSSOeMSMiw+
+9fGb2+WkksHARE3bLH+LTWKYvXRvI/FP73s8Oato/iKuh+vdE/zqgktmkGisjuGK
+/l1Cm8VaE8GBGh5kDDyfsyD5dDGJ0fYzJkfQqygd5B5TSaWflQsB//AXvHzkNy+G
+RHbrMl7t9rDCTtwnefSEJIEwAZerGKV0p+VlRy23mQLwxTxJ5jEjVvcFIMalnD4R
+nKaZYb3LgkCCTQ5Lw/xrkdAEJwfafhdu1CmvKelv1qpcz1vJdrFSfX5NOYS/93jI
+aKJT8Nm5AoGBAMmOOUTvbUd9nlbZXYGLTsoy+qA+OhLkB59krddH4mFoRvbggD6U
+Y/h7O/fA4spqtts+aVoEh/jyuwGTxMn0NPLjD0G8YZr1A4GUx1hgEQ1NIWvRlXrX
+s1bgIlaOc14hOpKf0to3mIovyhRm8PaDbQfHWfyl4yKtFgKiO4OCMK0/AoGBAMLK
+e9C5sedDKt8ESycaXnui1hA4WQkeMdXFflnabuhE29dkC7kDFEVurlmsQb3zFa0V
+dF40niT7xYqzdEJIbaZ3JZIrSFhnPSSBna+B1FjMhTVb/5sjPJS87BvjVYiZd5GY
+5Az4RgSlU3PlsaiuR95NH1vDxHXb5GcMs/EfnEklAoGBAIVFe2yve+yXjUkT9RYh
+TPm596pZOwEeskOcyK3epDuQPcwj6eh3Khs1MRPDALKjGUGi5PpWoKnlpe2HDcoT
+pacsp/vpWgiiFa1q+NzguKW46G5oaJSPZ8/75/ifvHzzL82fzEXqGPzWWKJg5te5
+UzCfikraTXOySyl2qC9uuEz1AoGBAILH8eNMmb48YW9EcbS6Ro9Z38EaI+U0SZ9O
+LqvjNS1q9fMiL6CzCYwoaJS6S5VdvMLtsaiCSV9pTtL182uBN2VZf3co6jS4c9ur
+zpQEZe6Mui7+KpodSVJPmXKL6mSBLT8q2IpAsrnxyhr5L5OiF4yQWSqCQMgkr6/k
+XnfYklSlAoGBAKBePjIdBGLy3ckdlTfbuTeO3kp2eZFBDtGzxP515+LcoRfOjd8T
+ZDX/porUMcgbtIF/B4SNso+8D/aHTCg3QAo6nDjFFjUBHhftgy+GP3BFfMvjqou6
+utJFRkc3FvrrkkeWHnyDQrPmAHjar94/xq1k1Vo+KQHQVQOrvtQt6KXK
+-----END RSA PRIVATE KEY-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example_validated.com.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example_validated.com.crt
new file mode 100644
index 00000000..6fafa161
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/client_example_validated.com.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFIjCCAwqgAwIBAgICIAEwDQYJKoZIhvcNAQELBQAwYDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEtvbmcgVGVzdGluZzElMCMG
+A1UEAwwcS29uZyBUZXN0aW5nIEludGVybWlkaWF0ZSBDQTAeFw0xOTA1MDIyMDAz
+MTFaFw0yOTA0MjgyMDAzMTFaMFMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRUwEwYDVQQKDAxLb25nIFRlc3RpbmcxGDAWBgNVBAMMD2Zvb0BleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJldMxsZHDxA
+RpbSXdIFZiTf8D0dYgsPnsmx5tVjA/zrVBSVBPO9KunaXNm4Z6JWmUwenzFGbzWP
+NLfbLn4khuoczzqSru5XfbyH1HrD0cd5lkf44Dw1/otfIFDBleiR/OWEiAxwS4zi
+xIajNyvLr3gC5dv+F+JuWpW1yVQxybIDQWoI25xpd3+ZkXO+OLkToo+YpuwIDlUj
+6Rkm5kbqoxDpaDihA2bsAqjNG7G+SHthaNyACsQsU/t6BHSWzHumScN0CxJ+TeVH
+fTZklelItZ6YP0B0RQjzvSGA423UgALzqJglGPe8UDjm3BMlg2xhTfnfy1J6Vmbt
+5jx6FOXUARsCAwEAAaOB8jCB7zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF
+oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBRTzNOmhGRXaZamxVfnlKXarIOEmDAfBgNVHSMEGDAW
+gBQLDgQOl/htYk8k8DvGb9IKO40RETAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw
+FAYIKwYBBQUHAwIGCCsGAQUFBwMEMCsGA1UdEQQkMCKBD2Zvb0BleGFtcGxlLmNv
+bYEPYmFyQGV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBziDuVjU0I1CwO
+b1Cx2TJpzi3l5FD/ozrMZT6F3EpkJFGZWgXrsXHz/0qKTrsbB2m3/fcyd0lwQ5Lh
+fz8X1HPrwXa3BqZskNu1vOUNiqAYWvQ5gtbpweJ96LzMSYVGLK78NigYTtK+Rgq3
+As5CVfLXDBburrQNGyRTsilCQDNBvIpib0eqg/HJCNDFMPrBzTMPpUutyatfpFH2
+UwTiVBfA14YYDxZaetYWeksy28XH6Uj0ylyz67VHND+gBMmQNLXQHJTIDh8JuIf2
+ec6o4HrtyyuRE3urNQmcPMAokacm4NKw2+og6Rg1VS/pckaSPOlSEmNnKFiXStv+
+AVd77NGriUWDFCmnrFNOPOIS019W0oOk6YMwTUDSa86Ii6skCtBLHmp/cingkTWg
+7KEbdT1uVVPgseC2AFpQ1BWJOjjtyW3GWuxERIhuab9/ckTz6BuIiuK7mfsvPBrn
+BqjZyt9WAx8uaWMS/ZrmIj3fUXefaPtl27jMSsiU5oi2vzFu0xiXJb6Jr7RQxD3O
+XRnycL/chWnp7eVV1TQS+XzZ3ZZQIjckDWX4E+zGo4o9pD1YC0eytbIlSuqYVr/t
+dZmD2gqju3Io9EXPDlRDP2VIX9q1euF9caz1vpLCfV+F8wVPtZe5p6JbNugdgjix
+nDZ2sD2xGXy6/fNG75oHveYo6MREFw==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/good_tls_client.crt b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/good_tls_client.crt
new file mode 100644
index 00000000..af214d21
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/good_tls_client.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFQDCCAygCAWUwDQYJKoZIhvcNAQEFBQAwZjELMAkGA1UEBhMCQVUxDDAKBgNV
+BAgMA05TVzEPMA0GA1UEBwwGU3lkbmV5MQ0wCwYDVQQKDARLb25nMQswCQYDVQQL
+DAJQUzEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTAeFw0yMDA2MTAyMzIx
+MThaFw0yMTA2MTAyMzIxMThaMGYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIDANOU1cx
+DzANBgNVBAcMBlN5ZG5leTENMAsGA1UECgwES29uZzELMAkGA1UECwwCUFMxHDAa
+BgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDH1JuwgFDvXpLA0kXhFoDhM/Ip7J3GVwkulSyamKbTvfNTcV7B
+fCCwwyP++XLZ7E6k3POnQwtUv6oDT7TldmutgZvlVfc9hUnMhoe2PEL6qbDZbzto
+UE7GDgpASHIbNTeU8AvZY9cCaFg9aF3gK36NSOAmBXMXsISN28MiVcxFZlgpZfpk
+LIAvtaUXD6TMxpC2H5DCcVDlv7ikcwpoN5lhTP3WtpuGN8IIA8euf0Bsqr+UfWtS
+gcOPWFBipY1aOuzSZ/VacHxyiPfM7pMhp3O3Cbuqf5hhadtDGCq8ZeBORBpJyfXh
+hPx3j/jWtapinQO0Sl+4ODZA/0/41d8+sh5ZPuNzsDNzufiH1PQkJuDcLa1DivTM
+sHD1NYFQpBLKZU9F6Vr7Pu6ntckW/iRfXZCQuYusBzk2xGquuMd4LqLmTstXsgDy
+iYoeAF4JsypsPLorJrmR/64d3urNTUeiZfXvAwV3sFLxJg8eekdEVXSGIVl7wmDZ
+2guYq6jG33dvmyq6U7Rbtpi4qWJdMOclboU7p6C06T8FPvyDMZUolJ3fghN7HIHZ
+G4PDDQXTjCtG2MAY1wwxnLj5RI0iLt8RA15p+NkdWwuWCb/YA03hb4vafjk+fqgj
+JqC/wI2d/FLCqhET1qqHXB/wMKCpVra+aBuDB1QIdK3CbHPUNpoqcMreVwIDAQAB
+MA0GCSqGSIb3DQEBBQUAA4ICAQBPv3626lgITncOD5qM8t0NIqamJMKFGh0VQUHY
+IBO9weGbczyP72ZzXwxTKinCHirVLRiGjItrD4zAPDuMsQ1NCMHjmytXlS0jnuMw
+i2xWtcXoKuOlA2Y/AzU9s/SkZNTUgnZBVCbPp8q+ezJCtODgY1x0xDS6KJgWCCTQ
+YVTt4vFyfWGTJYS9cgeYI/q8znZOs8slRwgSI063ICD5TyHQeQWDqTsYAssVaMt6
+amiNKx1DKEgIbgTOEqUzYeZpBqNL/pRxkHB9PAbUE57+n3zSBKmxXQfmAoCDGGhy
+IPmYqh3FJFx98iud//O5awC4184A7eGyC+nVapv23yXaQHoEeH7huiNJwucml3Ja
+L2AY0VeEmj1HKYbwh4SV6CVjgTv002tiDjId04nf4DLL6hU+YjBsg9z6K1ZXrIUB
+WX9UIpPlXv3N3Uq+R+1sidfYNwOcAy5VLbj+mwN2uHdCH2DFkvo9W5abCMaxLlKh
+tH3V9xijK54Ph79ySkgyjC20o+KsL+ZJR75fniTjn716m+uJys4B+BWGJCgDzi1X
+pRGnW6Yj1IAErPRtKQU3FRG7frNrpRPTt/F8tPTPBqi2bSeMaCn2X2u0gVDo7FhX
+pkh6xnPZ/qHUXwDqjngDinNQ8zTMgxt/sv84zabDAf67ZkAyYriQx8H6kWULgcou
+1h+bNg==
+-----END CERTIFICATE-----
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/good_tls_client.key b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/good_tls_client.key
new file mode 100644
index 00000000..ae247909
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/tls-metadata-headers/good_tls_client.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAx9SbsIBQ716SwNJF4RaA4TPyKeydxlcJLpUsmpim073zU3Fe
+wXwgsMMj/vly2exOpNzzp0MLVL+qA0+05XZrrYGb5VX3PYVJzIaHtjxC+qmw2W87
+aFBOxg4KQEhyGzU3lPAL2WPXAmhYPWhd4Ct+jUjgJgVzF7CEjdvDIlXMRWZYKWX6
+ZCyAL7WlFw+kzMaQth+QwnFQ5b+4pHMKaDeZYUz91rabhjfCCAPHrn9AbKq/lH1r
+UoHDj1hQYqWNWjrs0mf1WnB8coj3zO6TIadztwm7qn+YYWnbQxgqvGXgTkQaScn1
+4YT8d4/41rWqYp0DtEpfuDg2QP9P+NXfPrIeWT7jc7Azc7n4h9T0JCbg3C2tQ4r0
+zLBw9TWBUKQSymVPRela+z7up7XJFv4kX12QkLmLrAc5NsRqrrjHeC6i5k7LV7IA
+8omKHgBeCbMqbDy6Kya5kf+uHd7qzU1HomX17wMFd7BS8SYPHnpHRFV0hiFZe8Jg
+2doLmKuoxt93b5squlO0W7aYuKliXTDnJW6FO6egtOk/BT78gzGVKJSd34ITexyB
+2RuDww0F04wrRtjAGNcMMZy4+USNIi7fEQNeafjZHVsLlgm/2ANN4W+L2n45Pn6o
+Iyagv8CNnfxSwqoRE9aqh1wf8DCgqVa2vmgbgwdUCHStwmxz1DaaKnDK3lcCAwEA
+AQKCAgEAl7fmVQW3bWYiid+cSp9pTpLAA/CwgYxHXmTILsBDuXP4sJvc045Iy6v9
+QnNmLerqcuWG4LFYgwnaLRVxRN66Nku3b1Hrm3v9YhmqJERwnhwEN7HqKvL+PAUg
+51mZuOqYvq3acT0KW7uZc1YwICkltmNkO7nbflr46TO3XfRCWVaxDQ1uJGxdDtwc
+8ftgplEEQiKJJ/gc8bK0n7bwadv8yhFIkKKoRilDWg50pxnacibZugXXXBdFwSnO
+Lnd/cMhYUg+4CuhotBUl/yQL9XtLsebj4zlpGlziqnhIyi2XNhUi2G9MDeOIQ8a9
+qVbYfHK2A0aMkjOrjYHK2CSZkOJvgv6qqpqDYwGSdI1AXtbP0+Oed2z0CQkj57qY
+hJXDkOjBgkldKL/MPI6g3LGnIa53RvX2oaFUZ0gKjO5F1m4c4cogF0xZUmQ9qt4v
+aHuzP5TBQdgms2yKIK/CafXXOmN435xdWJgkPxaM1JZq3K8hegL+rTHH8Obsf1p4
+sRvUGjsHY655YwvQkU6oBSPvtw3g9sYM+RsWTQKMXv2CAc+8q7D3byCr6ZL2CTC5
+gf5ZwQounjeDEzbwDFnCE2RV70N+xlqyLaQy3/Ftsee0L4Bi9amIIhlFSZs2ACS3
+Jk2WcC4ar7JhwvMq3pjs7QhfEpfbISUWhgNnqVK5m14fq9ZzcBECggEBAPIkCEV3
+ywMtkA/4/+mjAOlgjdaD5Yc5p7NkhQSn8TTxx5f4b6C0dADLolE0qinelRU2CQGX
+AK/QO53BFxnIt+9+FzmOf1oSWlzlzESNQz21YfQwmu+b7Bhw7ZwnJ0xdXeYmqfg8
+zXuwR9FfeXjo8fv89YbxhBM2Uxp/v0GZMw+DEC70yhcuhJZFHx4E7OabR/ozc/0b
+bhiIVdcOlRvgonKserXtei1J0aDFlfCF/t/eUUtFu5dOHKgYP0/yLUeFTIh5wSlj
+zQGjF9y10tztErOL0pFnzzTXu/nD19eelF0gWB1+OviGa21iJzKABnPYjPdW91lk
+ntBlF0KSSZrA3uMCggEBANNEn/X8KHJbrtjd0K4Q3arLZxqhv6xIXnvdhG+5LmMH
+sRKxKK08L6NYlfM6FgALuSnWJfMcoDpQ+bZoQzM7NuhaLcWLnXAGl1y/F5Sa8PU3
+7vbKJJQXb5jVy0aPP9kCu0DhNNs+DrN6NBr4gff02QijmFRfHWT4SVlEDY0cLknP
+/JiwNWjcPVGjdrWFF8+577CouQOvZcE51htkgpEaXTpbQqqe5WIHoAlx1XSQ9kA7
+szPPtTNS87Ey2cMgMlSJzU4Ad0ETwWUk7zWIx9kVGkWxbRz333YakzZyS46iL2i3
+51tdGzraXbwIkcZ5HRvBWH5b0egwLr292Xe5gEQCiP0CggEAPAY+nqnhg3op+iup
+lMs5kKDEyGaN4YWjUs8aQls3af59keieE2JoV0IjjKnXfOWWY1mvxdse6nZeMO8K
+wPOG1TGWpEf0velKyTiV+HBkOMnb7GKYJcmrLyvfWxRBo6aJfmIbRFQeRAkg5IAc
+ZqKoSeUdIESCjxiqZSlBygsmIzREML+x0SlQ6MddvV1PNKNNpnJW0GmaUy6IyPTu
+dair3G1xReQGo29nLcmNL8Cnu+sqj9RLemh8phbMqw7HtrEOlc5I2e/JhBxf7/CK
+wwVp22RP4IexT9Ii4Plxahv/f05jSXxfNfhsbQMl4FjoQJNv7mh6jIhq7hIBQBlv
+cZNaLQKCAQA0vFGNjnbuLNKVUbEwcgdTBdjjvfJhZ+Ml2iKf3MKmd5oMx2oW7Aoc
+JRWxFBO/yS1cMxdf+wpH5ESg8p+rnyFSmIgKYpJXD14jCZQyRQbWvzKIKec9jCgQ
+J3XJVCKk4diqFk0iWPhEdsGg5d1TbqvqSyPQE7n7pTpx7NnRGnK3fBJabsm2Nhuc
+UEhmgSVzdb9OAp/EOrkUpYI7bjhlTgZtdbBLdMRfVvXsxZx5r2G4oO5XhYZ+nDs2
+RTI0UcoebhNwYA9kkDDbNX/8hfsUxf673NEWxQzjpziOA7O2k+dvpVA77x7H7xAs
+yprQxxSsA/Q5/mOEKNrSBBzPlN9emp9lAoIBACmJyhsQUxeyzghKOEK1OeC0klC0
+Hr3p8ZobyFk9nCEsyAWoU/uY97JJmYxZ2NSJiNEcWm/s59+K2T7oc9Wm9fbheKVY
+kpjq+dQcDWQTa0DWqDIjMBRkDyg4lGTTjEZwA9mLj074HNIbHFfh6rzRYWfOMJ4+
+FcCjue9y7by+00wTZ2ZAHqU2BchizjYU2fY1dVAhM21/85bRX4YlUXlcQnCOMPCE
+3tsPDn0K+magZ8VWhTdSGKj1vNfpf6P7u5CV6IjUyc18EJwzjsRbBa+OtRWrVB44
+/Jejes+G3Gln86S0SuHQVJcE8VVsEgKzKjdqXM+eJSYI8gGCapd++lNv7D0=
+-----END RSA PRIVATE KEY-----
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/to-strip.conf b/kong-versions/test9.9.9.3/kong/spec/fixtures/to-strip.conf
new file mode 100644
index 00000000..1e1e0ddc
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/to-strip.conf
@@ -0,0 +1,9 @@
+database = postgres # strip me
+log_level = debug # strip this
+ # comment too
+pg_ssl = off # Toggles client-server TLS connections
+ # between Kong and PostgreSQL.
+
+pg_password = test\#123 # do not strip an escaped octothorpe
+
+plugins = foobar, hello-world, bundled
diff --git a/kong-versions/test9.9.9.3/kong/spec/fixtures/valid-module.lua b/kong-versions/test9.9.9.3/kong/spec/fixtures/valid-module.lua
new file mode 100644
index 00000000..51c53187
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/fixtures/valid-module.lua
@@ -0,0 +1,15 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- Valid module (exposing data and having a global variable) for utils.load_module_if_exists unit tests.
+-- Assert that load_module_if_exists returns a module if it was valid
+
+local _M = {
+ exposed = "All your base are belong to us."
+}
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers.lua b/kong-versions/test9.9.9.3/kong/spec/helpers.lua
new file mode 100644
index 00000000..73ae6bf4
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers.lua
@@ -0,0 +1,4646 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+------------------------------------------------------------------
+-- Collection of utilities to help testing Kong features and plugins.
+--
+-- @copyright Copyright 2016-2022 Kong Inc. All rights reserved.
+-- @license [Apache 2.0](https://opensource.org/licenses/Apache-2.0)
+-- @module spec.helpers
+
+local BIN_PATH = "bin/kong"
+local TEST_CONF_PATH = os.getenv("KONG_SPEC_TEST_CONF_PATH") or "spec/kong_tests.conf"
+local CUSTOM_PLUGIN_PATH = "./spec/fixtures/custom_plugins/?.lua"
+-- XXX EE custom plugins for enterprise tests
+local CUSTOM_EE_PLUGIN_PATH = "./spec-ee/fixtures/custom_plugins/?.lua;./spec-ee/fixtures/custom_plugins/?/init.lua;./?/init.lua"
+local CUSTOM_VAULT_PATH = "./spec/fixtures/custom_vaults/?.lua;./spec/fixtures/custom_vaults/?/init.lua"
+local DNS_MOCK_LUA_PATH = "./spec/fixtures/mocks/lua-resty-dns/?.lua"
+local GO_PLUGIN_PATH = "./spec/fixtures/go"
+local GRPC_TARGET_SRC_PATH = "./spec/fixtures/grpc/target/"
+local MOCK_UPSTREAM_PROTOCOL = "http"
+local MOCK_UPSTREAM_SSL_PROTOCOL = "https"
+local MOCK_UPSTREAM_HOST = "127.0.0.1"
+local MOCK_UPSTREAM_HOSTNAME = "localhost"
+local MOCK_UPSTREAM_PORT = 15555
+local MOCK_UPSTREAM_SSL_PORT = 15556
+local MOCK_UPSTREAM_STREAM_PORT = 15557
+local MOCK_UPSTREAM_STREAM_SSL_PORT = 15558
+local GRPCBIN_HOST = os.getenv("KONG_SPEC_TEST_GRPCBIN_HOST") or "localhost"
+local GRPCBIN_PORT = tonumber(os.getenv("KONG_SPEC_TEST_GRPCBIN_PORT")) or 9000
+local GRPCBIN_SSL_PORT = tonumber(os.getenv("KONG_SPEC_TEST_GRPCBIN_SSL_PORT")) or 9001
+local MOCK_GRPC_UPSTREAM_PROTO_PATH = "./spec/fixtures/grpc/hello.proto"
+local ZIPKIN_HOST = os.getenv("KONG_SPEC_TEST_ZIPKIN_HOST") or "localhost"
+local ZIPKIN_PORT = tonumber(os.getenv("KONG_SPEC_TEST_ZIPKIN_PORT")) or 9411
+local OTELCOL_HOST = os.getenv("KONG_SPEC_TEST_OTELCOL_HOST") or "localhost"
+local OTELCOL_HTTP_PORT = tonumber(os.getenv("KONG_SPEC_TEST_OTELCOL_HTTP_PORT")) or 4318
+local OTELCOL_ZPAGES_PORT = tonumber(os.getenv("KONG_SPEC_TEST_OTELCOL_ZPAGES_PORT")) or 55679
+local OTELCOL_FILE_EXPORTER_PATH = os.getenv("KONG_SPEC_TEST_OTELCOL_FILE_EXPORTER_PATH") or "./tmp/otel/file_exporter.json"
+local REDIS_HOST = os.getenv("KONG_SPEC_TEST_REDIS_HOST") or "localhost"
+local REDIS_PORT = tonumber(os.getenv("KONG_SPEC_TEST_REDIS_PORT") or 6379)
+local REDIS_SSL_PORT = tonumber(os.getenv("KONG_SPEC_TEST_REDIS_SSL_PORT") or 6380)
+local REDIS_SSL_SNI = os.getenv("KONG_SPEC_TEST_REDIS_SSL_SNI") or "test-redis.example.com"
+local TEST_COVERAGE_MODE = os.getenv("KONG_COVERAGE")
+local TEST_COVERAGE_TIMEOUT = 30
+local BLACKHOLE_HOST = "10.255.255.255"
+local KONG_VERSION = require("kong.meta")._VERSION
+local PLUGINS_LIST
+
+local consumers_schema_def = require "kong.db.schema.entities.consumers"
+local services_schema_def = require "kong.db.schema.entities.services"
+local plugins_schema_def = require "kong.db.schema.entities.plugins"
+local routes_schema_def = require "kong.db.schema.entities.routes"
+local prefix_handler = require "kong.cmd.utils.prefix_handler"
+local dc_blueprints = require "spec.fixtures.dc_blueprints"
+local conf_loader = require "kong.conf_loader"
+local kong_global = require "kong.global"
+local Blueprints = require "spec.fixtures.blueprints"
+local constants = require "kong.constants"
+local pl_tablex = require "pl.tablex"
+local pl_utils = require "pl.utils"
+local pl_path = require "pl.path"
+local pl_file = require "pl.file"
+local version = require "version"
+local pl_dir = require "pl.dir"
+local pl_Set = require "pl.Set"
+local Schema = require "kong.db.schema"
+local Entity = require "kong.db.schema.entity"
+local cjson = require "cjson.safe"
+local kong_table = require "kong.tools.table"
+local http = require "resty.http"
+local pkey = require "resty.openssl.pkey"
+local nginx_signals = require "kong.cmd.utils.nginx_signals"
+local log = require "kong.cmd.utils.log"
+local DB = require "kong.db"
+local shell = require "resty.shell"
+local ffi = require "ffi"
+local invoke_plugin = require "kong.enterprise_edition.invoke_plugin"
+local portal_router = require "kong.portal.router"
+local rbac = require "kong.rbac"
+local ssl = require "ngx.ssl"
+local ws_client = require "resty.websocket.client"
+local table_clone = require "table.clone"
+local https_server = require "spec.fixtures.https_server"
+local stress_generator = require "spec.fixtures.stress_generator"
+local resty_signal = require "resty.signal"
+local lfs = require "lfs"
+local luassert = require "luassert.assert"
+local uuid = require("kong.tools.uuid").uuid
+local colors = require "ansicolors"
+local strip = require("kong.tools.string").strip
+local splitlines = require("pl.stringx").splitlines
+
+-- XXX EE
+local dist_constants = require "kong.enterprise_edition.distributions_constants"
+local kong_vitals = require "kong.vitals"
+-- EE
+
+local http_new = http.new
+
+ffi.cdef [[
+ int setenv(const char *name, const char *value, int overwrite);
+ int unsetenv(const char *name);
+]]
+
+local kong_exec -- forward declaration
+
+
+log.set_lvl(log.levels.quiet) -- disable stdout logs in tests
+
+-- Add to package path so dao helpers can insert custom plugins
+-- (while running from the busted environment)
+do
+ local paths = {}
+ table.insert(paths, os.getenv("KONG_LUA_PACKAGE_PATH"))
+ table.insert(paths, CUSTOM_PLUGIN_PATH)
+ -- XXX EE custom plugins for enterprise tests
+ table.insert(paths, CUSTOM_EE_PLUGIN_PATH)
+ table.insert(paths, CUSTOM_VAULT_PATH)
+ table.insert(paths, package.path)
+ package.path = table.concat(paths, ";")
+end
+
+--- Returns the OpenResty version.
+-- Extract the current OpenResty version in use and returns
+-- a numerical representation of it.
+-- Ex: `1.11.2.2` -> `11122`
+-- @function openresty_ver_num
+local function openresty_ver_num()
+ local nginx_bin = assert(nginx_signals.find_nginx_bin())
+ local _, _, stderr = shell.run(string.format("%s -V", nginx_bin), nil, 0)
+
+ local a, b, c, d = string.match(stderr or "", "openresty/(%d+)%.(%d+)%.(%d+)%.(%d+)")
+ if not a then
+ error("could not execute 'nginx -V': " .. stderr)
+ end
+
+ return tonumber(a .. b .. c .. d)
+end
+
+--- Unindent a multi-line string for proper indenting in
+-- square brackets.
+-- @function unindent
+-- @usage
+-- local u = helpers.unindent
+--
+-- u[[
+-- hello world
+-- foo bar
+-- ]]
+--
+-- -- will return: "hello world\nfoo bar"
+local function unindent(str, concat_newlines, spaced_newlines)
+ str = string.match(str, "(.-%S*)%s*$")
+ if not str then
+ return ""
+ end
+
+ local level = math.huge
+ local prefix = ""
+ local len
+
+ str = str:match("^%s") and "\n" .. str or str
+ for pref in str:gmatch("\n(%s+)") do
+ len = #prefix
+
+ if len < level then
+ level = len
+ prefix = pref
+ end
+ end
+
+ local repl = concat_newlines and "" or "\n"
+ repl = spaced_newlines and " " or repl
+
+ return (str:gsub("^\n%s*", ""):gsub("\n" .. prefix, repl):gsub("\n$", ""):gsub("\\r", "\r"))
+end
+
+
+--- Set an environment variable
+-- @function setenv
+-- @param env (string) name of the environment variable
+-- @param value the value to set
+-- @return true on success, false otherwise
+local function setenv(env, value)
+ assert(type(env) == "string", "env must be a string")
+ assert(type(value) == "string", "value must be a string")
+ return ffi.C.setenv(env, value, 1) == 0
+end
+
+
+--- Unset an environment variable
+-- @function unsetenv
+-- @param env (string) name of the environment variable
+-- @return true on success, false otherwise
+local function unsetenv(env)
+ assert(type(env) == "string", "env must be a string")
+ return ffi.C.unsetenv(env) == 0
+end
+
+
+--- Write a yaml file.
+-- @function make_yaml_file
+-- @param content (string) the yaml string to write to the file, if omitted the
+-- current database contents will be written using `kong config db_export`.
+-- @param filename (optional) if not provided, a temp name will be created
+-- @return filename of the file written
+local function make_yaml_file(content, filename)
+ local filename = filename or pl_path.tmpname() .. ".yml"
+ if content then
+ local fd = assert(io.open(filename, "w"))
+ assert(fd:write(unindent(content)))
+ assert(fd:write("\n")) -- ensure last line ends in newline
+ assert(fd:close())
+ else
+ assert(kong_exec("config db_export --conf "..TEST_CONF_PATH.." "..filename))
+ end
+ return filename
+end
+
+
+local get_available_port
+do
+ local USED_PORTS = {}
+
+ function get_available_port()
+ for _i = 1, 10 do
+ local port = math.random(10000, 30000)
+
+ if not USED_PORTS[port] then
+ USED_PORTS[port] = true
+
+ local ok = shell.run("netstat -lnt | grep \":" .. port .. "\" > /dev/null", nil, 0)
+
+ if not ok then
+ -- return code of 1 means `grep` did not found the listening port
+ return port
+
+ else
+ print("Port " .. port .. " is occupied, trying another one")
+ end
+ end
+ end
+
+ error("Could not find an available port after 10 tries")
+ end
+end
+
+
+---------------
+-- Conf and DAO
+---------------
+local conf = assert(conf_loader(TEST_CONF_PATH))
+
+_G.kong = kong_global.new()
+kong_global.init_pdk(_G.kong, conf)
+ngx.ctx.KONG_PHASE = kong_global.phases.access
+_G.kong.core_cache = {
+ get = function(self, key, opts, func, ...)
+ if key == constants.CLUSTER_ID_PARAM_KEY then
+ return "123e4567-e89b-12d3-a456-426655440000"
+ end
+
+ return func(...)
+ end
+}
+
+local db = assert(DB.new(conf))
+assert(db:init_connector())
+db.vaults:load_vault_schemas(conf.loaded_vaults)
+db.plugins:load_plugin_schemas(conf.loaded_plugins)
+local blueprints = assert(Blueprints.new(db))
+local dcbp
+local config_yml
+
+
+kong.db = db
+
+
+local cache
+
+--- Gets the ml_cache instance.
+-- @function get_cache
+-- @param db the database object
+-- @return ml_cache instance
+local function get_cache(db)
+ if not cache then
+ local worker_events = require "resty.events.compat"
+ worker_events.configure({
+ listening = "unix:",
+ testing = true,
+ })
+
+ local cluster_events = assert(kong_global.init_cluster_events(conf, db))
+ cache = assert(kong_global.init_cache(conf,
+ cluster_events,
+ worker_events
+ ))
+ end
+
+ return cache
+end
+
+
+kong.cache = get_cache(db)
+
+cache._busted_hooked = false
+
+local function clear_cache_on_file_end(file)
+ if _G.kong and
+ _G.kong.cache and
+ _G.kong.cache.mlcache and
+ _G.kong.cache.mlcache.lru and
+ _G.kong.cache.mlcache.lru.free_queue and
+ _G.kong.cache.mlcache.lru.cache_queue
+ then
+ _G.kong.cache.mlcache.lru.free_queue = nil
+ _G.kong.cache.mlcache.lru.cache_queue = nil
+ end
+end
+
+local function register_busted_hook(opts)
+ local busted = require("busted")
+ if not cache or cache._busted_hooked then
+ return
+ end
+
+ cache._busted_hooked = true
+
+ busted.subscribe({'file', 'end' }, clear_cache_on_file_end)
+end
+
+register_busted_hook()
+
+local vitals
+local function get_vitals(db)
+ if not vitals then
+ vitals = kong_vitals.new({
+ db = db,
+ ttl_seconds = 3600,
+ ttl_minutes = 24 * 60,
+ ttl_days = 30,
+ })
+ end
+
+ return vitals
+end
+
+kong.vitals = get_vitals(db)
+
+local analytics
+local function get_analytics()
+ if not analytics then
+ local kong_analytics = require "kong.analytics"
+ analytics = kong_analytics.new({})
+ end
+
+ return analytics
+end
+
+kong.analytics = get_analytics()
+
+--- Iterator over DB strategies.
+-- @function each_strategy
+-- @param strategies (optional string array) explicit list of strategies to use,
+-- defaults to `{ "postgres", }`.
+-- @see all_strategies
+-- @usage
+-- -- repeat all tests for each strategy
+-- for _, strategy_name in helpers.each_strategy() do
+-- describe("my test set [#" .. strategy .. "]", function()
+--
+-- -- add your tests here
+--
+-- end)
+-- end
+local function each_strategy() -- luacheck: ignore -- required to trick ldoc into processing for docs
+end
+
+--- Iterator over all strategies, the DB ones and the DB-less one.
+-- To test with DB-less, check the example.
+-- @function all_strategies
+-- @param strategies (optional string array) explicit list of strategies to use,
+-- defaults to `{ "postgres", "off" }`.
+-- @see each_strategy
+-- @see make_yaml_file
+-- @usage
+-- -- example of using DB-less testing
+--
+-- -- use "all_strategies" to iterate over; "postgres", "off"
+-- for _, strategy in helpers.all_strategies() do
+-- describe(PLUGIN_NAME .. ": (access) [#" .. strategy .. "]", function()
+--
+-- lazy_setup(function()
+--
+-- -- when calling "get_db_utils" with "strategy=off", we still use
+-- -- "postgres" so we can write the test setup to the database.
+-- local bp = helpers.get_db_utils(
+-- strategy == "off" and "postgres" or strategy,
+-- nil, { PLUGIN_NAME })
+--
+-- -- Inject a test route, when "strategy=off" it will still be written
+-- -- to Postgres.
+-- local route1 = bp.routes:insert({
+-- hosts = { "test1.com" },
+-- })
+--
+-- -- start kong
+-- assert(helpers.start_kong({
+-- -- set the strategy
+-- database = strategy,
+-- nginx_conf = "spec/fixtures/custom_nginx.template",
+-- plugins = "bundled," .. PLUGIN_NAME,
+--
+-- -- The call to "make_yaml_file" will write the contents of
+-- -- the database to a temporary file, which filename is returned.
+-- -- But only when "strategy=off".
+-- declarative_config = strategy == "off" and helpers.make_yaml_file() or nil,
+--
+-- -- the below lines can be omitted, but are just to prove that the test
+-- -- really runs DB-less despite that Postgres was used as intermediary
+-- -- storage.
+-- pg_host = strategy == "off" and "unknownhost.konghq.com" or nil,
+-- }))
+-- end)
+--
+-- ... rest of your test file
+local function all_strategies() -- luacheck: ignore -- required to trick ldoc into processing for docs
+end
+
+do
+ local def_db_strategies = {"postgres"}
+ local def_all_strategies = {"postgres", "off"}
+ local env_var = os.getenv("KONG_DATABASE")
+ if env_var then
+ def_db_strategies = { env_var }
+ def_all_strategies = { env_var }
+ end
+ local db_available_strategies = pl_Set(def_db_strategies)
+ local all_available_strategies = pl_Set(def_all_strategies)
+
+ local function iter(strategies, i)
+ i = i + 1
+ local strategy = strategies[i]
+ if strategy then
+ return i, strategy
+ end
+ end
+
+ each_strategy = function(strategies)
+ if not strategies then
+ return iter, def_db_strategies, 0
+ end
+
+ for i = #strategies, 1, -1 do
+ if not db_available_strategies[strategies[i]] then
+ table.remove(strategies, i)
+ end
+ end
+ return iter, strategies, 0
+ end
+
+ all_strategies = function(strategies)
+ if not strategies then
+ return iter, def_all_strategies, 0
+ end
+
+ for i = #strategies, 1, -1 do
+ if not all_available_strategies[strategies[i]] then
+ table.remove(strategies, i)
+ end
+ end
+ return iter, strategies, 0
+ end
+end
+
+local function truncate_tables(db, tables)
+ if not tables then
+ return
+ end
+
+ for _, t in ipairs(tables) do
+ if db[t] and db[t].schema then
+ db[t]:truncate()
+ end
+ end
+end
+
+local function bootstrap_database(db)
+ local schema_state = assert(db:schema_state())
+ if schema_state.needs_bootstrap then
+ assert(db:schema_bootstrap())
+ end
+
+ if schema_state.new_migrations then
+ assert(db:run_migrations(schema_state.new_migrations, {
+ run_up = true,
+ run_teardown = true,
+ }))
+ end
+end
+
+--- Gets the database utility helpers and prepares the database for a testrun.
+-- This will a.o. bootstrap the datastore and truncate the existing data that
+-- migth be in it. The BluePrint and DB objects returned can be used to create
+-- test entities in the database.
+--
+-- So the difference between the `db` and `bp` is small. The `db` one allows access
+-- to the datastore for creating entities and inserting data. The `bp` one is a
+-- wrapper around the `db` one. It will auto-insert some stuff and check for errors;
+--
+-- - if you create a route using `bp`, it will automatically attach it to the
+-- default service that it already created, without you having to specify that
+-- service.
+-- - any errors returned by `db`, which will be `nil + error` in Lua, will be
+-- wrapped in an assertion by `bp` so if something is wrong it will throw a hard
+-- error which is convenient when testing. When using `db` you have to manually
+-- check for errors.
+--
+-- Since `bp` is a wrapper around `db` it will only know about the Kong standard
+-- entities in the database. Hence the `db` one should be used when working with
+-- custom DAO's for which no `bp` entry is available.
+-- @function get_db_utils
+-- @param strategy (optional) the database strategy to use, will default to the
+-- strategy in the test configuration.
+-- @param tables (optional) tables to truncate, this can be used to accelarate
+-- tests if only a few tables are used. By default all tables will be truncated.
+-- @param plugins (optional) array of plugins to mark as loaded. Since kong will
+-- load all the bundled plugins by default, this is useful mostly for marking
+-- custom plugins as loaded.
+-- @param vaults (optional) vault configuration to use.
+-- @param skip_migrations (optional) if true, migrations will not be run.
+-- @return BluePrint, DB
+-- @usage
+-- local PLUGIN_NAME = "my_fancy_plugin"
+-- local bp = helpers.get_db_utils("postgres", nil, { PLUGIN_NAME })
+--
+-- -- Inject a test route. No need to create a service, there is a default
+-- -- service which will echo the request.
+-- local route1 = bp.routes:insert({
+-- hosts = { "test1.com" },
+-- })
+-- -- add the plugin to test to the route we created
+-- bp.plugins:insert {
+-- name = PLUGIN_NAME,
+-- route = { id = route1.id },
+-- config = {},
+-- }
+local function get_db_utils(strategy, tables, plugins, vaults, skip_migrations)
+ strategy = strategy or conf.database
+ if tables ~= nil and type(tables) ~= "table" then
+ error("arg #2 must be a list of tables to truncate", 2)
+ end
+ if plugins ~= nil and type(plugins) ~= "table" then
+ error("arg #3 must be a list of plugins to enable", 2)
+ end
+
+ if plugins then
+ for _, plugin in ipairs(plugins) do
+ conf.loaded_plugins[plugin] = true
+ end
+ end
+
+ if vaults ~= nil and type(vaults) ~= "table" then
+ error("arg #4 must be a list of vaults to enable", 2)
+ end
+
+ if vaults then
+ for _, vault in ipairs(vaults) do
+ conf.loaded_vaults[vault] = true
+ end
+ end
+
+ -- Clean workspaces from the context - otherwise, migrations will fail,
+ -- as some of them have dao calls
+ -- If `no_truncate` is falsey, `dao:truncate` and `db:truncate` are called,
+ -- and these set the workspace back again to the new `default` workspace
+ ngx.ctx.workspace = nil
+
+ -- DAO (DB module)
+ local db = assert(DB.new(conf, strategy))
+ assert(db:init_connector())
+
+ if not skip_migrations then
+ bootstrap_database(db)
+ end
+
+ do
+ local database = conf.database
+ conf.database = strategy
+ conf.database = database
+ end
+
+ db:truncate("plugins")
+ assert(db.vaults:load_vault_schemas(conf.loaded_vaults))
+ assert(db.plugins:load_plugin_schemas(conf.loaded_plugins))
+
+ -- XXX EE
+ kong.invoke_plugin = invoke_plugin.new {
+ loaded_plugins = db.plugins:get_handlers(),
+ kong_global = kong_global,
+ }
+
+ db:truncate("tags")
+
+ -- initialize portal router
+ kong.portal_router = portal_router.new(db)
+
+ _G.kong.db = db
+
+ -- cleanup tables
+ if not tables then
+ assert(db:truncate())
+
+ else
+ tables[#tables + 1] = "workspaces"
+ truncate_tables(db, tables)
+ end
+
+ -- blueprints
+ local bp
+ if strategy ~= "off" then
+ bp = assert(Blueprints.new(db))
+ dcbp = nil
+ else
+ bp = assert(dc_blueprints.new(db))
+ dcbp = bp
+ end
+
+ if plugins then
+ for _, plugin in ipairs(plugins) do
+ conf.loaded_plugins[plugin] = false
+ end
+ end
+
+ rbac.register_dao_hooks(db)
+ if vaults then
+ for _, vault in ipairs(vaults) do
+ conf.loaded_vaults[vault] = false
+ end
+ end
+
+ if strategy ~= "off" then
+ local workspaces = require "kong.workspaces"
+ workspaces.upsert_default(db)
+ end
+
+ -- calculation can only happen here because this function
+ -- initializes the kong.db instance
+ PLUGINS_LIST = assert(kong.db.plugins:get_handlers())
+ table.sort(PLUGINS_LIST, function(a, b)
+ return a.name:lower() < b.name:lower()
+ end)
+
+ PLUGINS_LIST = pl_tablex.map(function(p)
+ return { name = p.name, version = p.handler.VERSION, }
+ end, PLUGINS_LIST)
+
+ return bp, db
+end
+
+-----------------
+-- Custom helpers
+-----------------
+local resty_http_proxy_mt = setmetatable({}, { __index = http })
+resty_http_proxy_mt.__index = resty_http_proxy_mt
+
+local pack = function(...) return { n = select("#", ...), ... } end
+local unpack = function(t) return unpack(t, 1, t.n) end
+
+--- Prints all returned parameters.
+-- Simple debugging aid, it will pass all received parameters, hence will not
+-- influence the flow of the code. See also `fail`.
+-- @function intercept
+-- @see fail
+-- @usage -- modify
+-- local a,b = some_func(c,d)
+-- -- into
+-- local a,b = intercept(some_func(c,d))
+local function intercept(...)
+ local args = pack(...)
+ print(require("pl.pretty").write(args))
+ return unpack(args)
+end
+
+
+-- Prepopulate Schema's cache
+Schema.new(consumers_schema_def)
+Schema.new(services_schema_def)
+Schema.new(routes_schema_def)
+
+local plugins_schema = assert(Entity.new(plugins_schema_def))
+
+
+--- Validate a plugin configuration against a plugin schema.
+-- @function validate_plugin_config_schema
+-- @param config The configuration to validate. This is not the full schema,
+-- only the `config` sub-object needs to be passed.
+-- @param schema_def The schema definition
+-- @return the validated schema, or nil+error
+local function validate_plugin_config_schema(config, schema_def, extra_fields)
+ assert(plugins_schema:new_subschema(schema_def.name, schema_def))
+ local entity = {
+ id = uuid(),
+ name = schema_def.name,
+ config = config
+ }
+
+ if extra_fields then
+ for k, v in pairs(extra_fields) do
+ entity[k] = v
+ end
+ end
+
+ local entity_to_insert, err = plugins_schema:process_auto_fields(entity, "insert")
+ if err then
+ return nil, err
+ end
+ local _, err = plugins_schema:validate_insert(entity_to_insert)
+ if err then return
+ nil, err
+ end
+ return entity_to_insert
+end
+
+
+-- Case insensitive lookup function, returns the value and the original key. Or
+-- if not found nil and the search key
+-- @usage -- sample usage
+-- local test = { SoMeKeY = 10 }
+-- print(lookup(test, "somekey")) --> 10, "SoMeKeY"
+-- print(lookup(test, "NotFound")) --> nil, "NotFound"
+local function lookup(t, k)
+ local ok = k
+ if type(k) ~= "string" then
+ return t[k], k
+ else
+ k = k:lower()
+ end
+ for key, value in pairs(t) do
+ if tostring(key):lower() == k then
+ return value, key
+ end
+ end
+ return nil, ok
+end
+
+
+--- Check if a request can be retried in the case of a closed connection
+--
+-- For now this is limited to "safe" methods as defined by:
+-- https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1
+--
+-- XXX Since this strictly applies to closed connections, it might be okay to
+-- open this up to include idempotent methods like PUT and DELETE if we do
+-- some more testing first
+local function can_reopen(method)
+ method = string.upper(method or "GET")
+ return method == "GET"
+ or method == "HEAD"
+ or method == "OPTIONS"
+ or method == "TRACE"
+end
+
+
+--- http_client.
+-- An http-client class to perform requests.
+--
+-- * Based on [lua-resty-http](https://github.com/pintsized/lua-resty-http) but
+-- with some modifications
+--
+-- * Additional convenience methods will be injected for the following methods;
+-- "get", "post", "put", "patch", "delete". Each of these methods comes with a
+-- built-in assert. The signature of the functions is `client:get(path, opts)`.
+--
+-- * Body will be formatted according to the "Content-Type" header, see `http_client:send`.
+--
+-- * Query parameters will be added, see `http_client:send`.
+--
+-- @section http_client
+-- @usage
+-- -- example usage of the client
+-- local client = helpers.proxy_client()
+-- -- no need to check for `nil+err` since it is already wrapped in an assert
+--
+-- local opts = {
+-- headers = {
+-- ["My-Header"] = "my header value"
+-- }
+-- }
+-- local result = client:get("/services/foo", opts)
+-- -- the 'get' is wrapped in an assert, so again no need to check for `nil+err`
+
+
+--- Send a http request.
+-- Based on [lua-resty-http](https://github.com/pintsized/lua-resty-http).
+--
+-- * If `opts.body` is a table and "Content-Type" header contains
+-- `application/json`, `www-form-urlencoded`, or `multipart/form-data`, then it
+-- will automatically encode the body according to the content type.
+--
+-- * If `opts.query` is a table, a query string will be constructed from it and
+-- appended to the request path (assuming none is already present).
+--
+-- * instead of this generic function there are also shortcut functions available
+-- for every method, eg. `client:get`, `client:post`, etc. See `http_client`.
+--
+-- @function http_client:send
+-- @param opts table with options. See [lua-resty-http](https://github.com/pintsized/lua-resty-http)
+function resty_http_proxy_mt:send(opts, is_reopen)
+ local cjson = require "cjson"
+ local encode_args = require("kong.tools.http").encode_args
+
+ opts = opts or {}
+
+ -- build body
+ local headers = opts.headers or {}
+ local content_type, content_type_name = lookup(headers, "Content-Type")
+ content_type = content_type or ""
+ local t_body_table = type(opts.body) == "table"
+
+ if string.find(content_type, "application/json") and t_body_table then
+ opts.body = cjson.encode(opts.body)
+
+ elseif string.find(content_type, "www-form-urlencoded", nil, true) and t_body_table then
+ opts.body = encode_args(opts.body, true, opts.no_array_indexes)
+
+ elseif string.find(content_type, "multipart/form-data", nil, true) and t_body_table then
+ local form = opts.body
+ local boundary = "8fd84e9444e3946c"
+ local body = ""
+
+ for k, v in pairs(form) do
+ body = body .. "--" .. boundary .. "\r\nContent-Disposition: form-data; name=\"" .. k .. "\"\r\n\r\n" .. tostring(v) .. "\r\n"
+ end
+
+ if body ~= "" then
+ body = body .. "--" .. boundary .. "--\r\n"
+ end
+
+ local clength = lookup(headers, "content-length")
+ if not clength and not opts.dont_add_content_length then
+ headers["content-length"] = #body
+ end
+
+ if not content_type:find("boundary=") then
+ headers[content_type_name] = content_type .. "; boundary=" .. boundary
+ end
+
+ opts.body = body
+ end
+
+ -- build querystring (assumes none is currently in 'opts.path')
+ if type(opts.query) == "table" then
+ local qs = encode_args(opts.query)
+ opts.path = opts.path .. "?" .. qs
+ opts.query = nil
+ end
+
+ local res, err = self:request(opts)
+ if res then
+ -- wrap the read_body() so it caches the result and can be called multiple
+ -- times
+ local reader = res.read_body
+ res.read_body = function(self)
+ if not self._cached_body and not self._cached_error then
+ self._cached_body, self._cached_error = reader(self)
+ end
+ return self._cached_body, self._cached_error
+ end
+
+ elseif (err == "closed" or err == "connection reset by peer")
+ and not is_reopen
+ and self.reopen
+ and can_reopen(opts.method)
+ then
+ ngx.log(ngx.INFO, "Re-opening connection to ", self.options.scheme, "://",
+ self.options.host, ":", self.options.port)
+
+ self:_connect()
+ return self:send(opts, true)
+ end
+
+ return res, err
+end
+
+
+--- Open or re-open the client TCP connection
+function resty_http_proxy_mt:_connect()
+ local opts = self.options
+
+ if TEST_COVERAGE_MODE == "true" then
+ opts.connect_timeout = TEST_COVERAGE_TIMEOUT * 1000
+ opts.send_timeout = TEST_COVERAGE_TIMEOUT * 1000
+ opts.read_timeout = TEST_COVERAGE_TIMEOUT * 1000
+ end
+
+ local _, err = self:connect(opts)
+ if err then
+ error("Could not connect to " ..
+ (opts.host or "unknown") .. ":" .. (opts.port or "unknown") ..
+ ": " .. err)
+ end
+
+ if opts.connect_timeout and
+ opts.send_timeout and
+ opts.read_timeout
+ then
+ self:set_timeouts(opts.connect_timeout, opts.send_timeout, opts.read_timeout)
+ else
+ self:set_timeout(opts.timeout or 10000)
+ end
+end
+
+
+-- Implements http_client:get("path", [options]), as well as post, put, etc.
+-- These methods are equivalent to calling http_client:send, but are shorter
+-- They also come with a built-in assert
+for _, method_name in ipairs({"get", "post", "put", "patch", "delete", "head", "options"}) do
+ resty_http_proxy_mt[method_name] = function(self, path, options)
+ local full_options = kong.table.merge({ method = method_name:upper(), path = path}, options)
+ return assert(self:send(full_options))
+ end
+end
+
+
+--- Creates a http client from options.
+-- Instead of using this client, you'll probably want to use the pre-configured
+-- clients available as `proxy_client`, `admin_client`, etc. because these come
+-- pre-configured and connected to the underlying Kong test instance.
+--
+-- @function http_client_opts
+-- @param options connection and other options
+-- @return http client
+-- @see http_client:send
+-- @see proxy_client
+-- @see proxy_ssl_client
+-- @see admin_client
+-- @see admin_ssl_client
+local function http_client_opts(options)
+ if not options.scheme then
+ options = kong_table.cycle_aware_deep_copy(options)
+ options.scheme = "http"
+ if options.port == 443 then
+ options.scheme = "https"
+ else
+ options.scheme = "http"
+ end
+ end
+
+ local self = setmetatable(assert(http_new()), resty_http_proxy_mt)
+
+ self.options = options
+
+ if options.reopen ~= nil then
+ self.reopen = options.reopen
+ end
+
+ self:_connect()
+
+ return self
+end
+
+
+--- Creates a http client.
+-- Instead of using this client, you'll probably want to use the pre-configured
+-- clients available as `proxy_client`, `admin_client`, etc. because these come
+-- pre-configured and connected to the underlying Kong test instance.
+--
+-- @function http_client
+-- @param host hostname to connect to
+-- @param port port to connect to
+-- @param timeout in seconds
+-- @return http client
+-- @see http_client:send
+-- @see proxy_client
+-- @see proxy_ssl_client
+-- @see admin_client
+-- @see admin_ssl_client
+local function http_client(host, port, timeout)
+ if type(host) == "table" then
+ return http_client_opts(host)
+ end
+
+ if TEST_COVERAGE_MODE == "true" then
+ timeout = TEST_COVERAGE_TIMEOUT * 1000
+ end
+
+ return http_client_opts({
+ host = host,
+ port = port,
+ timeout = timeout,
+ })
+end
+
+
+--- Returns the proxy port.
+-- @function get_proxy_port
+-- @param ssl (boolean) if `true` returns the ssl port
+-- @param http2 (boolean) if `true` returns the http2 port
+local function get_proxy_port(ssl, http2)
+ if ssl == nil then ssl = false end
+ for _, entry in ipairs(conf.proxy_listeners) do
+ if entry.ssl == ssl and (http2 == nil or entry.http2 == http2) then
+ return entry.port
+ end
+ end
+ error("No proxy port found for ssl=" .. tostring(ssl), 2)
+end
+
+
+--- Returns the proxy ip.
+-- @function get_proxy_ip
+-- @param ssl (boolean) if `true` returns the ssl ip address
+-- @param http2 (boolean) if `true` returns the http2 ip address
+local function get_proxy_ip(ssl, http2)
+ if ssl == nil then ssl = false end
+ for _, entry in ipairs(conf.proxy_listeners) do
+ if entry.ssl == ssl and (http2 == nil or entry.http2 == http2) then
+ return entry.ip
+ end
+ end
+ error("No proxy ip found for ssl=" .. tostring(ssl), 2)
+end
+
+
+--- returns a pre-configured `http_client` for the Kong proxy port.
+-- @function proxy_client
+-- @param timeout (optional, number) the timeout to use
+-- @param forced_port (optional, number) if provided will override the port in
+-- the Kong configuration with this port
+local function proxy_client(timeout, forced_port, forced_ip)
+ local proxy_ip = get_proxy_ip(false)
+ local proxy_port = get_proxy_port(false)
+ assert(proxy_ip, "No http-proxy found in the configuration")
+ return http_client_opts({
+ scheme = "http",
+ host = forced_ip or proxy_ip,
+ port = forced_port or proxy_port,
+ timeout = timeout or 60000,
+ })
+end
+
+
+--- returns a pre-configured `http_client` for the Kong SSL proxy port.
+-- @function proxy_ssl_client
+-- @param timeout (optional, number) the timeout to use
+-- @param sni (optional, string) the sni to use
+local function proxy_ssl_client(timeout, sni)
+ local proxy_ip = get_proxy_ip(true, true)
+ local proxy_port = get_proxy_port(true, true)
+ assert(proxy_ip, "No https-proxy found in the configuration")
+ local client = http_client_opts({
+ scheme = "https",
+ host = proxy_ip,
+ port = proxy_port,
+ timeout = timeout or 60000,
+ ssl_verify = false,
+ ssl_server_name = sni,
+ })
+ return client
+end
+
+
+--- returns a pre-configured `http_client` for the Kong admin port.
+-- @function admin_client
+-- @param timeout (optional, number) the timeout to use
+-- @param forced_port (optional, number) if provided will override the port in
+-- the Kong configuration with this port
+local function admin_client(timeout, forced_port)
+ local admin_ip, admin_port
+ for _, entry in ipairs(conf.admin_listeners) do
+ if entry.ssl == false then
+ admin_ip = entry.ip
+ admin_port = entry.port
+ end
+ end
+ assert(admin_ip, "No http-admin found in the configuration")
+ return http_client_opts({
+ scheme = "http",
+ host = admin_ip,
+ port = forced_port or admin_port,
+ timeout = timeout or 60000,
+ reopen = true,
+ })
+end
+
+--- returns a pre-configured `http_client` for the Kong admin SSL port.
+-- @function admin_ssl_client
+-- @param timeout (optional, number) the timeout to use
+local function admin_ssl_client(timeout)
+ if TEST_COVERAGE_MODE == "true" then
+ timeout = TEST_COVERAGE_TIMEOUT * 1000
+ end
+
+ local admin_ip, admin_port
+ for _, entry in ipairs(conf.proxy_listeners) do
+ if entry.ssl == true then
+ admin_ip = entry.ip
+ admin_port = entry.port
+ end
+ end
+ assert(admin_ip, "No https-admin found in the configuration")
+ local client = http_client_opts({
+ scheme = "https",
+ host = admin_ip,
+ port = admin_port,
+ timeout = timeout or 60000,
+ reopen = true,
+ })
+ return client
+end
+
+--- returns a pre-configured `http_client` for the Kong Admin GUI.
+-- @function admin_gui_client
+-- @tparam[opt=60000] number timeout the timeout to use
+-- @tparam[opt] number forced_port if provided will override the port in
+-- the Kong configuration with this port
+-- @return http-client, see `spec.helpers.http_client`.
+local function admin_gui_client(timeout, forced_port)
+ local admin_ip = "127.0.0.1"
+ local admin_port
+ for _, entry in ipairs(conf.admin_gui_listeners) do
+ if entry.ssl == false then
+ admin_ip = entry.ip
+ admin_port = entry.port
+ end
+ end
+ admin_port = forced_port or admin_port
+ assert(admin_port, "No http-admin found in the configuration")
+ return http_client_opts({
+ scheme = "http",
+ host = admin_ip,
+ port = admin_port,
+ timeout = timeout or 60000,
+ reopen = true,
+ })
+end
+
+--- returns a pre-configured `http_client` for the Kong admin GUI SSL port.
+-- @function admin_gui_ssl_client
+-- @tparam[opt=60000] number timeout the timeout to use
+-- @tparam[opt] number forced_port if provided will override the port in
+-- the Kong configuration with this port
+-- @return http-client, see `spec.helpers.http_client`.
+local function admin_gui_ssl_client(timeout, forced_port)
+ local admin_ip = "127.0.0.1"
+ local admin_port
+ for _, entry in ipairs(conf.admin_gui_listeners) do
+ if entry.ssl == true then
+ admin_ip = entry.ip
+ admin_port = entry.port
+ end
+ end
+ admin_port = forced_port or admin_port
+ assert(admin_port, "No https-admin found in the configuration")
+ return http_client_opts({
+ scheme = "https",
+ host = admin_ip,
+ port = admin_port,
+ timeout = timeout or 60000,
+ reopen = true,
+ })
+end
+
+
+----------------
+-- HTTP2 and GRPC clients
+-- @section Shell-helpers
+
+
+-- Generate grpcurl flags from a table of `flag-value`. If `value` is not a
+-- string, value is ignored and `flag` is passed as is.
+local function gen_grpcurl_opts(opts_t)
+ local opts_l = {}
+
+ for opt, val in pairs(opts_t) do
+ if val ~= false then
+ opts_l[#opts_l + 1] = opt .. " " .. (type(val) == "string" and val or "")
+ end
+ end
+
+ return table.concat(opts_l, " ")
+end
+
+
+--- Creates an HTTP/2 client using golang's http2 package.
+--- Sets `KONG_TEST_DEBUG_HTTP2=1` env var to print debug messages.
+-- @function http2_client
+-- @param host hostname to connect to
+-- @param port port to connect to
+local function http2_client(host, port, tls)
+ local port = assert(port)
+ tls = tls or false
+
+ -- Note: set `GODEBUG=http2debug=1` is helpful if you are debugging this go program
+ local tool_path = "bin/h2client"
+ local http2_debug
+ -- note: set env var "KONG_TEST_DEBUG_HTTP2" !! the "_TEST" will be dropped
+ if os.getenv("KONG_DEBUG_HTTP2") then
+ http2_debug = true
+ tool_path = "GODEBUG=http2debug=1 bin/h2client"
+ end
+
+
+ local meta = {}
+ meta.__call = function(_, opts)
+ local headers = opts and opts.headers
+ local timeout = opts and opts.timeout
+ local body = opts and opts.body
+ local path = opts and opts.path or ""
+ local http1 = opts and opts.http_version == "HTTP/1.1"
+
+ local url = (tls and "https" or "http") .. "://" .. host .. ":" .. port .. path
+
+ local cmd = string.format("%s -url %s -skip-verify", tool_path, url)
+
+ if headers then
+ local h = {}
+ for k, v in pairs(headers) do
+ table.insert(h, string.format("%s=%s", k, v))
+ end
+ cmd = cmd .. " -headers " .. table.concat(h, ",")
+ end
+
+ if timeout then
+ cmd = cmd .. " -timeout " .. timeout
+ end
+
+ if http1 then
+ cmd = cmd .. " -http1"
+ end
+
+ --shell.run does not support '<'
+ if body then
+ cmd = cmd .. " -post"
+ end
+
+ if http2_debug then
+ print("HTTP/2 cmd:\n" .. cmd)
+ end
+
+ --100MB for retrieving stdout & stderr
+ local ok, stdout, stderr = shell.run(cmd, body, 0, 1024*1024*100)
+ assert(ok, stderr)
+
+ if http2_debug then
+ print("HTTP/2 debug:\n")
+ print(stderr)
+ end
+
+ local stdout_decoded = cjson.decode(stdout)
+ if not stdout_decoded then
+ error("Failed to decode h2client output: " .. stdout)
+ end
+
+ local headers = stdout_decoded.headers
+ headers.get = function(_, key)
+ if string.sub(key, 1, 1) == ":" then
+ key = string.sub(key, 2)
+ end
+ return headers[key]
+ end
+ setmetatable(headers, {
+ __index = function(headers, key)
+ for k, v in pairs(headers) do
+ if key:lower() == k:lower() then
+ return v
+ end
+ end
+ end
+ })
+ return stdout_decoded.body, headers
+ end
+
+ return setmetatable({}, meta)
+end
+
+--- returns a pre-configured cleartext `http2_client` for the Kong proxy port.
+-- @function proxy_client_h2c
+-- @return http2 client
+local function proxy_client_h2c()
+ local proxy_ip = get_proxy_ip(false, true)
+ local proxy_port = get_proxy_port(false, true)
+ assert(proxy_ip, "No http-proxy found in the configuration")
+ return http2_client(proxy_ip, proxy_port)
+end
+
+
+--- returns a pre-configured TLS `http2_client` for the Kong SSL proxy port.
+-- @function proxy_client_h2
+-- @return http2 client
+local function proxy_client_h2()
+ local proxy_ip = get_proxy_ip(true, true)
+ local proxy_port = get_proxy_port(true, true)
+ assert(proxy_ip, "No https-proxy found in the configuration")
+ return http2_client(proxy_ip, proxy_port, true)
+end
+
+local exec -- forward declaration
+
+--- Creates a gRPC client, based on the grpcurl CLI.
+-- @function grpc_client
+-- @param host hostname to connect to
+-- @param port port to connect to
+-- @param opts table with options supported by grpcurl
+-- @return grpc client
+local function grpc_client(host, port, opts)
+ local host = assert(host)
+ local port = assert(tostring(port))
+
+ opts = opts or {}
+ if not opts["-proto"] then
+ opts["-proto"] = MOCK_GRPC_UPSTREAM_PROTO_PATH
+ end
+
+ return setmetatable({
+ opts = opts,
+ cmd_template = string.format("bin/grpcurl %%s %s:%s %%s", host, port)
+
+ }, {
+ __call = function(t, args)
+ local service = assert(args.service)
+ local body = args.body
+
+ local t_body = type(body)
+ if t_body ~= "nil" then
+ if t_body == "table" then
+ body = cjson.encode(body)
+ end
+
+ args.opts["-d"] = string.format("'%s'", body)
+ end
+
+ local opts = gen_grpcurl_opts(pl_tablex.merge(t.opts, args.opts, true))
+ local ok, _, out, err = exec(string.format(t.cmd_template, opts, service), true)
+
+ if ok then
+ return ok, ("%s%s"):format(out or "", err or "")
+ else
+ return nil, ("%s%s"):format(out or "", err or "")
+ end
+ end
+ })
+end
+
+
+--- returns a pre-configured `grpc_client` for the Kong proxy port.
+-- @function proxy_client_grpc
+-- @param host hostname to connect to
+-- @param port port to connect to
+-- @return grpc client
+local function proxy_client_grpc(host, port)
+ local proxy_ip = host or get_proxy_ip(false, true)
+ local proxy_port = port or get_proxy_port(false, true)
+ assert(proxy_ip, "No http-proxy found in the configuration")
+ return grpc_client(proxy_ip, proxy_port, {["-plaintext"] = true})
+end
+
+--- returns a pre-configured `grpc_client` for the Kong SSL proxy port.
+-- @function proxy_client_grpcs
+-- @param host hostname to connect to
+-- @param port port to connect to
+-- @return grpc client
+local function proxy_client_grpcs(host, port)
+ local proxy_ip = host or get_proxy_ip(true, true)
+ local proxy_port = port or get_proxy_port(true, true)
+ assert(proxy_ip, "No https-proxy found in the configuration")
+ return grpc_client(proxy_ip, proxy_port, {["-insecure"] = true})
+end
+
+
+---
+-- Reconfiguration completion detection helpers
+--
+
+local MAX_RETRY_TIME = 10
+
+--- Set up admin client and proxy client to so that interactions with the proxy client
+-- wait for preceding admin API client changes to have completed.
+
+-- @function make_synchronized_clients
+-- @param clients table with admin_client and proxy_client fields (both optional)
+-- @return admin_client, proxy_client
+
+local function make_synchronized_clients(clients)
+ clients = clients or {}
+ local synchronized_proxy_client = clients.proxy_client or proxy_client()
+ local synchronized_admin_client = clients.admin_client or admin_client()
+
+ -- Install the reconfiguration completion detection plugin
+ local res = synchronized_admin_client:post("/plugins", {
+ headers = { ["Content-Type"] = "application/json" },
+ body = {
+ name = "reconfiguration-completion",
+ config = {
+ version = "0",
+ }
+ },
+ })
+ local body = luassert.res_status(201, res)
+ local plugin = cjson.decode(body)
+ local plugin_id = plugin.id
+
+ -- Wait until the plugin is active on the proxy path, indicated by the presence of the X-Kong-Reconfiguration-Status header
+ luassert.eventually(function()
+ res = synchronized_proxy_client:get("/non-existent-proxy-path")
+ luassert.res_status(404, res)
+ luassert.equals("unknown", res.headers['x-kong-reconfiguration-status'])
+ end)
+ .has_no_error()
+
+ -- Save the original request functions for the admin and proxy client
+ local proxy_request = synchronized_proxy_client.request
+ local admin_request = synchronized_admin_client.request
+
+ local current_version = 0 -- incremented whenever a configuration change is made through the admin API
+ local last_configured_version = 0 -- current version of the reconfiguration-completion plugin's configuration
+
+ -- Wrap the admin API client request
+ function synchronized_admin_client.request(client, opts)
+ -- Whenever the configuration is changed through the admin API, increment the current version number
+ if opts.method == "POST" or opts.method == "PUT" or opts.method == "PATCH" or opts.method == "DELETE" then
+ current_version = current_version + 1
+ end
+ return admin_request(client, opts)
+ end
+
+ function synchronized_admin_client.synchronize_sibling(self, sibling)
+ sibling.request = self.request
+ end
+
+ -- Wrap the proxy client request
+ function synchronized_proxy_client.request(client, opts)
+ -- If the configuration has been changed through the admin API, update the version number in the
+ -- reconfiguration-completion plugin.
+ if current_version > last_configured_version then
+ last_configured_version = current_version
+ res = admin_request(synchronized_admin_client, {
+ method = "PATCH",
+ path = "/plugins/" .. plugin_id,
+ headers = { ["Content-Type"] = "application/json" },
+ body = cjson.encode({
+ config = {
+ version = tostring(current_version),
+ }
+ }),
+ })
+ luassert.res_status(200, res)
+ end
+
+ -- Retry the request until the reconfiguration is complete and the reconfiguration completion
+ -- plugin on the database has been updated to the current version.
+ if not opts.headers then
+ opts.headers = {}
+ end
+ opts.headers["If-Kong-Configuration-Version"] = tostring(current_version)
+ local retry_until = ngx.now() + MAX_RETRY_TIME
+ local err
+ :: retry ::
+ res, err = proxy_request(client, opts)
+ if err then
+ return res, err
+ end
+ if res.headers['x-kong-reconfiguration-status'] ~= "complete" then
+ res:read_body()
+ ngx.sleep(res.headers['retry-after'] or 1)
+ if ngx.now() < retry_until then
+ goto retry
+ end
+ return nil, "reconfiguration did not occur within " .. MAX_RETRY_TIME .. " seconds"
+ end
+ return res, err
+ end
+
+ function synchronized_proxy_client.synchronize_sibling(self, sibling)
+ sibling.request = self.request
+ end
+
+ return synchronized_proxy_client, synchronized_admin_client
+end
+
+---
+-- TCP/UDP server helpers
+--
+-- @section servers
+
+
+--- Starts a local TCP server.
+-- Accepts a single connection (or multiple, if given `opts.requests`)
+-- and then closes, echoing what was received (last read, in case
+-- of multiple requests).
+-- @function tcp_server
+-- @tparam number port The port where the server will be listening on
+-- @tparam[opt] table opts options defining the server's behavior with the following fields:
+-- @tparam[opt=60] number opts.timeout time (in seconds) after which the server exits
+-- @tparam[opt=1] number opts.requests the number of requests to accept before exiting
+-- @tparam[opt=false] bool opts.tls make it a TLS server if truthy
+-- @tparam[opt] string opts.prefix a prefix to add to the echoed data received
+-- @return A thread object (from the `llthreads2` Lua package)
+-- @see kill_tcp_server
+local function tcp_server(port, opts)
+ local threads = require "llthreads2.ex"
+ opts = opts or {}
+ if TEST_COVERAGE_MODE == "true" then
+ opts.timeout = TEST_COVERAGE_TIMEOUT
+ end
+ local thread = threads.new({
+ function(port, opts)
+ local socket = require "socket"
+ local server = assert(socket.tcp())
+ server:settimeout(opts.timeout or 60)
+ assert(server:setoption("reuseaddr", true))
+ assert(server:bind("*", port))
+ assert(server:listen())
+ local line
+ local oks, fails = 0, 0
+ local handshake_done = false
+ local n = opts.requests or 1
+ for _ = 1, n + 1 do
+ local client, err
+ if opts.timeout then
+ client, err = server:accept()
+ if err == "timeout" then
+ line = "timeout"
+ break
+
+ else
+ assert(client, err)
+ end
+
+ else
+ client = assert(server:accept())
+ end
+
+ if opts.tls and handshake_done then
+ local ssl = require "spec.helpers.ssl"
+
+ local params = {
+ mode = "server",
+ protocol = "any",
+ key = "spec/fixtures/kong_spec.key",
+ certificate = "spec/fixtures/kong_spec.crt",
+ }
+
+ client = ssl.wrap(client, params)
+ client:dohandshake()
+ end
+
+ line, err = client:receive()
+ if err == "closed" then
+ fails = fails + 1
+
+ else
+ if not handshake_done then
+ assert(line == "\\START")
+ client:send("\\OK\n")
+ handshake_done = true
+
+ else
+ if line == "@DIE@" then
+ client:send(string.format("%d:%d\n", oks, fails))
+ client:close()
+ break
+ end
+
+ oks = oks + 1
+
+ client:send((opts.prefix or "") .. line .. "\n")
+ end
+
+ client:close()
+ end
+ end
+ server:close()
+ return line
+ end
+ }, port, opts)
+
+ local thr = thread:start()
+
+ -- not necessary for correctness because we do the handshake,
+ -- but avoids harmless "connection error" messages in the wait loop
+ -- in case the client is ready before the server below.
+ ngx.sleep(0.001)
+
+ local sock = ngx.socket.tcp()
+ sock:settimeout(0.01)
+ while true do
+ if sock:connect("localhost", port) then
+ sock:send("\\START\n")
+ local ok = sock:receive()
+ sock:close()
+ if ok == "\\OK" then
+ break
+ end
+ end
+ end
+ sock:close()
+
+ return thr
+end
+
+
+--- Stops a local TCP server.
+-- A server previously created with `tcp_server` can be stopped prematurely by
+-- calling this function.
+-- @function kill_tcp_server
+-- @param port the port the TCP server is listening on.
+-- @return oks, fails; the number of successes and failures processed by the server
+-- @see tcp_server
+local function kill_tcp_server(port)
+ local sock = ngx.socket.tcp()
+ assert(sock:connect("localhost", port))
+ assert(sock:send("@DIE@\n"))
+ local str = assert(sock:receive())
+ assert(sock:close())
+ local oks, fails = str:match("(%d+):(%d+)")
+ return tonumber(oks), tonumber(fails)
+end
+
+local code_status = {
+ [200] = "OK",
+ [201] = "Created",
+ [202] = "Accepted",
+ [203] = "Non-Authoritative Information",
+ [204] = "No Content",
+ [205] = "Reset Content",
+ [206] = "Partial Content",
+ [207] = "Multi-Status",
+ [300] = "Multiple Choices",
+ [301] = "Moved Permanently",
+ [302] = "Found",
+ [303] = "See Other",
+ [304] = "Not Modified",
+ [305] = "Use Proxy",
+ [307] = "Temporary Redirect",
+ [308] = "Permanent Redirect",
+ [400] = "Bad Request",
+ [401] = "Unauthorized",
+ [402] = "Payment Required",
+ [403] = "Forbidden",
+ [404] = "Not Found",
+ [405] = "Method Not Allowed",
+ [406] = "Not Acceptable",
+ [407] = "Proxy Authentication Required",
+ [408] = "Request Timeout",
+ [409] = "Conflict",
+ [410] = "Gone",
+ [411] = "Length Required",
+ [412] = "Precondition Failed",
+ [413] = "Payload Too Large",
+ [414] = "URI Too Long",
+ [415] = "Unsupported Media Type",
+ [416] = "Range Not Satisfiable",
+ [417] = "Expectation Failed",
+ [418] = "I'm a teapot",
+ [422] = "Unprocessable Entity",
+ [423] = "Locked",
+ [424] = "Failed Dependency",
+ [426] = "Upgrade Required",
+ [428] = "Precondition Required",
+ [429] = "Too Many Requests",
+ [431] = "Request Header Fields Too Large",
+ [451] = "Unavailable For Legal Reasons",
+ [500] = "Internal Server Error",
+ [501] = "Not Implemented",
+ [502] = "Bad Gateway",
+ [503] = "Service Unavailable",
+ [504] = "Gateway Timeout",
+ [505] = "HTTP Version Not Supported",
+ [506] = "Variant Also Negotiates",
+ [507] = "Insufficient Storage",
+ [508] = "Loop Detected",
+ [510] = "Not Extended",
+ [511] = "Network Authentication Required",
+}
+
+
+local EMPTY = {}
+
+
+local function handle_response(code, body, headers)
+ if not code then
+ code = 500
+ body = ""
+ headers = EMPTY
+ end
+
+ local head_str = ""
+
+ for k, v in pairs(headers or EMPTY) do
+ head_str = head_str .. k .. ": " .. v .. "\r\n"
+ end
+
+ return code .. " " .. code_status[code] .. " HTTP/1.1" .. "\r\n" ..
+ "Content-Length: " .. #body .. "\r\n" ..
+ "Connection: close\r\n" ..
+ head_str ..
+ "\r\n" ..
+ body
+end
+
+
+local function handle_request(client, response)
+ local lines = {}
+ local headers = {}
+ local line, err
+
+ local content_length
+ repeat
+ line, err = client:receive("*l")
+ if err then
+ return nil, err
+ else
+ local k, v = line:match("^([^:]+):%s*(.+)$")
+ if k then
+ headers[k] = v
+ if k:lower() == "content-length" then
+ content_length = tonumber(v)
+ end
+ end
+ table.insert(lines, line)
+ end
+ until line == ""
+
+ local method = lines[1]:match("^(%S+)%s+(%S+)%s+(%S+)$")
+ local method_lower = method:lower()
+
+ local body
+ if content_length then
+ body = client:receive(content_length)
+
+ elseif method_lower == "put" or method_lower == "post" then
+ body = client:receive("*a")
+ end
+
+ local response_str
+ local meta = getmetatable(response)
+ if type(response) == "function" or (meta and meta.__call) then
+ response_str = response(lines, body, headers)
+
+ elseif type(response) == "table" and response.code then
+ response_str = handle_response(response.code, response.body, response.headers)
+
+ elseif type(response) == "table" and response[1] then
+ response_str = handle_response(response[1], response[2], response[3])
+
+ elseif type(response) == "string" then
+ response_str = response
+
+ elseif response == nil then
+ response_str = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"
+ end
+
+
+ client:send(response_str)
+ return lines, body, headers
+end
+
+
+--- Start a local HTTP server with coroutine.
+--
+-- **DEPRECATED**: please use `spec.helpers.http_mock` instead.
+--
+-- local mock = helpers.http_mock(1234, { timeout = 0.1 })
+-- wait for a request, and respond with the custom response
+-- the request is returned as the function's return values
+-- return nil, err if error
+-- local lines, body, headers = mock(custom_response)
+-- local lines, body, headers = mock()
+-- mock("closing", true) -- close the server
+local function http_mock(port, opts)
+ local socket = require "socket"
+ local server = assert(socket.tcp())
+ if TEST_COVERAGE_MODE == "true" then
+ opts.timeout = TEST_COVERAGE_TIMEOUT
+ end
+ server:settimeout(opts and opts.timeout or 60)
+ assert(server:setoption('reuseaddr', true))
+ assert(server:bind("*", port))
+ assert(server:listen())
+ return coroutine.wrap(function(response, exit)
+ local lines, body, headers
+ -- start listening
+ while not exit do
+ local client, err = server:accept()
+ if err then
+ lines, body = false, err
+
+ else
+ lines, body, headers = handle_request(client, response)
+ client:close()
+ end
+
+ response, exit = coroutine.yield(lines, body, headers)
+ end
+
+ server:close()
+ return true
+ end)
+end
+
+
+--- Starts a local UDP server.
+-- Reads the specified number of packets and then closes.
+-- The server-thread return values depend on `n`:
+--
+-- * `n = 1`; returns the received packet (string), or `nil + err`
+--
+-- * `n > 1`; returns `data + err`, where `data` will always be a table with the
+-- received packets. So `err` must explicitly be checked for errors.
+-- @function udp_server
+-- @tparam[opt] number port The port the server will be listening on, default: `MOCK_UPSTREAM_PORT`
+-- @tparam[opt=1] number n The number of packets that will be read
+-- @tparam[opt=360] number timeout Timeout per read (default 360)
+-- @return A thread object (from the `llthreads2` Lua package)
+local function udp_server(port, n, timeout)
+ local threads = require "llthreads2.ex"
+
+ if TEST_COVERAGE_MODE == "true" then
+ timeout = TEST_COVERAGE_TIMEOUT
+ end
+
+ local thread = threads.new({
+ function(port, n, timeout)
+ local socket = require "socket"
+ local server = assert(socket.udp())
+ server:settimeout(timeout or 360)
+ server:setoption("reuseaddr", true)
+ server:setsockname("127.0.0.1", port)
+ local err
+ local data = {}
+ local handshake_done = false
+ local i = 0
+ while i < n do
+ local pkt, rport
+ pkt, err, rport = server:receivefrom()
+ if not pkt then
+ break
+ end
+ if pkt == "KONG_UDP_HELLO" then
+ if not handshake_done then
+ handshake_done = true
+ server:sendto("KONG_UDP_READY", "127.0.0.1", rport)
+ end
+ else
+ i = i + 1
+ data[i] = pkt
+ err = nil -- upon succes it would contain the remote ip address
+ end
+ end
+ server:close()
+ return (n > 1 and data or data[1]), err
+ end
+ }, port or MOCK_UPSTREAM_PORT, n or 1, timeout)
+ thread:start()
+
+ local socket = require "socket"
+ local handshake = socket.udp()
+ handshake:settimeout(0.01)
+ handshake:setsockname("127.0.0.1", 0)
+ while true do
+ handshake:sendto("KONG_UDP_HELLO", "127.0.0.1", port)
+ local data = handshake:receive()
+ if data == "KONG_UDP_READY" then
+ break
+ end
+ end
+ handshake:close()
+
+ return thread
+end
+
+--------------------
+-- Custom assertions
+--
+-- @section assertions
+
+local say = require "say"
+require("spec.helpers.wait")
+
+--- Waits until a specific condition is met.
+-- The check function will repeatedly be called (with a fixed interval), until
+-- the condition is met. Throws an error on timeout.
+--
+-- NOTE: this is a regular Lua function, not a Luassert assertion.
+-- @function wait_until
+-- @param f check function that should return `truthy` when the condition has
+-- been met
+-- @param timeout (optional) maximum time to wait after which an error is
+-- thrown, defaults to 5.
+-- @param step (optional) interval between checks, defaults to 0.05.
+-- @return nothing. It returns when the condition is met, or throws an error
+-- when it times out.
+-- @usage
+-- -- wait 10 seconds for a file "myfilename" to appear
+-- helpers.wait_until(function() return file_exist("myfilename") end, 10)
+local function wait_until(f, timeout, step)
+ if TEST_COVERAGE_MODE == "true" then
+ timeout = TEST_COVERAGE_TIMEOUT
+ end
+
+ luassert.wait_until({
+ condition = "truthy",
+ fn = f,
+ timeout = timeout,
+ step = step,
+ })
+end
+
+
+--- Waits until no Lua error occurred
+-- The check function will repeatedly be called (with a fixed interval), until
+-- there is no Lua error occurred
+--
+-- NOTE: this is a regular Lua function, not a Luassert assertion.
+-- @function pwait_until
+-- @param f check function
+-- @param timeout (optional) maximum time to wait after which an error is
+-- thrown, defaults to 5.
+-- @param step (optional) interval between checks, defaults to 0.05.
+-- @return nothing. It returns when the condition is met, or throws an error
+-- when it times out.
+local function pwait_until(f, timeout, step)
+ if TEST_COVERAGE_MODE == "true" then
+ timeout = TEST_COVERAGE_TIMEOUT
+ end
+
+ luassert.wait_until({
+ condition = "no_error",
+ fn = f,
+ timeout = timeout,
+ step = step,
+ })
+end
+
+
+--- Wait for some timers, throws an error on timeout.
+--
+-- NOTE: this is a regular Lua function, not a Luassert assertion.
+-- @function wait_timer
+-- @tparam string timer_name_pattern the call will apply to all timers matching this string
+-- @tparam boolean plain if truthy, the `timer_name_pattern` will be matched plain, so without pattern matching
+-- @tparam string mode one of: "all-finish", "all-running", "any-finish", "any-running", or "worker-wide-all-finish"
+--
+-- any-finish: At least one of the timers that were matched finished
+--
+-- all-finish: All timers that were matched finished
+--
+-- any-running: At least one of the timers that were matched is running
+--
+-- all-running: All timers that were matched are running
+--
+-- worker-wide-all-finish: All the timers in the worker that were matched finished
+-- @tparam number timeout maximum time to wait (optional, default: 2)
+-- @tparam number admin_client_timeout, to override the default timeout setting (optional)
+-- @tparam number forced_admin_port to override the default port of admin API (optional)
+-- @usage helpers.wait_timer("rate-limiting", true, "all-finish", 10)
+local function wait_timer(timer_name_pattern, plain,
+ mode, timeout,
+ admin_client_timeout, forced_admin_port)
+ if not timeout then
+ timeout = 2
+ end
+
+ local _admin_client
+
+ local all_running_each_worker = nil
+ local all_finish_each_worker = nil
+ local any_running_each_worker = nil
+ local any_finish_each_worker = nil
+
+ wait_until(function ()
+ if _admin_client then
+ _admin_client:close()
+ end
+
+ _admin_client = admin_client(admin_client_timeout, forced_admin_port)
+ local res = assert(_admin_client:get("/timers"))
+ local body = luassert.res_status(200, res)
+ local json = assert(cjson.decode(body))
+ local worker_id = json.worker.id
+ local worker_count = json.worker.count
+
+ if not all_running_each_worker then
+ all_running_each_worker = {}
+ all_finish_each_worker = {}
+ any_running_each_worker = {}
+ any_finish_each_worker = {}
+
+ for i = 0, worker_count - 1 do
+ all_running_each_worker[i] = false
+ all_finish_each_worker[i] = false
+ any_running_each_worker[i] = false
+ any_finish_each_worker[i] = false
+ end
+ end
+
+ local is_matched = false
+
+ for timer_name, timer in pairs(json.stats.timers) do
+ if string.find(timer_name, timer_name_pattern, 1, plain) then
+ is_matched = true
+
+ all_finish_each_worker[worker_id] = false
+
+ if timer.is_running then
+ all_running_each_worker[worker_id] = true
+ any_running_each_worker[worker_id] = true
+ goto continue
+ end
+
+ all_running_each_worker[worker_id] = false
+
+ goto continue
+ end
+
+ ::continue::
+ end
+
+ if not is_matched then
+ any_finish_each_worker[worker_id] = true
+ all_finish_each_worker[worker_id] = true
+ end
+
+ local all_running = false
+
+ local all_finish = false
+ local all_finish_worker_wide = true
+
+ local any_running = false
+ local any_finish = false
+
+ for _, v in pairs(all_running_each_worker) do
+ all_running = all_running or v
+ end
+
+ for _, v in pairs(all_finish_each_worker) do
+ all_finish = all_finish or v
+ all_finish_worker_wide = all_finish_worker_wide and v
+ end
+
+ for _, v in pairs(any_running_each_worker) do
+ any_running = any_running or v
+ end
+
+ for _, v in pairs(any_finish_each_worker) do
+ any_finish = any_finish or v
+ end
+
+ if mode == "all-running" then
+ return all_running
+ end
+
+ if mode == "all-finish" then
+ return all_finish
+ end
+
+ if mode == "worker-wide-all-finish" then
+ return all_finish_worker_wide
+ end
+
+ if mode == "any-finish" then
+ return any_finish
+ end
+
+ if mode == "any-running" then
+ return any_running
+ end
+
+ error("unexpected error")
+ end, timeout)
+end
+
+
+--- Waits for invalidation of a cached key by polling the mgt-api
+-- and waiting for a 404 response. Throws an error on timeout.
+--
+-- NOTE: this is a regular Lua function, not a Luassert assertion.
+-- @function wait_for_invalidation
+-- @param key (string) the cache-key to check
+-- @param timeout (optional) in seconds (for default see `wait_until`).
+-- @return nothing. It returns when the key is invalidated, or throws an error
+-- when it times out.
+-- @usage
+-- local cache_key = "abc123"
+-- helpers.wait_for_invalidation(cache_key, 10)
+local function wait_for_invalidation(key, timeout)
+ -- TODO: this code is duplicated all over the codebase,
+ -- search codebase for "/cache/" endpoint
+ local api_client = admin_client()
+ wait_until(function()
+ local res = api_client:get("/cache/" .. key)
+ res:read_body()
+ return res.status == 404
+ end, timeout)
+end
+
+
+--- Wait for all targets, upstreams, services, and routes update
+--
+-- NOTE: this function is not available for DBless-mode
+-- @function wait_for_all_config_update
+-- @tparam[opt] table opts a table contains params
+-- @tparam[opt=30] number opts.timeout maximum seconds to wait, defatuls is 30
+-- @tparam[opt] number opts.admin_client_timeout to override the default timeout setting
+-- @tparam[opt] number opts.forced_admin_port to override the default Admin API port
+-- @tparam[opt] bollean opts.stream_enabled to enable stream module
+-- @tparam[opt] number opts.proxy_client_timeout to override the default timeout setting
+-- @tparam[opt] number opts.forced_proxy_port to override the default proxy port
+-- @tparam[opt] number opts.stream_port to set the stream port
+-- @tparam[opt] string opts.stream_ip to set the stream ip
+-- @tparam[opt=false] boolean opts.override_global_rate_limiting_plugin to override the global rate-limiting plugin in waiting
+-- @tparam[opt=false] boolean opts.override_global_key_auth_plugin to override the global key-auth plugin in waiting
+local function wait_for_all_config_update(opts)
+ opts = opts or {}
+ if TEST_COVERAGE_MODE == "true" then
+ opts.timeout = TEST_COVERAGE_TIMEOUT
+ end
+ local timeout = opts.timeout or 30
+ local admin_client_timeout = opts.admin_client_timeout
+ local forced_admin_port = opts.forced_admin_port
+ local proxy_client_timeout = opts.proxy_client_timeout
+ local forced_proxy_port = opts.forced_proxy_port
+ local stream_port = opts.stream_port
+ local stream_ip = opts.stream_ip
+ local stream_enabled = opts.stream_enabled or false
+ local override_rl = opts.override_global_rate_limiting_plugin or false
+ local override_auth = opts.override_global_key_auth_plugin or false
+ local headers = opts.override_default_headers or { ["Content-Type"] = "application/json" }
+ local disable_ipv6 = opts.disable_ipv6 or false
+
+ local function call_admin_api(method, path, body, expected_status, headers)
+ local client = admin_client(admin_client_timeout, forced_admin_port)
+
+ local res
+
+ if string.upper(method) == "POST" then
+ res = client:post(path, {
+ headers = headers,
+ body = body,
+ })
+
+ elseif string.upper(method) == "DELETE" then
+ res = client:delete(path, {
+ headers = headers
+ })
+ end
+
+ local ok, json_or_nil_or_err = pcall(function ()
+ assert(res.status == expected_status, "unexpected response code: " .. res.status)
+
+ if string.upper(method) == "DELETE" then
+ return
+ end
+
+ local json = cjson.decode((res:read_body()))
+ assert(json ~= nil, "unexpected response body")
+ return json
+ end)
+
+ client:close()
+
+ assert(ok, json_or_nil_or_err)
+
+ return json_or_nil_or_err
+ end
+
+ local upstream_id, target_id, service_id, route_id
+ local stream_upstream_id, stream_target_id, stream_service_id, stream_route_id
+ local consumer_id, rl_plugin_id, key_auth_plugin_id, credential_id
+ local upstream_name = "really.really.really.really.really.really.really.mocking.upstream.test"
+ local service_name = "really-really-really-really-really-really-really-mocking-service"
+ local stream_upstream_name = "stream-really.really.really.really.really.really.really.mocking.upstream.test"
+ local stream_service_name = "stream-really-really-really-really-really-really-really-mocking-service"
+ local route_path = "/really-really-really-really-really-really-really-mocking-route"
+ local key_header_name = "really-really-really-really-really-really-really-mocking-key"
+ local consumer_name = "really-really-really-really-really-really-really-mocking-consumer"
+ local test_credentials = "really-really-really-really-really-really-really-mocking-credentials"
+
+ local host = "127.0.0.1"
+ local port = get_available_port()
+
+ local server = https_server.new(port, host, "http", nil, 1, nil, disable_ipv6)
+
+ server:start()
+
+ -- create mocking upstream
+ local res = assert(call_admin_api("POST",
+ "/upstreams",
+ { name = upstream_name },
+ 201, headers))
+ upstream_id = res.id
+
+ -- create mocking target to mocking upstream
+ res = assert(call_admin_api("POST",
+ string.format("/upstreams/%s/targets", upstream_id),
+ { target = host .. ":" .. port },
+ 201, headers))
+ target_id = res.id
+
+ -- create mocking service to mocking upstream
+ res = assert(call_admin_api("POST",
+ "/services",
+ { name = service_name, url = "http://" .. upstream_name .. "/always_200" },
+ 201, headers))
+ service_id = res.id
+
+ -- create mocking route to mocking service
+ res = assert(call_admin_api("POST",
+ string.format("/services/%s/routes", service_id),
+ { paths = { route_path }, strip_path = true, path_handling = "v0",},
+ 201, headers))
+ route_id = res.id
+
+ if override_rl then
+ -- create rate-limiting plugin to mocking mocking service
+ res = assert(call_admin_api("POST",
+ string.format("/services/%s/plugins", service_id),
+ { name = "rate-limiting", config = { minute = 999999, policy = "local" } },
+ 201, headers))
+ rl_plugin_id = res.id
+ end
+
+ if override_auth then
+ -- create key-auth plugin to mocking mocking service
+ res = assert(call_admin_api("POST",
+ string.format("/services/%s/plugins", service_id),
+ { name = "key-auth", config = { key_names = { key_header_name } } },
+ 201, headers))
+ key_auth_plugin_id = res.id
+
+ -- create consumer
+ res = assert(call_admin_api("POST",
+ "/consumers",
+ { username = consumer_name },
+ 201, headers))
+ consumer_id = res.id
+
+ -- create credential to key-auth plugin
+ res = assert(call_admin_api("POST",
+ string.format("/consumers/%s/key-auth", consumer_id),
+ { key = test_credentials },
+ 201, headers))
+ credential_id = res.id
+ end
+
+ if stream_enabled then
+ -- create mocking upstream
+ local res = assert(call_admin_api("POST",
+ "/upstreams",
+ { name = stream_upstream_name },
+ 201, headers))
+ stream_upstream_id = res.id
+
+ -- create mocking target to mocking upstream
+ res = assert(call_admin_api("POST",
+ string.format("/upstreams/%s/targets", stream_upstream_id),
+ { target = host .. ":" .. port },
+ 201, headers))
+ stream_target_id = res.id
+
+ -- create mocking service to mocking upstream
+ res = assert(call_admin_api("POST",
+ "/services",
+ { name = stream_service_name, url = "tcp://" .. stream_upstream_name },
+ 201, headers))
+ stream_service_id = res.id
+
+ -- create mocking route to mocking service
+ res = assert(call_admin_api("POST",
+ string.format("/services/%s/routes", stream_service_id),
+ { destinations = { { port = stream_port }, }, protocols = { "tcp" },},
+ 201, headers))
+ stream_route_id = res.id
+ end
+
+ local ok, err = pcall(function ()
+ -- wait for mocking route ready
+ pwait_until(function ()
+ local proxy = proxy_client(proxy_client_timeout, forced_proxy_port)
+
+ if override_auth then
+ res = proxy:get(route_path, { headers = { [key_header_name] = test_credentials } })
+
+ else
+ res = proxy:get(route_path)
+ end
+
+ local ok, err = pcall(assert, res.status == 200)
+ proxy:close()
+ assert(ok, err)
+ end, timeout / 2)
+
+ if stream_enabled then
+ pwait_until(function ()
+ local proxy = proxy_client(proxy_client_timeout, stream_port, stream_ip)
+
+ res = proxy:get("/always_200")
+ local ok, err = pcall(assert, res.status == 200)
+ proxy:close()
+ assert(ok, err)
+ end, timeout)
+ end
+ end)
+ if not ok then
+ server:shutdown()
+ error(err)
+ end
+
+ -- delete mocking configurations
+ if override_auth then
+ call_admin_api("DELETE", string.format("/consumers/%s/key-auth/%s", consumer_id, credential_id), nil, 204, headers)
+ call_admin_api("DELETE", string.format("/consumers/%s", consumer_id), nil, 204, headers)
+ call_admin_api("DELETE", "/plugins/" .. key_auth_plugin_id, nil, 204, headers)
+ end
+
+ if override_rl then
+ call_admin_api("DELETE", "/plugins/" .. rl_plugin_id, nil, 204, headers)
+ end
+
+ call_admin_api("DELETE", "/routes/" .. route_id, nil, 204, headers)
+ call_admin_api("DELETE", "/services/" .. service_id, nil, 204, headers)
+ call_admin_api("DELETE", string.format("/upstreams/%s/targets/%s", upstream_id, target_id), nil, 204, headers)
+ call_admin_api("DELETE", "/upstreams/" .. upstream_id, nil, 204, headers)
+
+ if stream_enabled then
+ call_admin_api("DELETE", "/routes/" .. stream_route_id, nil, 204, headers)
+ call_admin_api("DELETE", "/services/" .. stream_service_id, nil, 204, headers)
+ call_admin_api("DELETE", string.format("/upstreams/%s/targets/%s", stream_upstream_id, stream_target_id), nil, 204, headers)
+ call_admin_api("DELETE", "/upstreams/" .. stream_upstream_id, nil, 204, headers)
+ end
+
+ ok, err = pcall(function ()
+ -- wait for mocking configurations to be deleted
+ pwait_until(function ()
+ local proxy = proxy_client(proxy_client_timeout, forced_proxy_port)
+ res = proxy:get(route_path)
+ local ok, err = pcall(assert, res.status == 404)
+ proxy:close()
+ assert(ok, err)
+ end, timeout / 2)
+ end)
+
+ server:shutdown()
+
+ if not ok then
+ error(err)
+ end
+
+end
+
+
+--- Waits for a file to meet a certain condition
+-- The check function will repeatedly be called (with a fixed interval), until
+-- there is no Lua error occurred
+--
+-- NOTE: this is a regular Lua function, not a Luassert assertion.
+-- @function wait_for_file
+-- @tparam string mode one of:
+--
+-- "file", "directory", "link", "socket", "named pipe", "char device", "block device", "other"
+--
+-- @tparam string path the file path
+-- @tparam[opt=10] number timeout maximum seconds to wait
+local function wait_for_file(mode, path, timeout)
+ pwait_until(function()
+ local result, err = lfs.attributes(path, "mode")
+ local msg = string.format("failed to wait for the mode (%s) of '%s': %s",
+ mode, path, tostring(err))
+ assert(result == mode, msg)
+ end, timeout or 10)
+end
+
+
+local wait_for_file_contents
+do
+ --- Wait until a file exists and is non-empty.
+ --
+ -- If, after the timeout is reached, the file does not exist, is not
+ -- readable, or is empty, an assertion error will be raised.
+ --
+ -- @function wait_for_file_contents
+ -- @param fname the filename to wait for
+ -- @param timeout (optional) maximum time to wait after which an error is
+ -- thrown, defaults to 10.
+ -- @return contents the file contents, as a string
+ function wait_for_file_contents(fname, timeout)
+ assert(type(fname) == "string",
+ "filename must be a string")
+
+ timeout = timeout or 10
+ assert(type(timeout) == "number" and timeout >= 0,
+ "timeout must be nil or a number >= 0")
+
+ local data = pl_file.read(fname)
+ if data and #data > 0 then
+ return data
+ end
+
+ pcall(wait_until, function()
+ data = pl_file.read(fname)
+ return data and #data > 0
+ end, timeout)
+
+ assert(data, "file (" .. fname .. ") does not exist or is not readable"
+ .. " after " .. tostring(timeout) .. " seconds")
+
+ assert(#data > 0, "file (" .. fname .. ") exists but is empty after " ..
+ tostring(timeout) .. " seconds")
+
+ return data
+ end
+end
+
+
+
+--- Generic modifier "response".
+-- Will set a "response" value in the assertion state, so following
+-- assertions will operate on the value set.
+-- @function response
+-- @param response_obj results from `http_client:send` function (or any of the
+-- shortcuts `client:get`, `client:post`, etc).
+-- @usage
+-- local res = client:get("/request", { .. request options here ..})
+-- local response_length = assert.response(res).has.header("Content-Length")
+local function modifier_response(state, arguments, level)
+ assert(arguments.n > 0,
+ "response modifier requires a response object as argument")
+
+ local res = arguments[1]
+
+ assert(type(res) == "table" and type(res.read_body) == "function",
+ "response modifier requires a response object as argument, got: " .. tostring(res))
+
+ rawset(state, "kong_response", res)
+ rawset(state, "kong_request", nil)
+
+ return state
+end
+luassert:register("modifier", "response", modifier_response)
+
+
+--- Generic modifier "request".
+-- Will set a "request" value in the assertion state, so following
+-- assertions will operate on the value set.
+--
+-- The request must be inside a 'response' from the `mock_upstream`. If a request
+-- is send to the `mock_upstream` endpoint `"/request"`, it will echo the request
+-- received in the body of the response.
+-- @function request
+-- @param response_obj results from `http_client:send` function (or any of the
+-- shortcuts `client:get`, `client:post`, etc).
+-- @usage
+-- local res = client:post("/request", {
+-- headers = { ["Content-Type"] = "application/json" },
+-- body = { hello = "world" },
+-- })
+-- local request_length = assert.request(res).has.header("Content-Length")
+local function modifier_request(state, arguments, level)
+ local generic = "The assertion 'request' modifier takes a http response"
+ .. " object as input to decode the json-body returned by"
+ .. " mock_upstream, to retrieve the proxied request."
+
+ local res = arguments[1]
+
+ assert(type(res) == "table" and type(res.read_body) == "function",
+ "Expected a http response object, got '" .. tostring(res) .. "'. " .. generic)
+
+ local body, request, err
+ body = assert(res:read_body())
+ request, err = cjson.decode(body)
+
+ assert(request, "Expected the http response object to have a json encoded body,"
+ .. " but decoding gave error '" .. tostring(err) .. "'. Obtained body: "
+ .. body .. "\n." .. generic)
+
+
+ if lookup((res.headers or {}),"X-Powered-By") ~= "mock_upstream" then
+ error("Could not determine the response to be from mock_upstream")
+ end
+
+ rawset(state, "kong_request", request)
+ rawset(state, "kong_response", nil)
+
+ return state
+end
+luassert:register("modifier", "request", modifier_request)
+
+
+--- Generic fail assertion. A convenience function for debugging tests, always
+-- fails. It will output the values it was called with as a table, with an `n`
+-- field to indicate the number of arguments received. See also `intercept`.
+-- @function fail
+-- @param ... any set of parameters to be displayed with the failure
+-- @see intercept
+-- @usage
+-- assert.fail(some, value)
+local function fail(state, args)
+ local out = {}
+ for k,v in pairs(args) do out[k] = v end
+ args[1] = out
+ args.n = 1
+ return false
+end
+say:set("assertion.fail.negative", [[
+Fail assertion was called with the following parameters (formatted as a table);
+%s
+]])
+luassert:register("assertion", "fail", fail,
+ "assertion.fail.negative",
+ "assertion.fail.negative")
+
+
+--- Assertion to check whether a value lives in an array.
+-- @function contains
+-- @param expected The value to search for
+-- @param array The array to search for the value
+-- @param pattern (optional) If truthy, then `expected` is matched as a Lua string
+-- pattern
+-- @return the array index at which the value was found
+-- @usage
+-- local arr = { "one", "three" }
+-- local i = assert.contains("one", arr) --> passes; i == 1
+-- local i = assert.contains("two", arr) --> fails
+-- local i = assert.contains("ee$", arr, true) --> passes; i == 2
+local function contains(state, args)
+ local expected, arr, pattern = unpack(args)
+ local found
+ for i = 1, #arr do
+ if (pattern and string.match(arr[i], expected)) or arr[i] == expected then
+ found = i
+ break
+ end
+ end
+ return found ~= nil, {found}
+end
+say:set("assertion.contains.negative", [[
+Expected array to contain element.
+Expected to contain:
+%s
+]])
+say:set("assertion.contains.positive", [[
+Expected array to not contain element.
+Expected to not contain:
+%s
+]])
+luassert:register("assertion", "contains", contains,
+ "assertion.contains.negative",
+ "assertion.contains.positive")
+
+local deep_sort do
+ local function deep_compare(a, b)
+ if a == nil then
+ a = ""
+ end
+
+ if b == nil then
+ b = ""
+ end
+
+ deep_sort(a)
+ deep_sort(b)
+
+ if type(a) ~= type(b) then
+ return type(a) < type(b)
+ end
+
+ if type(a) == "table" then
+ return deep_compare(a[1], b[1])
+ end
+
+ return a < b
+ end
+
+ function deep_sort(t)
+ if type(t) == "table" then
+ for _, v in pairs(t) do
+ deep_sort(v)
+ end
+ table.sort(t, deep_compare)
+ end
+
+ return t
+ end
+end
+
+
+local function copy_errlog(errlog_path)
+ local file_path = "Unknown path"
+ local line_number = "Unknown line"
+ local errlog_cache_dir = os.getenv("SPEC_ERRLOG_CACHE_DIR") or "/tmp/kong_errlog_cache"
+
+ local ok, err = pl_dir.makepath(errlog_cache_dir)
+ assert(ok, "makepath failed: " .. tostring(err))
+
+ local info = debug.getinfo(4, "Sl")
+ if info then
+ file_path = info.source:gsub("^@", "")
+ line_number = info.currentline
+ end
+
+ if string.find(file_path, '/', nil, true) then
+ file_path = string.gsub(file_path, '/', '_')
+ end
+ file_path = errlog_cache_dir .. "/" .. file_path:gsub("%.lua$", "_") .. "line_" .. line_number .. '.log'
+
+ ok, err = pl_file.copy(errlog_path, file_path)
+ if ok then
+ print(colors("%{yellow}Log saved as: " .. file_path .. "%{reset}"))
+ else
+ print(colors("%{red}Failed to save error log for test " .. file_path .. ": " .. err))
+ end
+end
+
+--- Assertion to check the status-code of a http response.
+-- @function status
+-- @param expected the expected status code
+-- @param response (optional) results from `http_client:send` function,
+-- alternatively use `response`.
+-- @return the response body as a string, for a json body see `jsonbody`.
+-- @usage
+-- local res = assert(client:send { .. your request params here .. })
+-- local body = assert.has.status(200, res) -- or alternativly
+-- local body = assert.response(res).has.status(200) -- does the same
+local function res_status(state, args)
+ assert(not rawget(state, "kong_request"),
+ "Cannot check statuscode against a request object,"
+ .. " only against a response object")
+
+ local expected = args[1]
+ local res = args[2] or rawget(state, "kong_response")
+
+ assert(type(expected) == "number",
+ "Expected response code must be a number value. Got: " .. tostring(expected))
+ assert(type(res) == "table" and type(res.read_body) == "function",
+ "Expected a http_client response. Got: " .. tostring(res))
+
+ if expected ~= res.status then
+ local body, err = res:read_body()
+ if not body then body = "Error reading body: " .. err end
+ table.insert(args, 1, strip(body))
+ table.insert(args, 1, res.status)
+ table.insert(args, 1, expected)
+ args.n = 3
+
+ if res.status == 500 then
+ copy_errlog(conf.nginx_err_logs)
+
+ -- on HTTP 500, we can try to read the server's error logs
+ -- for debugging purposes (very useful for travis)
+ local str = pl_file.read(conf.nginx_err_logs)
+ if not str then
+ return false -- no err logs to read in this prefix
+ end
+
+ local lines_t = splitlines(str)
+ local str_t = {}
+ -- filter out debugs as they are not usually useful in this context
+ for i = 1, #lines_t do
+ if not lines_t[i]:match(" %[debug%] ") then
+ table.insert(str_t, lines_t[i])
+ end
+ end
+
+ local first_line = #str_t - math.min(60, #str_t) + 1
+ local msg_t = {"\nError logs (" .. conf.nginx_err_logs .. "), only last 60 non-debug logs are displayed:"}
+ for i = first_line, #str_t do
+ msg_t[#msg_t+1] = str_t[i]
+ end
+
+ table.insert(args, 4, table.concat(msg_t, "\n"))
+ args.n = 4
+ end
+
+ return false
+ else
+ local body, err = res:read_body()
+ local output = body
+ if not output then output = "Error reading body: " .. err end
+ output = strip(output)
+ table.insert(args, 1, output)
+ table.insert(args, 1, res.status)
+ table.insert(args, 1, expected)
+ args.n = 3
+ return true, { strip(body) }
+ end
+end
+say:set("assertion.res_status.negative", [[
+Invalid response status code.
+Status expected:
+%s
+Status received:
+%s
+Body:
+%s
+%s]])
+say:set("assertion.res_status.positive", [[
+Invalid response status code.
+Status not expected:
+%s
+Status received:
+%s
+Body:
+%s
+%s]])
+luassert:register("assertion", "status", res_status,
+ "assertion.res_status.negative", "assertion.res_status.positive")
+luassert:register("assertion", "res_status", res_status,
+ "assertion.res_status.negative", "assertion.res_status.positive")
+
+
+--- Checks and returns a json body of an http response/request. Only checks
+-- validity of the json, does not check appropriate headers. Setting the target
+-- to check can be done through the `request` and `response` modifiers.
+--
+-- For a non-json body, see the `status` assertion.
+-- @function jsonbody
+-- @return the decoded json as a table
+-- @usage
+-- local res = assert(client:send { .. your request params here .. })
+-- local json_table = assert.response(res).has.jsonbody()
+local function jsonbody(state, args)
+ assert(args[1] == nil and rawget(state, "kong_request") or rawget(state, "kong_response"),
+ "the `jsonbody` assertion does not take parameters. " ..
+ "Use the `response`/`require` modifiers to set the target to operate on")
+
+ if rawget(state, "kong_response") then
+ local body = rawget(state, "kong_response"):read_body()
+ local json, err = cjson.decode(body)
+ if not json then
+ table.insert(args, 1, "Error decoding: " .. tostring(err) .. "\nResponse body:" .. body)
+ args.n = 1
+ return false
+ end
+ return true, {json}
+
+ else
+ local r = rawget(state, "kong_request")
+ if r.post_data
+ and (r.post_data.kind == "json" or r.post_data.kind == "json (error)")
+ and r.post_data.params
+ then
+ local pd = r.post_data
+ return true, { { params = pd.params, data = pd.text, error = pd.error, kind = pd.kind } }
+
+ else
+ error("No json data found in the request")
+ end
+ end
+end
+say:set("assertion.jsonbody.negative", [[
+Expected response body to contain valid json. Got:
+%s
+]])
+say:set("assertion.jsonbody.positive", [[
+Expected response body to not contain valid json. Got:
+%s
+]])
+luassert:register("assertion", "jsonbody", jsonbody,
+ "assertion.jsonbody.negative",
+ "assertion.jsonbody.positive")
+
+
+--- Asserts that a named header in a `headers` subtable exists.
+-- Header name comparison is done case-insensitive.
+-- @function header
+-- @param name header name to look for (case insensitive).
+-- @see response
+-- @see request
+-- @return value of the header
+-- @usage
+-- local res = client:get("/request", { .. request options here ..})
+-- local resp_header_value = assert.response(res).has.header("Content-Length")
+-- local req_header_value = assert.request(res).has.header("Content-Length")
+local function res_header(state, args)
+ local header = args[1]
+ local res = args[2] or rawget(state, "kong_request") or rawget(state, "kong_response")
+ assert(type(res) == "table" and type(res.headers) == "table",
+ "'header' assertion input does not contain a 'headers' subtable")
+ local value = lookup(res.headers, header)
+ table.insert(args, 1, res.headers)
+ table.insert(args, 1, header)
+ args.n = 2
+ if not value then
+ return false
+ end
+ return true, {value}
+end
+say:set("assertion.res_header.negative", [[
+Expected header:
+%s
+But it was not found in:
+%s
+]])
+say:set("assertion.res_header.positive", [[
+Did not expected header:
+%s
+But it was found in:
+%s
+]])
+luassert:register("assertion", "header", res_header,
+ "assertion.res_header.negative",
+ "assertion.res_header.positive")
+
+
+---
+-- An assertion to look for a query parameter in a query string.
+-- Parameter name comparison is done case-insensitive.
+-- @function queryparam
+-- @param name name of the query parameter to look up (case insensitive)
+-- @return value of the parameter
+-- @usage
+-- local res = client:get("/request", {
+-- query = { hello = "world" },
+-- })
+-- local param_value = assert.request(res).has.queryparam("hello")
+local function req_query_param(state, args)
+ local param = args[1]
+ local req = rawget(state, "kong_request")
+ assert(req, "'queryparam' assertion only works with a request object")
+ local params
+ if type(req.uri_args) == "table" then
+ params = req.uri_args
+
+ else
+ error("No query parameters found in request object")
+ end
+ local value = lookup(params, param)
+ table.insert(args, 1, params)
+ table.insert(args, 1, param)
+ args.n = 2
+ if not value then
+ return false
+ end
+ return true, {value}
+end
+say:set("assertion.req_query_param.negative", [[
+Expected query parameter:
+%s
+But it was not found in:
+%s
+]])
+say:set("assertion.req_query_param.positive", [[
+Did not expected query parameter:
+%s
+But it was found in:
+%s
+]])
+luassert:register("assertion", "queryparam", req_query_param,
+ "assertion.req_query_param.negative",
+ "assertion.req_query_param.positive")
+
+
+---
+-- Adds an assertion to look for a urlencoded form parameter in a request.
+-- Parameter name comparison is done case-insensitive. Use the `request` modifier to set
+-- the request to operate on.
+-- @function formparam
+-- @param name name of the form parameter to look up (case insensitive)
+-- @return value of the parameter
+-- @usage
+-- local r = assert(proxy_client:post("/request", {
+-- body = {
+-- hello = "world",
+-- },
+-- headers = {
+-- host = "mock_upstream",
+-- ["Content-Type"] = "application/x-www-form-urlencoded",
+-- },
+-- })
+-- local value = assert.request(r).has.formparam("hello")
+-- assert.are.equal("world", value)
+local function req_form_param(state, args)
+ local param = args[1]
+ local req = rawget(state, "kong_request")
+ assert(req, "'formparam' assertion can only be used with a mock_upstream request object")
+
+ local value
+ if req.post_data
+ and (req.post_data.kind == "form" or req.post_data.kind == "multipart-form")
+ then
+ value = lookup(req.post_data.params or {}, param)
+ else
+ error("Could not determine the request to be from either mock_upstream")
+ end
+
+ table.insert(args, 1, req)
+ table.insert(args, 1, param)
+ args.n = 2
+ if not value then
+ return false
+ end
+ return true, {value}
+end
+say:set("assertion.req_form_param.negative", [[
+Expected url encoded form parameter:
+%s
+But it was not found in request:
+%s
+]])
+say:set("assertion.req_form_param.positive", [[
+Did not expected url encoded form parameter:
+%s
+But it was found in request:
+%s
+]])
+luassert:register("assertion", "formparam", req_form_param,
+ "assertion.req_form_param.negative",
+ "assertion.req_form_param.positive")
+
+
+---
+-- Assertion to ensure a value is greater than a base value.
+-- @function is_gt
+-- @param base the base value to compare against
+-- @param value the value that must be greater than the base value
+local function is_gt(state, arguments)
+ local expected = arguments[1]
+ local value = arguments[2]
+
+ arguments[1] = value
+ arguments[2] = expected
+
+ return value > expected
+end
+say:set("assertion.gt.negative", [[
+Given value (%s) should be greater than expected value (%s)
+]])
+say:set("assertion.gt.positive", [[
+Given value (%s) should not be greater than expected value (%s)
+]])
+luassert:register("assertion", "gt", is_gt,
+ "assertion.gt.negative",
+ "assertion.gt.positive")
+
+--- Generic modifier "certificate".
+-- Will set a "certificate" value in the assertion state, so following
+-- assertions will operate on the value set.
+-- @function certificate
+-- @param cert The cert text
+-- @see cn
+-- @usage
+-- assert.certificate(cert).has.cn("ssl-example.com")
+local function modifier_certificate(state, arguments, level)
+ local generic = "The assertion 'certficate' modifier takes a cert text"
+ .. " as input to validate certificate parameters"
+ .. " against."
+ local cert = arguments[1]
+ assert(type(cert) == "string",
+ "Expected a certificate text, got '" .. tostring(cert) .. "'. " .. generic)
+ rawset(state, "kong_certificate", cert)
+ return state
+end
+luassert:register("modifier", "certificate", modifier_certificate)
+
+--- Assertion to check whether a CN is matched in an SSL cert.
+-- @function cn
+-- @param expected The CN value
+-- @param cert The cert text
+-- @return the CN found in the cert
+-- @see certificate
+-- @usage
+-- assert.cn("ssl-example.com", cert)
+--
+-- -- alternative:
+-- assert.certificate(cert).has.cn("ssl-example.com")
+local function assert_cn(state, args)
+ local expected = args[1]
+ if args[2] and rawget(state, "kong_certificate") then
+ error("assertion 'cn' takes either a 'certificate' modifier, or 2 parameters, not both")
+ end
+ local cert = args[2] or rawget(state, "kong_certificate")
+ local cn = string.match(cert, "CN%s*=%s*([^%s,]+)")
+ args[2] = cn or "(CN not found in certificate)"
+ args.n = 2
+ return cn == expected
+end
+say:set("assertion.cn.negative", [[
+Expected certificate to have the given CN value.
+Expected CN:
+%s
+Got instead:
+%s
+]])
+say:set("assertion.cn.positive", [[
+Expected certificate to not have the given CN value.
+Expected CN to not be:
+%s
+Got instead:
+%s
+]])
+luassert:register("assertion", "cn", assert_cn,
+ "assertion.cn.negative",
+ "assertion.cn.positive")
+
+do
+ --- Generic modifier "logfile"
+ -- Will set an "errlog_path" value in the assertion state.
+ -- @function logfile
+ -- @param path A path to the log file (defaults to the test prefix's
+ -- errlog).
+ -- @see line
+ -- @see clean_logfile
+ -- @usage
+ -- assert.logfile("./my/logfile.log").has.no.line("[error]", true)
+ local function modifier_errlog(state, args)
+ local errlog_path = args[1] or conf.nginx_err_logs
+
+ assert(type(errlog_path) == "string", "logfile modifier expects nil, or " ..
+ "a string as argument, got: " ..
+ type(errlog_path))
+
+ rawset(state, "errlog_path", errlog_path)
+
+ return state
+ end
+
+ luassert:register("modifier", "errlog", modifier_errlog) -- backward compat
+ luassert:register("modifier", "logfile", modifier_errlog)
+
+
+ local function substr(subject, pattern)
+ if subject:find(pattern, nil, true) ~= nil then
+ return subject
+ end
+ end
+
+ local function re_match(subject, pattern)
+ local pos, _, err = ngx.re.find(subject, pattern, "oj")
+ if err then
+ error(("invalid regex provided to logfile assertion %q: %s")
+ :format(pattern, err), 5)
+ end
+
+ if pos then
+ return subject
+ end
+ end
+
+
+ -- EE [[
+ -- FIXME: major hack here
+ --
+ -- CI and dev environments use an auto-generated license with a very
+ -- short life span, which triggers log entries like:
+ --
+ -- ```
+ -- 2022/11/10 15:50:17 [warn] 1440109#0: *54 stream [lua] license_helpers.lua:231: log_license_state(): The Kong Enterprise license will expire on 2022-12-20. Please contact to renew your license., context: ngx.timer
+ -- ```
+ --
+ -- These log entries are a time bomb for our integration tests, because
+ -- we have many test cases that do something like this:
+ --
+ -- ```
+ -- -- ensure there are no warnings in the error.log after doing $thing
+ -- assert.logfile().has.no.line("[warn]")
+ -- ```
+ --
+ -- This code attempts to filter out license warnings.
+ local license_warning = "Please contact to renew your license."
+ local license_warning_dev = "Using development (e.g. not a release) license validation"
+ local portal_vitals_deprecated = "portal and vitals are deprecated"
+ local portal_and_vitals_key_invalid = "portal_and_vitals_key is invalid. please contact your support representative."
+
+ local function is_ee_license_warning(line)
+ return line
+ and (substr(line, license_warning)
+ or substr(line, license_warning_dev)
+ or substr(line, portal_vitals_deprecated)
+ or substr(line, portal_and_vitals_key_invalid))
+ end
+ -- ]] EE
+
+
+ local function find_in_file(fpath, pattern, matcher)
+ local fh = assert(io.open(fpath, "r"))
+ local found
+
+ for line in fh:lines() do
+ -- EE [[
+ -- see comment above re: filtering out license warnings
+ if matcher(line, pattern) and not is_ee_license_warning(line) then
+ -- ]] EE
+ found = line
+ break
+ end
+ end
+
+ fh:close()
+
+ return found
+ end
+
+
+ --- Assertion checking if any line from a file matches the given regex or
+ -- substring.
+ -- @function line
+ -- @param regex The regex to evaluate against each line.
+ -- @param plain If true, the regex argument will be considered as a plain
+ -- string.
+ -- @param timeout An optional timeout after which the assertion will fail if
+ -- reached.
+ -- @param fpath An optional path to the file (defaults to the filelog
+ -- modifier)
+ -- @see logfile
+ -- @see clean_logfile
+ -- @usage
+ -- helpers.clean_logfile()
+ --
+ -- -- run some tests here
+ --
+ -- assert.logfile().has.no.line("[error]", true)
+ local function match_line(state, args)
+ local regex = args[1]
+ local plain = args[2]
+ local timeout = args[3] or 2
+ local fpath = args[4] or rawget(state, "errlog_path")
+
+ assert(type(regex) == "string",
+ "Expected the regex argument to be a string")
+ assert(type(fpath) == "string",
+ "Expected the file path argument to be a string")
+ assert(type(timeout) == "number" and timeout >= 0,
+ "Expected the timeout argument to be a number >= 0")
+
+
+ local matcher = plain and substr or re_match
+
+ local found = find_in_file(fpath, regex, matcher)
+ local deadline = ngx.now() + timeout
+
+ while not found and ngx.now() <= deadline do
+ ngx.sleep(0.05)
+ found = find_in_file(fpath, regex, matcher)
+ end
+
+ args[1] = fpath
+ args[2] = regex
+ args.n = 2
+
+ if found then
+ args[3] = found
+ args.n = 3
+ end
+
+ return found
+ end
+
+ say:set("assertion.match_line.negative", unindent [[
+ Expected file at:
+ %s
+ To match:
+ %s
+ ]])
+ say:set("assertion.match_line.positive", unindent [[
+ Expected file at:
+ %s
+ To not match:
+ %s
+ But matched line:
+ %s
+ ]])
+ luassert:register("assertion", "line", match_line,
+ "assertion.match_line.negative",
+ "assertion.match_line.positive")
+end
+
+
+--- Assertion to check whether a string matches a regular expression
+-- @function match_re
+-- @param string the string
+-- @param regex the regular expression
+-- @return true or false
+-- @usage
+-- assert.match_re("foobar", [[bar$]])
+--
+
+local function match_re(_, args)
+ local string = args[1]
+ local regex = args[2]
+ assert(type(string) == "string",
+ "Expected the string argument to be a string")
+ assert(type(regex) == "string",
+ "Expected the regex argument to be a string")
+ local from, _, err = ngx.re.find(string, regex)
+ if err then
+ error(err)
+ end
+ if from then
+ table.insert(args, 1, string)
+ table.insert(args, 1, regex)
+ args.n = 2
+ return true
+ else
+ return false
+ end
+end
+
+say:set("assertion.match_re.negative", unindent [[
+ Expected log:
+ %s
+ To match:
+ %s
+ ]])
+say:set("assertion.match_re.positive", unindent [[
+ Expected log:
+ %s
+ To not match:
+ %s
+ But matched line:
+ %s
+ ]])
+luassert:register("assertion", "match_re", match_re,
+ "assertion.match_re.negative",
+ "assertion.match_re.positive")
+
+
+----------------
+-- DNS-record mocking.
+-- These function allow to create mock dns records that the test Kong instance
+-- will use to resolve names. The created mocks are injected by the `start_kong`
+-- function.
+-- @usage
+-- -- Create a new DNS mock and add some DNS records
+-- local fixtures = {
+-- dns_mock = helpers.dns_mock.new { mocks_only = true }
+-- }
+--
+-- fixtures.dns_mock:SRV {
+-- name = "my.srv.test.com",
+-- target = "a.my.srv.test.com",
+-- port = 80,
+-- }
+-- fixtures.dns_mock:SRV {
+-- name = "my.srv.test.com", -- adding same name again: record gets 2 entries!
+-- target = "b.my.srv.test.com", -- a.my.srv.test.com and b.my.srv.test.com
+-- port = 8080,
+-- }
+-- fixtures.dns_mock:A {
+-- name = "a.my.srv.test.com",
+-- address = "127.0.0.1",
+-- }
+-- fixtures.dns_mock:A {
+-- name = "b.my.srv.test.com",
+-- address = "127.0.0.1",
+-- }
+-- @section DNS-mocks
+
+
+local dns_mock = {}
+do
+ dns_mock.__index = dns_mock
+ dns_mock.__tostring = function(self)
+ -- fill array to prevent json encoding errors
+ local out = {
+ mocks_only = self.mocks_only,
+ records = {}
+ }
+ for i = 1, 33 do
+ out.records[i] = self[i] or {}
+ end
+ local json = assert(cjson.encode(out))
+ return json
+ end
+
+
+ local TYPE_A, TYPE_AAAA, TYPE_CNAME, TYPE_SRV = 1, 28, 5, 33
+
+
+ --- Creates a new DNS mock.
+ -- The options table supports the following fields:
+ --
+ -- - `mocks_only`: boolean, if set to `true` then only mock records will be
+ -- returned. If `falsy` it will fall through to an actual DNS lookup.
+ -- @function dns_mock.new
+ -- @param options table with mock options
+ -- @return dns_mock object
+ -- @usage
+ -- local mock = helpers.dns_mock.new { mocks_only = true }
+ function dns_mock.new(options)
+ return setmetatable(options or {}, dns_mock)
+ end
+
+
+ --- Adds an SRV record to the DNS mock.
+ -- Fields `name`, `target`, and `port` are required. Other fields get defaults:
+ --
+ -- * `weight`; 20
+ -- * `ttl`; 600
+ -- * `priority`; 20
+ -- @param rec the mock DNS record to insert
+ -- @return true
+ function dns_mock:SRV(rec)
+ if self == dns_mock then
+ error("can't operate on the class, you must create an instance", 2)
+ end
+ if getmetatable(self or {}) ~= dns_mock then
+ error("SRV method must be called using the colon notation", 2)
+ end
+ assert(rec, "Missing record parameter")
+ local name = assert(rec.name, "No name field in SRV record")
+
+ self[TYPE_SRV] = self[TYPE_SRV] or {}
+ local query_answer = self[TYPE_SRV][name]
+ if not query_answer then
+ query_answer = {}
+ self[TYPE_SRV][name] = query_answer
+ end
+
+ table.insert(query_answer, {
+ type = TYPE_SRV,
+ name = name,
+ target = assert(rec.target, "No target field in SRV record"),
+ port = assert(rec.port, "No port field in SRV record"),
+ weight = rec.weight or 10,
+ ttl = rec.ttl or 600,
+ priority = rec.priority or 20,
+ class = rec.class or 1
+ })
+ return true
+ end
+
+
+ --- Adds an A record to the DNS mock.
+ -- Fields `name` and `address` are required. Other fields get defaults:
+ --
+ -- * `ttl`; 600
+ -- @param rec the mock DNS record to insert
+ -- @return true
+ function dns_mock:A(rec)
+ if self == dns_mock then
+ error("can't operate on the class, you must create an instance", 2)
+ end
+ if getmetatable(self or {}) ~= dns_mock then
+ error("A method must be called using the colon notation", 2)
+ end
+ assert(rec, "Missing record parameter")
+ local name = assert(rec.name, "No name field in A record")
+
+ self[TYPE_A] = self[TYPE_A] or {}
+ local query_answer = self[TYPE_A][name]
+ if not query_answer then
+ query_answer = {}
+ self[TYPE_A][name] = query_answer
+ end
+
+ table.insert(query_answer, {
+ type = TYPE_A,
+ name = name,
+ address = assert(rec.address, "No address field in A record"),
+ ttl = rec.ttl or 600,
+ class = rec.class or 1
+ })
+ return true
+ end
+
+
+ --- Adds an AAAA record to the DNS mock.
+ -- Fields `name` and `address` are required. Other fields get defaults:
+ --
+ -- * `ttl`; 600
+ -- @param rec the mock DNS record to insert
+ -- @return true
+ function dns_mock:AAAA(rec)
+ if self == dns_mock then
+ error("can't operate on the class, you must create an instance", 2)
+ end
+ if getmetatable(self or {}) ~= dns_mock then
+ error("AAAA method must be called using the colon notation", 2)
+ end
+ assert(rec, "Missing record parameter")
+ local name = assert(rec.name, "No name field in AAAA record")
+
+ self[TYPE_AAAA] = self[TYPE_AAAA] or {}
+ local query_answer = self[TYPE_AAAA][name]
+ if not query_answer then
+ query_answer = {}
+ self[TYPE_AAAA][name] = query_answer
+ end
+
+ table.insert(query_answer, {
+ type = TYPE_AAAA,
+ name = name,
+ address = assert(rec.address, "No address field in AAAA record"),
+ ttl = rec.ttl or 600,
+ class = rec.class or 1
+ })
+ return true
+ end
+
+
+ --- Adds a CNAME record to the DNS mock.
+ -- Fields `name` and `cname` are required. Other fields get defaults:
+ --
+ -- * `ttl`; 600
+ -- @param rec the mock DNS record to insert
+ -- @return true
+ function dns_mock:CNAME(rec)
+ if self == dns_mock then
+ error("can't operate on the class, you must create an instance", 2)
+ end
+ if getmetatable(self or {}) ~= dns_mock then
+ error("CNAME method must be called using the colon notation", 2)
+ end
+ assert(rec, "Missing record parameter")
+ local name = assert(rec.name, "No name field in CNAME record")
+
+ self[TYPE_CNAME] = self[TYPE_CNAME] or {}
+ local query_answer = self[TYPE_CNAME][name]
+ if not query_answer then
+ query_answer = {}
+ self[TYPE_CNAME][name] = query_answer
+ end
+
+ table.insert(query_answer, {
+ type = TYPE_CNAME,
+ name = name,
+ cname = assert(rec.cname, "No cname field in CNAME record"),
+ ttl = rec.ttl or 600,
+ class = rec.class or 1
+ })
+ return true
+ end
+end
+
+---
+-- Assertion to partially compare two lua tables.
+-- @function partial_match
+-- @param partial_table the table with subset of fields expect to match
+-- @param full_table the full table that should contain partial_table and potentially other fields
+local function partial_match(state, arguments)
+
+ local function deep_matches(t1, t2, parent_keys)
+ for key, v in pairs(t1) do
+ local compound_key = (parent_keys and parent_keys .. "." .. key) or key
+ if type(v) == "table" then
+ local ok, compound_key, v1, v2 = deep_matches(t1[key], t2[key], compound_key)
+ if not ok then
+ return ok, compound_key, v1, v2
+ end
+ else
+ if (state.mod == true and t1[key] ~= t2[key]) or (state.mod == false and t1[key] == t2[key]) then
+ return false, compound_key, t1[key], t2[key]
+ end
+ end
+ end
+
+ return true
+ end
+
+ local partial_table = arguments[1]
+ local full_table = arguments[2]
+
+ local ok, compound_key, v1, v2 = deep_matches(partial_table, full_table)
+
+ if not ok then
+ arguments[1] = compound_key
+ arguments[2] = v1
+ arguments[3] = v2
+ arguments.n = 3
+
+ return not state.mod
+ end
+
+ return state.mod
+end
+
+say:set("assertion.partial_match.negative", [[
+Values at key %s should not be equal
+]])
+say:set("assertion.partial_match.positive", [[
+Values at key %s should be equal but are not.
+Expected: %s, given: %s
+]])
+luassert:register("assertion", "partial_match", partial_match,
+ "assertion.partial_match.positive",
+ "assertion.partial_match.negative")
+
+
+----------------
+-- Shell helpers
+-- @section Shell-helpers
+
+--- Execute a command.
+-- Modified version of `pl.utils.executeex()` so the output can directly be
+-- used on an assertion.
+-- @function execute
+-- @param cmd command string to execute
+-- @param returns (optional) boolean: if true, this function will
+-- return the same values as Penlight's executeex.
+-- @return if `returns` is true, returns four return values
+-- (ok, code, stdout, stderr); if `returns` is false,
+-- returns either (false, stderr) or (true, stderr, stdout).
+function exec(cmd, returns)
+ --100MB for retrieving stdout & stderr
+ local ok, stdout, stderr, _, code = shell.run(cmd, nil, 0, 1024*1024*100)
+ if returns then
+ return ok, code, stdout, stderr
+ end
+ if not ok then
+ stdout = nil -- don't return 3rd value if fail because of busted's `assert`
+ end
+ return ok, stderr, stdout
+end
+
+
+--- Execute a Kong command.
+-- @function kong_exec
+-- @param cmd Kong command to execute, eg. `start`, `stop`, etc.
+-- @param env (optional) table with kong parameters to set as environment
+-- variables, overriding the test config (each key will automatically be
+-- prefixed with `KONG_` and be converted to uppercase)
+-- @param returns (optional) boolean: if true, this function will
+-- return the same values as Penlight's `executeex`.
+-- @param env_vars (optional) a string prepended to the command, so
+-- that arbitrary environment variables may be passed
+-- @return if `returns` is true, returns four return values
+-- (ok, code, stdout, stderr); if `returns` is false,
+-- returns either (false, stderr) or (true, stderr, stdout).
+function kong_exec(cmd, env, returns, env_vars)
+ cmd = cmd or ""
+ env = env or {}
+
+ -- Insert the Lua path to the custom-plugin fixtures
+ do
+ local function cleanup(t)
+ if t then
+ t = strip(t)
+ if t:sub(-1,-1) == ";" then
+ t = t:sub(1, -2)
+ end
+ end
+ return t ~= "" and t or nil
+ end
+ local paths = {}
+ table.insert(paths, cleanup(CUSTOM_PLUGIN_PATH))
+ table.insert(paths, cleanup(CUSTOM_VAULT_PATH))
+ table.insert(paths, cleanup(env.lua_package_path))
+ table.insert(paths, cleanup(conf.lua_package_path))
+ env.lua_package_path = table.concat(paths, ";")
+ -- note; the nginx config template will add a final ";;", so no need to
+ -- include that here
+ end
+
+ if not env.plugins then
+ env.plugins = "bundled,dummy,cache,rewriter,error-handler-log," ..
+ "error-generator,error-generator-last," ..
+ "short-circuit"
+ end
+
+ -- build Kong environment variables
+ env_vars = env_vars or ""
+ for k, v in pairs(env) do
+ env_vars = string.format("%s KONG_%s='%s'", env_vars, k:upper(), v)
+ end
+
+ return exec(env_vars .. " " .. BIN_PATH .. " " .. cmd, returns)
+end
+
+
+--- Prepares the Kong environment.
+-- Creates the working directory if it does not exist.
+-- @param prefix (optional) path to the working directory, if omitted the test
+-- configuration will be used
+-- @function prepare_prefix
+local function prepare_prefix(prefix)
+ return pl_dir.makepath(prefix or conf.prefix)
+end
+
+
+--- Cleans the Kong environment.
+-- Deletes the working directory if it exists.
+-- @param prefix (optional) path to the working directory, if omitted the test
+-- configuration will be used
+-- @function clean_prefix
+local function clean_prefix(prefix)
+
+ -- like pl_dir.rmtree, but ignore mount points
+ local function rmtree(fullpath)
+ if pl_path.islink(fullpath) then return false,'will not follow symlink' end
+ for root,dirs,files in pl_dir.walk(fullpath,true) do
+ if pl_path.islink(root) then
+ -- sub dir is a link, remove link, do not follow
+ local res, err = os.remove(root)
+ if not res then
+ return nil, err .. ": " .. root
+ end
+
+ else
+ for i,f in ipairs(files) do
+ f = pl_path.join(root,f)
+ local res, err = os.remove(f)
+ if not res then
+ return nil,err .. ": " .. f
+ end
+ end
+
+ local res, err = pl_path.rmdir(root)
+ -- skip errors when trying to remove mount points
+ if not res and shell.run("findmnt " .. root .. " 2>&1 >/dev/null", nil, 0) == 0 then
+ return nil, err .. ": " .. root
+ end
+ end
+ end
+ return true
+ end
+
+ prefix = prefix or conf.prefix
+ if pl_path.exists(prefix) then
+ local _, err = rmtree(prefix)
+ if err then
+ error(err)
+ end
+ end
+end
+
+
+-- Reads the pid from a pid file and returns it, or nil + err
+local function get_pid_from_file(pid_path)
+ local pid
+ local fd, err = io.open(pid_path)
+ if not fd then
+ return nil, err
+ end
+
+ pid = fd:read("*l")
+ fd:close()
+
+ return pid
+end
+
+
+local function pid_dead(pid, timeout)
+ local max_time = ngx.now() + (timeout or 10)
+
+ repeat
+ if not shell.run("ps -p " .. pid .. " >/dev/null 2>&1", nil, 0) then
+ return true
+ end
+ -- still running, wait some more
+ ngx.sleep(0.05)
+ until ngx.now() >= max_time
+
+ return false
+end
+
+-- Waits for the termination of a pid.
+-- @param pid_path Filename of the pid file.
+-- @param timeout (optional) in seconds, defaults to 10.
+local function wait_pid(pid_path, timeout, is_retry)
+ local pid = get_pid_from_file(pid_path)
+
+ if TEST_COVERAGE_MODE == "true" then
+ timeout = TEST_COVERAGE_TIMEOUT
+ end
+
+ if pid then
+ if pid_dead(pid, timeout) then
+ return
+ end
+
+ if is_retry then
+ return
+ end
+
+ -- Timeout reached: kill with SIGKILL
+ shell.run("kill -9 " .. pid .. " >/dev/null 2>&1", nil, 0)
+
+ -- Sanity check: check pid again, but don't loop.
+ wait_pid(pid_path, timeout, true)
+ end
+end
+
+
+--- Return the actual configuration running at the given prefix.
+-- It may differ from the default, as it may have been modified
+-- by the `env` table given to start_kong.
+-- @function get_running_conf
+-- @param prefix (optional) The prefix path where the kong instance is running,
+-- defaults to the prefix in the default config.
+-- @return The conf table of the running instance, or nil + error.
+local function get_running_conf(prefix)
+ local default_conf = conf_loader(nil, {prefix = prefix or conf.prefix})
+ return conf_loader.load_config_file(default_conf.kong_env)
+end
+
+
+--- Clears the logfile. Will overwrite the logfile with an empty file.
+-- @function clean_logfile
+-- @param logfile (optional) filename to clear, defaults to the current
+-- error-log file
+-- @return nothing
+-- @see line
+local function clean_logfile(logfile)
+ logfile = logfile or (get_running_conf() or conf).nginx_err_logs
+
+ assert(type(logfile) == "string", "'logfile' must be a string")
+
+ local fh, err, errno = io.open(logfile, "w+")
+
+ if fh then
+ fh:close()
+ return
+
+ elseif errno == 2 then -- ENOENT
+ return
+ end
+
+ error("failed to truncate logfile: " .. tostring(err))
+end
+
+
+--- Return the actual Kong version the tests are running against.
+-- See [version.lua](https://github.com/kong/version.lua) for the format. This
+-- is mostly useful for testing plugins that should work with multiple Kong versions.
+-- @function get_version
+-- @return a `version` object
+-- @usage
+-- local version = require 'version'
+-- if helpers.get_version() < version("0.15.0") then
+-- -- do something
+-- end
+local function get_version()
+ return version(select(3, assert(kong_exec("version"))))
+end
+
+
+local function render_fixtures(conf, env, prefix, fixtures)
+
+ if fixtures and (fixtures.http_mock or fixtures.stream_mock) then
+ -- prepare the prefix so we get the full config in the
+ -- hidden `.kong_env` file, including test specified env vars etc
+ assert(kong_exec("prepare --conf " .. conf, env))
+ local render_config = assert(conf_loader(prefix .. "/.kong_env", nil,
+ { from_kong_env = true }))
+
+ for _, mocktype in ipairs { "http_mock", "stream_mock" } do
+
+ for filename, contents in pairs(fixtures[mocktype] or {}) do
+ -- render the file using the full configuration
+ contents = assert(prefix_handler.compile_conf(render_config, contents))
+
+ -- write file to prefix
+ filename = prefix .. "/" .. filename .. "." .. mocktype
+ assert(pl_utils.writefile(filename, contents))
+ end
+ end
+ end
+
+ if fixtures and fixtures.dns_mock then
+ -- write the mock records to the prefix
+ assert(getmetatable(fixtures.dns_mock) == dns_mock,
+ "expected dns_mock to be of a helpers.dns_mock class")
+ assert(pl_utils.writefile(prefix .. "/dns_mock_records.json",
+ tostring(fixtures.dns_mock)))
+
+ -- add the mock resolver to the path to ensure the records are loaded
+ if env.lua_package_path then
+ env.lua_package_path = DNS_MOCK_LUA_PATH .. ";" .. env.lua_package_path
+ else
+ env.lua_package_path = DNS_MOCK_LUA_PATH
+ end
+ else
+ -- remove any old mocks if they exist
+ os.remove(prefix .. "/dns_mock_records.json")
+ end
+
+ return true
+end
+
+
+local function build_go_plugins(path)
+ if pl_path.exists(pl_path.join(path, "go.mod")) then
+ local ok, _, stderr = shell.run(string.format(
+ "cd %s; go mod tidy; go mod download", path), nil, 0)
+ assert(ok, stderr)
+ end
+ for _, go_source in ipairs(pl_dir.getfiles(path, "*.go")) do
+ local ok, _, stderr = shell.run(string.format(
+ "cd %s; go build %s",
+ path, pl_path.basename(go_source)
+ ), nil, 0)
+ assert(ok, stderr)
+ end
+end
+
+local function isnewer(path_a, path_b)
+ if not pl_path.exists(path_a) then
+ return true
+ end
+ if not pl_path.exists(path_b) then
+ return false
+ end
+ return assert(pl_path.getmtime(path_b)) > assert(pl_path.getmtime(path_a))
+end
+
+local function make(workdir, specs)
+ workdir = pl_path.normpath(workdir or pl_path.currentdir())
+
+ for _, spec in ipairs(specs) do
+ local targetpath = pl_path.join(workdir, spec.target)
+ for _, src in ipairs(spec.src) do
+ local srcpath = pl_path.join(workdir, src)
+ if isnewer(targetpath, srcpath) then
+ local ok, _, stderr = shell.run(string.format("cd %s; %s", workdir, spec.cmd), nil, 0)
+ assert(ok, stderr)
+ if isnewer(targetpath, srcpath) then
+ error(string.format("couldn't make %q newer than %q", targetpath, srcpath))
+ end
+ break
+ end
+ end
+ end
+
+ return true
+end
+
+local grpc_target_proc
+local function start_grpc_target()
+ local ngx_pipe = require "ngx.pipe"
+ assert(make(GRPC_TARGET_SRC_PATH, {
+ {
+ target = "targetservice/targetservice.pb.go",
+ src = { "../targetservice.proto" },
+ cmd = "protoc --go_out=. --go-grpc_out=. -I ../ ../targetservice.proto",
+ },
+ {
+ target = "targetservice/targetservice_grpc.pb.go",
+ src = { "../targetservice.proto" },
+ cmd = "protoc --go_out=. --go-grpc_out=. -I ../ ../targetservice.proto",
+ },
+ {
+ target = "target",
+ src = { "grpc-target.go", "targetservice/targetservice.pb.go", "targetservice/targetservice_grpc.pb.go" },
+ cmd = "go mod tidy && go mod download all && go build",
+ },
+ }))
+ grpc_target_proc = assert(ngx_pipe.spawn({ GRPC_TARGET_SRC_PATH .. "/target" }, {
+ merge_stderr = true,
+ }))
+
+ return true
+end
+
+local function stop_grpc_target()
+ if grpc_target_proc then
+ grpc_target_proc:kill(resty_signal.signum("QUIT"))
+ grpc_target_proc = nil
+ end
+end
+
+local function get_grpc_target_port()
+ return 15010
+end
+
+
+--- Start the Kong instance to test against.
+-- The fixtures passed to this function can be 3 types:
+--
+-- * DNS mocks
+--
+-- * Nginx server blocks to be inserted in the http module
+--
+-- * Nginx server blocks to be inserted in the stream module
+-- @function start_kong
+-- @param env table with Kong configuration parameters (and values)
+-- @param tables list of database tables to truncate before starting
+-- @param preserve_prefix (boolean) if truthy, the prefix will not be cleaned
+-- before starting
+-- @param fixtures tables with fixtures, dns, http and stream mocks.
+-- @return return values from `execute`
+-- @usage
+-- -- example mocks
+-- -- Create a new DNS mock and add some DNS records
+-- local fixtures = {
+-- http_mock = {},
+-- stream_mock = {},
+-- dns_mock = helpers.dns_mock.new()
+-- }
+--
+-- **DEPRECATED**: http_mock fixture is deprecated. Please use `spec.helpers.http_mock` instead.
+--
+-- fixtures.dns_mock:A {
+-- name = "a.my.srv.test.com",
+-- address = "127.0.0.1",
+-- }
+--
+-- -- The blocks below will be rendered by the Kong template renderer, like other
+-- -- custom Kong templates. Hence the `${{xxxx}}` values.
+-- -- Multiple mocks can be added each under their own filename ("my_server_block" below)
+-- fixtures.http_mock.my_server_block = [[
+-- server {
+-- server_name my_server;
+-- listen 10001 ssl;
+--
+-- ssl_certificate ${{SSL_CERT}};
+-- ssl_certificate_key ${{SSL_CERT_KEY}};
+-- ssl_protocols TLSv1.2 TLSv1.3;
+--
+-- location ~ "/echobody" {
+-- content_by_lua_block {
+-- ngx.req.read_body()
+-- local echo = ngx.req.get_body_data()
+-- ngx.status = status
+-- ngx.header["Content-Length"] = #echo + 1
+-- ngx.say(echo)
+-- }
+-- }
+-- }
+-- ]]
+--
+-- fixtures.stream_mock.my_server_block = [[
+-- server {
+-- -- insert stream server config here
+-- }
+-- ]]
+--
+-- assert(helpers.start_kong( {database = "postgres"}, nil, nil, fixtures))
+local function start_kong(env, tables, preserve_prefix, fixtures)
+ if tables ~= nil and type(tables) ~= "table" then
+ error("arg #2 must be a list of tables to truncate")
+ end
+ env = env or {}
+ local prefix = env.prefix or conf.prefix
+
+ -- go plugins are enabled
+ -- compile fixture go plugins if any setting mentions it
+ for _,v in pairs(env) do
+ if type(v) == "string" and v:find(GO_PLUGIN_PATH) then
+ build_go_plugins(GO_PLUGIN_PATH)
+ break
+ end
+ end
+
+ -- note: set env var "KONG_TEST_DONT_CLEAN" !! the "_TEST" will be dropped
+ if not (preserve_prefix or os.getenv("KONG_DONT_CLEAN")) then
+ clean_prefix(prefix)
+ end
+
+ local ok, err = prepare_prefix(prefix)
+ if not ok then return nil, err end
+
+ truncate_tables(db, tables)
+
+ local nginx_conf = ""
+ local nginx_conf_flags = { "test" }
+ if env.nginx_conf then
+ nginx_conf = " --nginx-conf " .. env.nginx_conf
+ end
+
+ if TEST_COVERAGE_MODE == "true" then
+ -- render `coverage` blocks in the templates
+ nginx_conf_flags[#nginx_conf_flags + 1] = 'coverage'
+ end
+
+ if next(nginx_conf_flags) then
+ nginx_conf_flags = " --nginx-conf-flags " .. table.concat(nginx_conf_flags, ",")
+ else
+ nginx_conf_flags = ""
+ end
+
+ if dcbp and not env.declarative_config and not env.declarative_config_string then
+ if not config_yml then
+ config_yml = prefix .. "/config.yml"
+ local cfg = dcbp.done()
+ local declarative = require "kong.db.declarative"
+ local ok, err = declarative.to_yaml_file(cfg, config_yml)
+ if not ok then
+ return nil, err
+ end
+ end
+ env = kong_table.cycle_aware_deep_copy(env)
+ env.declarative_config = config_yml
+ end
+
+ assert(render_fixtures(TEST_CONF_PATH .. nginx_conf, env, prefix, fixtures))
+ return kong_exec("start --conf " .. TEST_CONF_PATH .. nginx_conf .. nginx_conf_flags, env)
+end
+
+
+-- Cleanup after kong test instance, should be called if start_kong was invoked with the nowait flag
+-- @function cleanup_kong
+-- @param prefix (optional) the prefix where the test instance runs, defaults to the test configuration.
+-- @param preserve_prefix (boolean) if truthy, the prefix will not be deleted after stopping
+-- @param preserve_dc ???
+local function cleanup_kong(prefix, preserve_prefix, preserve_dc)
+ -- remove socket files to ensure `pl.dir.rmtree()` ok
+ local socks = { "/worker_events.sock", "/stream_worker_events.sock", }
+ for _, name in ipairs(socks) do
+ local sock_file = (prefix or conf.prefix) .. name
+ os.remove(sock_file)
+ end
+
+ -- note: set env var "KONG_TEST_DONT_CLEAN" !! the "_TEST" will be dropped
+ if not (preserve_prefix or os.getenv("KONG_DONT_CLEAN")) then
+ clean_prefix(prefix)
+ end
+
+ if not preserve_dc then
+ config_yml = nil
+ end
+ ngx.ctx.workspace = nil
+end
+
+
+-- Stop the Kong test instance.
+-- @function stop_kong
+-- @param prefix (optional) the prefix where the test instance runs, defaults to the test configuration.
+-- @param preserve_prefix (boolean) if truthy, the prefix will not be deleted after stopping
+-- @param preserve_dc ???
+-- @param signal (optional string) signal name to send to kong, defaults to TERM
+-- @param nowait (optional) if truthy, don't wait for kong to terminate. caller needs to wait and call cleanup_kong
+-- @return true or nil+err
+local function stop_kong(prefix, preserve_prefix, preserve_dc, signal, nowait)
+ prefix = prefix or conf.prefix
+ signal = signal or "TERM"
+
+ local running_conf, err = get_running_conf(prefix)
+ if not running_conf then
+ return nil, err
+ end
+
+ local pid, err = get_pid_from_file(running_conf.nginx_pid)
+ if not pid then
+ return nil, err
+ end
+
+ local ok, _, err = shell.run(string.format("kill -%s %d", signal, pid), nil, 0)
+ if not ok then
+ return nil, err
+ end
+
+ if nowait then
+ return running_conf.nginx_pid
+ end
+
+ wait_pid(running_conf.nginx_pid)
+
+ cleanup_kong(prefix, preserve_prefix, preserve_dc)
+
+ return true
+end
+
+--- Restart Kong. Reusing declarative config when using `database=off`.
+-- @function restart_kong
+-- @param env see `start_kong`
+-- @param tables see `start_kong`
+-- @param fixtures see `start_kong`
+-- @return true or nil+err
+local function restart_kong(env, tables, fixtures)
+ stop_kong(env.prefix, true, true)
+ return start_kong(env, tables, true, fixtures)
+end
+
+--- Wait until no common workers.
+-- This will wait until all the worker PID's listed have gone (others may have appeared). If an `expected_total` is specified, it will also wait until the new workers have reached this number.
+-- @function wait_until_no_common_workers
+-- @tparam table workers an array of worker PID's (the return value of `get_kong_workers`)
+-- @tparam[opt] number expected_total the expected total workers count
+-- @tparam[opt] table wait_opts options to use, the available fields are:
+-- @tparam[opt] number wait_opts.timeout timeout passed to `wait_until`
+-- @tparam[opt] number wait_opts.step step passed to `wait_until`
+local function wait_until_no_common_workers(workers, expected_total, wait_opts)
+ wait_opts = wait_opts or {}
+ wait_until(function()
+ local pok, admin_client = pcall(admin_client)
+ if not pok then
+ return false
+ end
+ local res = assert(admin_client:send {
+ method = "GET",
+ path = "/",
+ })
+ luassert.res_status(200, res)
+ local json = cjson.decode(luassert.res_status(200, res))
+ admin_client:close()
+
+ local new_workers = json.pids.workers
+ local total = 0
+ local common = 0
+ if new_workers then
+ for _, v in ipairs(new_workers) do
+ total = total + 1
+ for _, v_old in ipairs(workers) do
+ if v == v_old then
+ common = common + 1
+ break
+ end
+ end
+ end
+ end
+ return common == 0 and total == (expected_total or total)
+ end, wait_opts.timeout, wait_opts.step)
+end
+
+
+--- Gets the Kong workers PID's.
+-- Will wait for a successful call to the admin-api for a maximum of 10 seconds,
+-- before returning a timeout.
+-- @function get_kong_workers
+-- @tparam[opt] number expected_total the expected total workers count
+-- @return array of worker PID's
+local function get_kong_workers(expected_total)
+ local workers
+
+ wait_until(function()
+ local pok, admin_client = pcall(admin_client)
+ if not pok then
+ return false
+ end
+ local res = admin_client:send {
+ method = "GET",
+ path = "/",
+ }
+ if not res or res.status ~= 200 then
+ return false
+ end
+ local body = luassert.res_status(200, res)
+ local json = cjson.decode(body)
+
+ admin_client:close()
+
+ workers = {}
+
+ for _, item in ipairs(json.pids.workers) do
+ if item ~= ngx.null then
+ table.insert(workers, item)
+ end
+ end
+
+ if expected_total and #workers ~= expected_total then
+ return nil, ("expected %s worker pids, got %s"):format(expected_total,
+ #workers)
+
+ elseif #workers == 0 then
+ return nil, "GET / returned no worker pids"
+ end
+
+ return true
+ end, 10)
+ return workers
+end
+
+
+--- Reload Kong and wait all workers are restarted.
+local function reload_kong(...)
+ local workers = get_kong_workers()
+ local ok, err = kong_exec(...)
+ if ok then
+ local opts = { ... }
+ wait_until_no_common_workers(workers, 1, opts[2])
+ end
+ return ok, err
+end
+
+local is_echo_server_ready, get_echo_server_received_data, echo_server_reset
+do
+ -- Message id is maintained within echo server context and not
+ -- needed for echo server user.
+ -- This id is extracted from the number in nginx error.log at each
+ -- line of log. i.e.:
+ -- 2023/12/15 14:10:12 [info] 718291#0: *303 stream [lua] content_by_lua ...
+ -- in above case, the id is 303.
+ local msg_id = -1
+ local prefix_dir = "servroot"
+
+ --- Check if echo server is ready.
+ --
+ -- @function is_echo_server_ready
+ -- @return boolean
+ function is_echo_server_ready()
+ -- ensure server is ready.
+ local sock = ngx.socket.tcp()
+ sock:settimeout(0.1)
+ local retry = 0
+ local test_port = 8188
+
+ while true do
+ if sock:connect("localhost", test_port) then
+ sock:send("START\n")
+ local ok = sock:receive()
+ sock:close()
+ if ok == "START" then
+ return true
+ end
+ else
+ retry = retry + 1
+ if retry > 10 then
+ return false
+ end
+ end
+ end
+ end
+
+ --- Get the echo server's received data.
+ -- This function check the part of expected data with a timeout.
+ --
+ -- @function get_echo_server_received_data
+ -- @param expected part of the data expected.
+ -- @param timeout (optional) timeout in seconds, default is 0.5.
+ -- @return the data the echo server received. If timeouts, return "timeout".
+ function get_echo_server_received_data(expected, timeout)
+ if timeout == nil then
+ timeout = 0.5
+ end
+
+ local extract_cmd = "grep content_by_lua "..prefix_dir.."/logs/error.log | tail -1"
+ local _, _, log = assert(exec(extract_cmd))
+ local pattern = "%*(%d+)%s.*received data: (.*)"
+ local cur_msg_id, data = string.match(log, pattern)
+
+ -- unit is second.
+ local t = 0.1
+ local time_acc = 0
+
+ -- retry it when data is not available. because sometime,
+ -- the error.log has not been flushed yet.
+ while string.find(data, expected) == nil or cur_msg_id == msg_id do
+ ngx.sleep(t)
+ time_acc = time_acc + t
+ if time_acc >= timeout then
+ return "timeout"
+ end
+
+ _, _, log = assert(exec(extract_cmd))
+ cur_msg_id, data = string.match(log, pattern)
+ end
+
+ -- update the msg_id, it persists during a cycle from echo server
+ -- start to stop.
+ msg_id = cur_msg_id
+
+ return data
+ end
+
+ function echo_server_reset()
+ stop_kong(prefix_dir)
+ msg_id = -1
+ end
+end
+
+--- Simulate a Hybrid mode DP and connect to the CP specified in `opts`.
+-- @function clustering_client
+-- @param opts Options to use, the `host`, `port`, `cert` and `cert_key` fields
+-- are required.
+-- Other fields that can be overwritten are:
+-- `node_hostname`, `node_id`, `node_version`, `node_plugins_list`. If absent,
+-- they are automatically filled.
+-- @return msg if handshake succeeded and initial message received from CP or nil, err
+local function clustering_client(opts)
+ assert(opts.host)
+ assert(opts.port)
+ assert(opts.cert)
+ assert(opts.cert_key)
+
+ local inflate_gzip = require("kong.tools.gzip").inflate_gzip
+
+ local c = assert(ws_client:new())
+ local uri = "wss://" .. opts.host .. ":" .. opts.port ..
+ "/v1/outlet?node_id=" .. (opts.node_id or uuid()) ..
+ "&node_hostname=" .. (opts.node_hostname or kong.node.get_hostname()) ..
+ "&node_version=" .. (opts.node_version or KONG_VERSION)
+
+ local conn_opts = {
+ ssl_verify = false, -- needed for busted tests as CP certs are not trusted by the CLI
+ client_cert = assert(ssl.parse_pem_cert(assert(pl_file.read(opts.cert)))),
+ client_priv_key = assert(ssl.parse_pem_priv_key(assert(pl_file.read(opts.cert_key)))),
+ server_name = opts.server_name or "kong_clustering",
+ }
+
+ local res, err = c:connect(uri, conn_opts)
+ if not res then
+ return nil, err
+ end
+ local payload = assert(cjson.encode({ type = "basic_info",
+ plugins = opts.node_plugins_list or
+ PLUGINS_LIST,
+ labels = opts.node_labels,
+ process_conf = opts.node_process_conf,
+ }))
+ assert(c:send_binary(payload))
+
+ assert(c:send_ping(string.rep("0", 32)))
+
+ local data, typ, err
+ data, typ, err = c:recv_frame()
+ c:close()
+
+ if typ == "binary" then
+ local odata = assert(inflate_gzip(data))
+ local msg = assert(cjson.decode(odata))
+ return msg
+
+ elseif typ == "pong" then
+ return "PONG"
+ end
+
+ return nil, "unknown frame from CP: " .. (typ or err)
+end
+
+
+--- Create a temporary directory, and return its path.
+-- @function tmpdir
+-- @return string path to the temporary directory
+local function tmpdir()
+ local handle = assert(io.popen("mktemp -d"))
+ local path = handle:read("*a")
+ handle:close()
+ return path:sub(1, #path - 1)
+end
+
+--- Gets the path to the fixtures directory calculating from the calling script.
+--- Useful for plugins-ee tests where `spec/fixtures` is not bundled for each plugin.
+-- @function get_fixtures_path
+-- @return string path to the fixtures directory
+local function get_fixtures_path()
+ local str = debug.getinfo(2, "S").source:sub(2)
+ local path = str:match("(.*/)") .. "fixtures/"
+ if path:sub(1, 1) ~= "/" then -- relative path
+ return lfs.currentdir() .. "/" .. path
+ end
+
+ return path
+end
+
+
+--- Generate asymmetric keys
+-- @function generate_keys
+-- @param fmt format to receive the public and private pair
+-- @return `pub, priv` key tuple or `nil + err` on failure
+local function generate_keys(fmt)
+ fmt = string.upper(fmt) or "JWK"
+ local key, err = pkey.new({
+ -- only support RSA for now
+ type = 'RSA',
+ bits = 2048,
+ exp = 65537
+ })
+ assert(key)
+ assert(err == nil, err)
+ local pub = key:tostring("public", fmt)
+ local priv = key:tostring("private", fmt)
+ return pub, priv
+end
+
+
+local make_temp_dir
+do
+ local seeded = false
+
+ function make_temp_dir()
+ if not seeded then
+ ngx.update_time()
+ math.randomseed(ngx.worker.pid() + ngx.now())
+ seeded = true
+ end
+
+ local tmp
+ local ok, err
+
+ local tries = 1000
+ for _ = 1, tries do
+ local name = "/tmp/.kong-test" .. math.random()
+
+ ok, err = pl_path.mkdir(name)
+
+ if ok then
+ tmp = name
+ break
+ end
+ end
+
+ assert(tmp ~= nil, "failed to create temporary directory " ..
+ "after " .. tostring(tries) .. " tries, " ..
+ "last error: " .. tostring(err))
+
+ return tmp, function() pl_dir.rmtree(tmp) end
+ end
+end
+
+----------------
+-- Variables/constants
+-- @section exported-fields
+
+
+--- Below is a list of fields/constants exported on the `helpers` module table:
+-- @table helpers
+-- @field dir The [`pl.dir` module of Penlight](http://tieske.github.io/Penlight/libraries/pl.dir.html)
+-- @field path The [`pl.path` module of Penlight](http://tieske.github.io/Penlight/libraries/pl.path.html)
+-- @field file The [`pl.file` module of Penlight](http://tieske.github.io/Penlight/libraries/pl.file.html)
+-- @field utils The [`pl.utils` module of Penlight](http://tieske.github.io/Penlight/libraries/pl.utils.html)
+-- @field test_conf The Kong test configuration. See also `get_running_conf` which might be slightly different.
+-- @field test_conf_path The configuration file in use.
+-- @field mock_upstream_hostname
+-- @field mock_upstream_protocol
+-- @field mock_upstream_host
+-- @field mock_upstream_port
+-- @field mock_upstream_url Base url constructed from the components
+-- @field mock_upstream_ssl_protocol
+-- @field mock_upstream_ssl_host
+-- @field mock_upstream_ssl_port
+-- @field mock_upstream_ssl_url Base url constructed from the components
+-- @field mock_upstream_stream_port
+-- @field mock_upstream_stream_ssl_port
+-- @field mock_grpc_upstream_proto_path
+-- @field grpcbin_host The host for grpcbin service, it can be set by env KONG_SPEC_TEST_GRPCBIN_HOST.
+-- @field grpcbin_port The port (SSL disabled) for grpcbin service, it can be set by env KONG_SPEC_TEST_GRPCBIN_PORT.
+-- @field grpcbin_ssl_port The port (SSL enabled) for grpcbin service it can be set by env KONG_SPEC_TEST_GRPCBIN_SSL_PORT.
+-- @field grpcbin_url The URL (SSL disabled) for grpcbin service
+-- @field grpcbin_ssl_url The URL (SSL enabled) for grpcbin service
+-- @field redis_host The host for Redis, it can be set by env KONG_SPEC_TEST_REDIS_HOST.
+-- @field redis_port The port (SSL disabled) for Redis, it can be set by env KONG_SPEC_TEST_REDIS_PORT.
+-- @field redis_ssl_port The port (SSL enabled) for Redis, it can be set by env KONG_SPEC_TEST_REDIS_SSL_PORT.
+-- @field redis_ssl_sni The server name for Redis, it can be set by env KONG_SPEC_TEST_REDIS_SSL_SNI.
+-- @field zipkin_host The host for Zipkin service, it can be set by env KONG_SPEC_TEST_ZIPKIN_HOST.
+-- @field zipkin_port the port for Zipkin service, it can be set by env KONG_SPEC_TEST_ZIPKIN_PORT.
+-- @field otelcol_host The host for OpenTelemetry Collector service, it can be set by env KONG_SPEC_TEST_OTELCOL_HOST.
+-- @field otelcol_http_port the port for OpenTelemetry Collector service, it can be set by env KONG_SPEC_TEST_OTELCOL_HTTP_PORT.
+-- @field otelcol_zpages_port the port for OpenTelemetry Collector Zpages service, it can be set by env KONG_SPEC_TEST_OTELCOL_ZPAGES_PORT.
+-- @field otelcol_file_exporter_path the path of for OpenTelemetry Collector's file exporter, it can be set by env KONG_SPEC_TEST_OTELCOL_FILE_EXPORTER_PATH.
+
+----------
+-- Exposed
+----------
+-- @export
+ return {
+ -- Penlight
+ dir = pl_dir,
+ path = pl_path,
+ file = pl_file,
+ utils = pl_utils,
+
+ -- Kong testing properties
+ db = db,
+ blueprints = blueprints,
+ get_db_utils = get_db_utils,
+ get_cache = get_cache,
+ bootstrap_database = bootstrap_database,
+ bin_path = BIN_PATH,
+ test_conf = conf,
+ test_conf_path = TEST_CONF_PATH,
+ go_plugin_path = GO_PLUGIN_PATH,
+ mock_upstream_hostname = MOCK_UPSTREAM_HOSTNAME,
+ mock_upstream_protocol = MOCK_UPSTREAM_PROTOCOL,
+ mock_upstream_host = MOCK_UPSTREAM_HOST,
+ mock_upstream_port = MOCK_UPSTREAM_PORT,
+ mock_upstream_url = MOCK_UPSTREAM_PROTOCOL .. "://" ..
+ MOCK_UPSTREAM_HOST .. ':' ..
+ MOCK_UPSTREAM_PORT,
+
+ mock_upstream_ssl_protocol = MOCK_UPSTREAM_SSL_PROTOCOL,
+ mock_upstream_ssl_host = MOCK_UPSTREAM_HOST,
+ mock_upstream_ssl_port = MOCK_UPSTREAM_SSL_PORT,
+ mock_upstream_ssl_url = MOCK_UPSTREAM_SSL_PROTOCOL .. "://" ..
+ MOCK_UPSTREAM_HOST .. ':' ..
+ MOCK_UPSTREAM_SSL_PORT,
+
+ mock_upstream_stream_port = MOCK_UPSTREAM_STREAM_PORT,
+ mock_upstream_stream_ssl_port = MOCK_UPSTREAM_STREAM_SSL_PORT,
+ mock_grpc_upstream_proto_path = MOCK_GRPC_UPSTREAM_PROTO_PATH,
+
+ zipkin_host = ZIPKIN_HOST,
+ zipkin_port = ZIPKIN_PORT,
+
+ otelcol_host = OTELCOL_HOST,
+ otelcol_http_port = OTELCOL_HTTP_PORT,
+ otelcol_zpages_port = OTELCOL_ZPAGES_PORT,
+ otelcol_file_exporter_path = OTELCOL_FILE_EXPORTER_PATH,
+
+ grpcbin_host = GRPCBIN_HOST,
+ grpcbin_port = GRPCBIN_PORT,
+ grpcbin_ssl_port = GRPCBIN_SSL_PORT,
+ grpcbin_url = string.format("grpc://%s:%d", GRPCBIN_HOST, GRPCBIN_PORT),
+ grpcbin_ssl_url = string.format("grpcs://%s:%d", GRPCBIN_HOST, GRPCBIN_SSL_PORT),
+
+ redis_host = REDIS_HOST,
+ redis_port = REDIS_PORT,
+ redis_ssl_port = REDIS_SSL_PORT,
+ redis_ssl_sni = REDIS_SSL_SNI,
+
+ blackhole_host = BLACKHOLE_HOST,
+
+ -- Kong testing helpers
+ execute = exec,
+ dns_mock = dns_mock,
+ kong_exec = kong_exec,
+ get_version = get_version,
+ get_running_conf = get_running_conf,
+ http_client = http_client,
+ grpc_client = grpc_client,
+ http2_client = http2_client,
+ make_synchronized_clients = make_synchronized_clients,
+ wait_until = wait_until,
+ pwait_until = pwait_until,
+ wait_pid = wait_pid,
+ wait_timer = wait_timer,
+ wait_for_all_config_update = wait_for_all_config_update,
+ wait_for_file = wait_for_file,
+ wait_for_file_contents = wait_for_file_contents,
+ tcp_server = tcp_server,
+ udp_server = udp_server,
+ kill_tcp_server = kill_tcp_server,
+ is_echo_server_ready = is_echo_server_ready,
+ echo_server_reset = echo_server_reset,
+ get_echo_server_received_data = get_echo_server_received_data,
+ http_mock = http_mock,
+ get_proxy_ip = get_proxy_ip,
+ get_proxy_port = get_proxy_port,
+ proxy_client = proxy_client,
+ proxy_client_grpc = proxy_client_grpc,
+ proxy_client_grpcs = proxy_client_grpcs,
+ proxy_client_h2c = proxy_client_h2c,
+ proxy_client_h2 = proxy_client_h2,
+ admin_client = admin_client,
+ admin_gui_client = admin_gui_client,
+ proxy_ssl_client = proxy_ssl_client,
+ admin_ssl_client = admin_ssl_client,
+ admin_gui_ssl_client = admin_gui_ssl_client,
+ prepare_prefix = prepare_prefix,
+ clean_prefix = clean_prefix,
+ clean_logfile = clean_logfile,
+ wait_for_invalidation = wait_for_invalidation,
+ each_strategy = each_strategy,
+ all_strategies = all_strategies,
+ validate_plugin_config_schema = validate_plugin_config_schema,
+ clustering_client = clustering_client,
+ tmpdir = tmpdir,
+ https_server = https_server,
+ stress_generator = stress_generator,
+ get_fixtures_path = get_fixtures_path,
+
+ -- miscellaneous
+ intercept = intercept,
+ openresty_ver_num = openresty_ver_num(),
+ unindent = unindent,
+ make_yaml_file = make_yaml_file,
+ setenv = setenv,
+ unsetenv = unsetenv,
+ deep_sort = deep_sort,
+
+ -- launching Kong subprocesses
+ start_kong = start_kong,
+ stop_kong = stop_kong,
+ cleanup_kong = cleanup_kong,
+ restart_kong = restart_kong,
+ reload_kong = reload_kong,
+ get_kong_workers = get_kong_workers,
+ wait_until_no_common_workers = wait_until_no_common_workers,
+
+ start_grpc_target = start_grpc_target,
+ stop_grpc_target = stop_grpc_target,
+ get_grpc_target_port = get_grpc_target_port,
+ generate_keys = generate_keys,
+
+ -- Only use in CLI tests from spec/02-integration/01-cmd
+ kill_all = function(prefix, timeout)
+ local kill = require "kong.cmd.utils.kill"
+
+ local running_conf = get_running_conf(prefix)
+ if not running_conf then return end
+
+ -- kill kong_tests.conf service
+ local pid_path = running_conf.nginx_pid
+ if pl_path.exists(pid_path) then
+ kill.kill(pid_path, "-TERM")
+ wait_pid(pid_path, timeout)
+ end
+ end,
+
+ with_current_ws = function(ws,fn, db)
+ local old_ws = ngx.ctx.workspace
+ ngx.ctx.workspace = nil
+ ws = ws or {db.workspaces:select_by_name("default")}
+ ngx.ctx.workspace = ws[1] and ws[1].id
+ local res = fn()
+ ngx.ctx.workspace = old_ws
+ return res
+ end,
+
+ signal = function(prefix, signal, pid_path)
+ local kill = require "kong.cmd.utils.kill"
+
+ if not pid_path then
+ local running_conf = get_running_conf(prefix)
+ if not running_conf then
+ error("no config file found at prefix: " .. prefix)
+ end
+
+ pid_path = running_conf.nginx_pid
+ end
+
+ return kill.kill(pid_path, signal)
+ end,
+
+ -- send signal to all Nginx workers, not including the master
+ signal_workers = function(prefix, signal, pid_path)
+ if not pid_path then
+ local running_conf = get_running_conf(prefix)
+ if not running_conf then
+ error("no config file found at prefix: " .. prefix)
+ end
+
+ pid_path = running_conf.nginx_pid
+ end
+
+ local cmd = string.format("pkill %s -P `cat %s`", signal, pid_path)
+ local _, _, _, _, code = shell.run(cmd)
+
+ if not pid_dead(pid_path) then
+ return false
+ end
+
+ return code
+ end,
+ -- returns the plugins and version list that is used by Hybrid mode tests
+ get_plugins_list = function()
+ assert(PLUGINS_LIST, "plugin list has not been initialized yet, " ..
+ "you must call get_db_utils first")
+ return table_clone(PLUGINS_LIST)
+ end,
+ get_available_port = get_available_port,
+
+ make_temp_dir = make_temp_dir,
+
+ -- XXX EE
+ is_enterprise_plugin = function(plugin_name)
+ for _, ee_plugin_name in pairs(dist_constants.plugins) do
+ if ee_plugin_name == plugin_name then
+ return true
+ end
+ end
+ return false
+ end,
+
+ is_fips_build = function()
+ local pro = require "resty.openssl.provider"
+ local p = pro.load("fips")
+ if p then p:unload() end
+ return p ~= nil
+ end,
+ -- EE
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/ai/embeddings_mock.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/embeddings_mock.lua
new file mode 100644
index 00000000..11181770
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/embeddings_mock.lua
@@ -0,0 +1,93 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--
+-- imports
+--
+
+local cjson = require("cjson")
+local gzip = require("kong.tools.gzip")
+
+--
+-- public vars
+--
+
+-- some previously generated text embeddings for mocking, using OpenAI's
+-- text-embedding-3-small model and 4 dimensions.
+local known_text_embeddings = {
+ ["dog"] = { 0.56267416, -0.20551957, -0.047182854, 0.79933304 },
+ ["cat"] = { 0.4653789, -0.42677408, -0.29335415, 0.717795 },
+ ["capacitor"] = { 0.350534, -0.025470039, -0.9204002, -0.17129119 },
+ ["smell"] = { 0.23342973, -0.08322083, -0.8492907, -0.46614397 },
+ ["Non-Perturbative Quantum Field Theory and Resurgence in Supersymmetric Gauge Theories"] = {
+ -0.6826024, -0.08655233, -0.72073454, -0.084287055,
+ },
+ ["taco"] = { -0.4407651, -0.85174876, -0.27901474, -0.048999753 },
+}
+
+--
+-- public functions
+--
+
+local function mock_embeddings(opts)
+ if opts.method ~= "POST" then
+ return nil, "Only POST method is supported"
+ end
+
+ if opts.headers["Content-Type"] ~= "application/json" then
+ return nil, "Only application/json content type is supported"
+ end
+
+ if opts.headers["Accept-Encoding"] ~= "gzip" then
+ return nil, "Only gzip encoding is supported"
+ end
+
+ if not opts.headers["Authorization"] then
+ return nil, "Authorization header is required"
+ end
+
+ local request_body = cjson.decode(opts.body)
+
+ if not request_body.dimensions then
+ request_body.dimensions = 4
+ end
+ if request_body.dimensions ~= 4 then
+ return nil, "Only 4 dimensions are supported"
+ end
+
+ local prompt = request_body.input
+ local embedding = known_text_embeddings[prompt]
+ if not embedding then
+ return nil, "Invalid prompt"
+ end
+
+ local response_body = {
+ data = {
+ { embedding = embedding },
+ },
+ }
+
+ local encoded_response_body = cjson.encode(response_body)
+ local gzipped_response_body = gzip.deflate_gzip(encoded_response_body)
+
+ return {
+ status = 200,
+ body = gzipped_response_body,
+ }
+end
+
+--
+-- module
+--
+
+return {
+ -- vars
+ known_text_embeddings = known_text_embeddings,
+
+ -- functions
+ mock_embeddings = mock_embeddings,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/ai/mistralai_mock.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/mistralai_mock.lua
new file mode 100644
index 00000000..724c8739
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/mistralai_mock.lua
@@ -0,0 +1,64 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--
+-- imports
+--
+
+local mocker = require("spec.fixtures.mocker")
+
+local mock_embeddings = require("spec.helpers.ai.embeddings_mock").mock_embeddings
+
+--
+-- private vars
+--
+
+local api = "https://api.mistral.ai"
+local embeddings_url = api .. "/v1/embeddings"
+
+--
+-- private functions
+--
+
+local mock_request_router = function(_self, url, opts)
+ if not string.find("^" .. url, api) then
+ return nil, "what are you doing?"
+ end
+
+ if url == embeddings_url then
+ return mock_embeddings(opts)
+ end
+
+ return nil, "URL " .. url .. " is not supported by mocking"
+end
+
+--
+-- public functions
+--
+
+local function setup(finally)
+ mocker.setup(finally, {
+ modules = {
+ { "resty.http", {
+ new = function()
+ return {
+ request_uri = mock_request_router,
+ }
+ end,
+ } },
+ }
+ })
+end
+
+--
+-- module
+--
+
+return {
+ -- functions
+ setup = setup,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/ai/openai_mock.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/openai_mock.lua
new file mode 100644
index 00000000..1366c9d6
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/openai_mock.lua
@@ -0,0 +1,64 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--
+-- imports
+--
+
+local mocker = require("spec.fixtures.mocker")
+
+local mock_embeddings = require("spec.helpers.ai.embeddings_mock").mock_embeddings
+
+--
+-- private vars
+--
+
+local api = "https://api.openai.com"
+local embeddings_url = api .. "/v1/embeddings"
+
+--
+-- private functions
+--
+
+local mock_request_router = function(_self, url, opts)
+ if not string.find("^" .. url, api) then
+ return nil, "what are you doing?"
+ end
+
+ if url == embeddings_url then
+ return mock_embeddings(opts)
+ end
+
+ return nil, "URL " .. url .. " is not supported by mocking"
+end
+
+--
+-- public functions
+--
+
+local function setup(finally)
+ mocker.setup(finally, {
+ modules = {
+ { "resty.http", {
+ new = function()
+ return {
+ request_uri = mock_request_router,
+ }
+ end,
+ } },
+ }
+ })
+end
+
+--
+-- module
+--
+
+return {
+ -- functions
+ setup = setup,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/ai/redis_mock.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/redis_mock.lua
new file mode 100644
index 00000000..464bc930
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/ai/redis_mock.lua
@@ -0,0 +1,289 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--
+-- imports
+--
+
+local cjson = require("cjson.safe")
+local ffi = require("ffi")
+
+local mocker = require("spec.fixtures.mocker")
+
+--
+-- private vars
+--
+
+-- the error message to force on the next Redis call
+local forced_error_msg = nil
+
+--
+-- private functions
+--
+
+-- the default precision to round to during conversion
+local default_precision = 1e-6
+
+-- Redis requires a vector to be converted to a byte string, this function reverses
+-- that process so that we can compare vectors.
+--
+-- @param bytes the byte string to convert
+-- @param precision the precision to round to (optional)
+-- @return the vector
+local function convert_bytes_to_vector(bytes, precision)
+ precision = precision or default_precision
+ local float_size = ffi.sizeof("float")
+ local num_floats = #bytes / float_size
+ local float_array = ffi.cast("float*", bytes)
+ local vector = {}
+ for i = 0, num_floats - 1 do
+ local value = float_array[i]
+ value = math.floor(value / precision + 0.5) * precision -- round to precision
+ table.insert(vector, value)
+ end
+ return vector
+end
+
+-- Searches for the cosine distance between two vectors, and compares it
+-- against a threshold.
+--
+-- @param v1 the first vector
+-- @param v2 the second vector
+-- @param threshold the threshold to compare against
+-- @return true if the vectors are within the threshold, false otherwise
+-- @return the distance between the vectors
+local function cosine_distance(v1, v2, threshold)
+ local dot_product = 0.0
+ local magnitude_v1 = 0.0
+ local magnitude_v2 = 0.0
+
+ for i = 1, #v1 do
+ dot_product = dot_product + v1[i] * v2[i]
+ magnitude_v1 = magnitude_v1 + v1[i] ^ 2
+ magnitude_v2 = magnitude_v2 + v2[i] ^ 2
+ end
+
+ magnitude_v1 = math.sqrt(magnitude_v1)
+ magnitude_v2 = math.sqrt(magnitude_v2)
+
+ local cosine_similarity = dot_product / (magnitude_v1 * magnitude_v2)
+ local cosine_distance = 1 - cosine_similarity
+
+ return cosine_distance <= threshold, cosine_distance
+end
+
+-- Searches for the euclidean distance between two vectors, and compares it
+-- against a threshold.
+--
+-- @param v1 the first vector
+-- @param v2 the second vector
+-- @param threshold the threshold to compare against
+-- @return true if the vectors are within the threshold, false otherwise
+-- @return the distance between the vectors
+local function euclidean_distance(v1, v2, threshold)
+ local distance = 0.0
+ for i = 1, #v1 do
+ distance = distance + (v1[i] - v2[i]) ^ 2
+ end
+
+ distance = math.sqrt(distance)
+
+ return distance <= threshold, distance
+end
+
+--
+-- public functions
+--
+
+local function setup(finally)
+ mocker.setup(finally, {
+ modules = {
+ { "resty.redis", {
+ new = function()
+ return {
+ -- function mocks
+ set_timeouts = function() end,
+ connect = function()
+ if forced_error_msg then
+ return false, forced_error_msg
+ end
+ end,
+ auth = function()
+ if forced_error_msg then
+ return false, forced_error_msg
+ end
+ end,
+ ping = function()
+ if forced_error_msg then
+ return false, forced_error_msg
+ end
+ end,
+
+ -- raw command mocks
+ ["FT.CREATE"] = function(red, index, ...)
+ if forced_error_msg then
+ return false, forced_error_msg
+ end
+
+ if not index or index == "idx:_vss" then
+ return false, "Invalid index name"
+ end
+
+ -- gather the distance metric
+ local args = { ... }
+ local distance_metric = args[#args]
+ if distance_metric ~= "EUCLIDEAN" and distance_metric ~= "COSINE" then
+ return false, "Invalid distance metric"
+ end
+
+ red.indexes[index] = distance_metric
+ return true, nil
+ end,
+ ["FT.DROPINDEX"] = function(red, index, ...)
+ if forced_error_msg then
+ return false, forced_error_msg
+ end
+
+ if not red.indexes[index] then
+ return false, "Index not found"
+ end
+
+ red.indexes[index] = nil
+ return true, nil
+ end,
+ ["FT.SEARCH"] = function(red, index, ...)
+ if forced_error_msg then
+ return nil, forced_error_msg
+ end
+
+ -- verify whether the index for the search is valid,
+ -- and determine whether the index was configured
+ -- with euclidean or cosine distance
+ local distance_metric = red.indexes[index]
+ if not distance_metric then
+ return nil, "Index not found"
+ end
+
+ -- determine the threshold, and record
+ local num_args = select("#", ...)
+ local threshold = select(num_args, ...)
+ red.last_threshold_received = threshold
+
+ -- determine the vector
+ local vector_bytes = select(num_args - 2, ...)
+ local search_vector = convert_bytes_to_vector(vector_bytes)
+
+ -- The caller can override the response with mock_next_search to set this next_response_key
+ -- and that will force a specific payload to be returned, if desired.
+ local payload = red.cache[red.next_response_key]
+ if payload then
+ -- reset the override
+ red.next_response_key = nil
+
+ -- the structure Redis would respond with, but we only care about the proximity and payload
+ return { {}, {}, { {}, "1.0", {}, payload } }
+ end
+
+ -- if the payload wasn't forced with an override, we'll do a vector search.
+ -- we won't try to fully emulate Redis' vector search but we can do a simple
+ -- distance comparison to emulate it.
+ local payloads = {}
+ for _key, value in pairs(red.cache) do
+ local decoded_payload, err = cjson.decode(value)
+ if err then
+ return nil, err
+ end
+
+ -- check the proximity of the found vector
+ local found_vector = decoded_payload.vector
+ local proximity_match, distance
+ if distance_metric == "COSINE" then
+ proximity_match, distance = cosine_distance(search_vector, found_vector, threshold)
+ elseif distance_metric == "EUCLIDEAN" then
+ proximity_match, distance = euclidean_distance(search_vector, found_vector, threshold)
+ end
+ if proximity_match then
+ table.insert(payloads, { {}, tostring(distance), {}, value })
+ end
+ end
+
+ -- sort the payloads by distance
+ table.sort(payloads, function(a, b)
+ return tonumber(a[2]) < tonumber(b[2])
+ end)
+
+ -- if no payloads were found, just return an empty table to emulate cache miss
+ if #payloads < 1 then
+ return {}
+ end
+
+ -- the structure Redis would respond with, but we only care about the proximity and payload
+ local res = { {}, {} } -- filler response information from Redis we don't use
+ for i = 1, #payloads do
+ table.insert(res, payloads[i])
+ end
+ return res, nil
+ end,
+ ["JSON.GET"] = function(red, key)
+ if forced_error_msg then
+ return nil, forced_error_msg
+ end
+
+ return red.cache[key], nil
+ end,
+ ["JSON.SET"] = function(red, key, _path, payload) -- currently, path is not used because we only set cache at root
+ if forced_error_msg then
+ return false, forced_error_msg
+ end
+
+ if red.cache[key] ~= nil then
+ return false, "Already exists"
+ end
+
+ red.key_count = red.key_count + 1
+ red.cache[key] = payload
+
+ return true, nil
+ end,
+ ["JSON.DEL"] = function(red, key, path)
+ if forced_error_msg then
+ return false, forced_error_msg
+ end
+
+ red.key_count = red.key_count - 1
+ red.cache[key] = nil
+
+ return true, nil
+ end,
+
+ -- internal tracking
+ indexes = {},
+ key_count = 0,
+ cache = {},
+ next_response_key = nil,
+ last_threshold_received = 0.0,
+ }
+ end,
+ mock_next_search = function(red, key)
+ red.next_response_key = key
+ end,
+ forced_failure = function(err_msg)
+ forced_error_msg = err_msg
+ end,
+ } },
+ }
+ })
+end
+
+--
+-- module
+--
+
+return {
+ -- functions
+ setup = setup,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/dns.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/dns.lua
new file mode 100644
index 00000000..c1d90b6f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/dns.lua
@@ -0,0 +1,178 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+--- test helper methods for DNS and load-balancers
+-- @module spec.helpers.dns
+
+local _M = {}
+
+
+if ngx then
+ _M.gettime = ngx.now
+ _M.sleep = ngx.sleep
+else
+ local socket = require("socket")
+ _M.gettime = socket.gettime
+ _M.sleep = socket.sleep
+end
+local gettime = _M.gettime
+
+
+--- Iterator over different balancer types.
+-- returns; consistent-hash, round-robin, least-conn
+-- @return `algorithm_name`, `balancer_module`
+function _M.balancer_types()
+ local b_types = {
+ -- algorithm name
+ { "consistent-hashing", "consistent_hashing" },
+ { "round-robin", "round_robin" },
+ { "least-connections", "least_connections" },
+ }
+ local i = 0
+ return function()
+ i = i + 1
+ if b_types[i] then
+ return b_types[i][1], require("resty.dns.balancer." .. b_types[i][2])
+ end
+ end
+end
+
+
+--- Expires a record now.
+-- @param record a DNS record previously created
+function _M.dnsExpire(record)
+ record.expire = gettime() - 1
+end
+
+
+--- Creates an SRV record in the cache.
+-- @tparam dnsclient client the dns client in which cache it is to be stored
+-- @tparam table records a single entry, or a list of entries for the hostname
+-- @tparam[opt=4] number staleTtl the staleTtl to use for the record TTL (see Kong config reference for description)
+-- @usage
+-- local host = "konghq.com" -- must be the same for all entries obviously...
+-- local rec = dnsSRV(dnsCLient, {
+-- -- defaults: weight = 10, priority = 20, ttl = 600
+-- { name = host, target = "20.20.20.20", port = 80, weight = 10, priority = 20, ttl = 600 },
+-- { name = host, target = "50.50.50.50", port = 80, weight = 10, priority = 20, ttl = 600 },
+-- })
+function _M.dnsSRV(client, records, staleTtl)
+ local dnscache = client.getcache()
+ -- if single table, then insert into a new list
+ if not records[1] then records = { records } end
+
+ for _, record in ipairs(records) do
+ record.type = client.TYPE_SRV
+
+ -- check required input
+ assert(record.target, "target field is required for SRV record")
+ assert(record.name, "name field is required for SRV record")
+ assert(record.port, "port field is required for SRV record")
+ record.name = record.name:lower()
+
+ -- optionals, insert defaults
+ record.weight = record.weight or 10
+ record.ttl = record.ttl or 600
+ record.priority = record.priority or 20
+ record.class = record.class or 1
+ end
+ -- set timeouts
+ records.touch = gettime()
+ records.expire = gettime() + records[1].ttl
+
+ -- create key, and insert it
+ local key = records[1].type..":"..records[1].name
+ dnscache:set(key, records, records[1].ttl + (staleTtl or 4))
+ -- insert last-succesful lookup type
+ dnscache:set(records[1].name, records[1].type)
+ return records
+end
+
+
+--- Creates an A record in the cache.
+-- @tparam dnsclient client the dns client in which cache it is to be stored
+-- @tparam table records a single entry, or a list of entries for the hostname
+-- @tparam[opt=4] number staleTtl the staleTtl to use for the record TTL (see Kong config reference for description)
+-- @usage
+-- local host = "konghq.com" -- must be the same for all entries obviously...
+-- local rec = dnsSRV(dnsCLient, {
+-- -- defaults: ttl = 600
+-- { name = host, address = "20.20.20.20", ttl = 600 },
+-- { name = host, address = "50.50.50.50", ttl = 600 },
+-- })
+function _M.dnsA(client, records, staleTtl)
+ local dnscache = client.getcache()
+ -- if single table, then insert into a new list
+ if not records[1] then records = { records } end
+
+ for _, record in ipairs(records) do
+ record.type = client.TYPE_A
+
+ -- check required input
+ assert(record.address, "address field is required for A record")
+ assert(record.name, "name field is required for A record")
+ record.name = record.name:lower()
+
+ -- optionals, insert defaults
+ record.ttl = record.ttl or 600
+ record.class = record.class or 1
+ end
+ -- set timeouts
+ records.touch = gettime()
+ records.expire = gettime() + records[1].ttl
+
+ -- create key, and insert it
+ local key = records[1].type..":"..records[1].name
+ dnscache:set(key, records, records[1].ttl + (staleTtl or 4))
+ -- insert last-succesful lookup type
+ dnscache:set(records[1].name, records[1].type)
+ return records
+end
+
+
+--- Creates an AAAA record in the cache.
+-- @tparam dnsclient client the dns client in which cache it is to be stored
+-- @tparam table records a single entry, or a list of entries for the hostname
+-- @tparam[opt=4] number staleTtl the staleTtl to use for the record TTL (see Kong config reference for description)
+-- @usage
+-- local host = "konghq.com" -- must be the same for all entries obviously...
+-- local rec = dnsSRV(dnsCLient, {
+-- -- defaults: ttl = 600
+-- { name = host, address = "::1", ttl = 600 },
+-- })
+function _M.dnsAAAA(client, records, staleTtl)
+ local dnscache = client.getcache()
+ -- if single table, then insert into a new list
+ if not records[1] then records = { records } end
+
+ for _, record in ipairs(records) do
+ record.type = client.TYPE_AAAA
+
+ -- check required input
+ assert(record.address, "address field is required for AAAA record")
+ assert(record.name, "name field is required for AAAA record")
+ record.name = record.name:lower()
+
+ -- optionals, insert defaults
+ record.ttl = record.ttl or 600
+ record.class = record.class or 1
+ end
+ -- set timeouts
+ records.touch = gettime()
+ records.expire = gettime() + records[1].ttl
+
+ -- create key, and insert it
+ local key = records[1].type..":"..records[1].name
+ dnscache:set(key, records, records[1].ttl + (staleTtl or 4))
+ -- insert last-succesful lookup type
+ dnscache:set(records[1].name, records[1].type)
+ return records
+end
+
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock.lua
new file mode 100644
index 00000000..1813f125
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock.lua
@@ -0,0 +1,283 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--- Module implementing http_mock, a HTTP mocking server for testing.
+-- @module spec.helpers.http_mock
+
+local helpers = require "spec.helpers"
+
+local pairs = pairs
+local ipairs = ipairs
+local type = type
+local setmetatable = setmetatable
+
+local modules = {
+ require "spec.helpers.http_mock.nginx_instance",
+ require "spec.helpers.http_mock.asserts",
+ require "spec.helpers.http_mock.debug_port",
+ require "spec.helpers.http_mock.clients",
+}
+
+local http_mock = {}
+
+-- since http_mock contains a lot of functionality, it is implemented in separate submodules
+-- and combined into one large http_mock module here.
+for _, module in ipairs(modules) do
+ for k, v in pairs(module) do
+ http_mock[k] = v
+ end
+end
+
+-- get a session from the logs with a timeout
+-- throws error if no request is recieved within the timeout
+-- @treturn table the session
+function http_mock:get_session()
+ local ret
+ self.eventually:has_session_satisfy(function(s)
+ ret = s
+ return true
+ end)
+ return ret
+end
+
+-- get a request from the logs with a timeout
+-- throws error if no request is recieved within the timeout
+-- @treturn table the request
+function http_mock:get_request()
+ return self:get_session().req
+end
+
+-- get a response from the logs with a timeout
+-- throws error if no request is recieved within the timeout
+-- @treturn table the response
+function http_mock:get_response()
+ return self:get_session().resp
+end
+
+local http_mock_MT = { __index = http_mock, __gc = http_mock.stop }
+
+
+-- TODO: make default_mocking the same to the `mock_upstream`
+local default_mocking = {
+ ["/"] = {
+ access = [[
+ ngx.req.set_header("X-Test", "test")
+ ngx.print("ok")
+ ngx.exit(200)
+ ]],
+ },
+}
+
+local function default_field(tbl, key, default)
+ if tbl[key] == nil then
+ tbl[key] = default
+ end
+end
+
+--- create a mock instance which represents a HTTP mocking server
+-- @tparam[opt] table|string|number listens the listen directive of the mock server. This can be
+-- a single directive (string), or a list of directives (table), or a number which will be used as the port.
+-- Defaults to a random available port
+-- @tparam[opt] table|string routes the code of the mock server, defaults to a simple response. See Examples.
+-- @tparam[opt={}] table opts options for the mock server, supporting fields:
+-- @tparam[opt="servroot_tapping"] string opts.prefix the prefix of the mock server
+-- @tparam[opt="_"] string opts.hostname the hostname of the mock server
+-- @tparam[opt=false] bool opts.tls whether to use tls
+-- @tparam[opt={}] table opts.directives the extra directives of the mock server
+-- @tparam[opt={}] table opts.log_opts the options for logging with fields listed below:
+-- @tparam[opt=true] bool opts.log_opts.collect_req whether to log requests()
+-- @tparam[opt=true] bool opts.log_opts.collect_req_body_large whether to log large request bodies
+-- @tparam[opt=false] bool opts.log_opts.collect_resp whether to log responses
+-- @tparam[opt=false] bool opts.log_opts.collect_resp_body whether to log response bodies
+-- @tparam[opt=true] bool opts.log_opts.collect_err: whether to log errors
+-- @tparam[opt] string opts.init: the lua code injected into the init_by_lua_block
+-- @treturn http_mock a mock instance
+-- @treturn string the port the mock server listens to
+-- @usage
+-- local mock = http_mock.new(8000, [[
+-- ngx.req.set_header("X-Test", "test")
+-- ngx.print("hello world")
+-- ]], {
+-- prefix = "mockserver",
+-- log_opts = {
+-- resp = true,
+-- resp_body = true,
+-- },
+-- tls = true,
+-- })
+--
+-- mock:start()
+-- local client = mock:get_client() -- get a client to access the mocking port
+-- local res = assert(client:send({}))
+-- assert.response(res).has.status(200)
+-- assert.response(res).has.header("X-Test", "test")
+-- assert.response(res).has.body("hello world")
+-- mock.eventually:has_response(function(resp)
+-- assert.same(resp.body, "hello world")
+-- end)
+-- mock:wait_until_no_request() -- wait until all the requests are finished
+-- mock:clean() -- clean the logs
+-- client:send({})
+-- client:send({})
+-- local logs = mock:retrieve_mocking_logs() -- get all the logs of HTTP sessions
+-- mock:stop()
+--
+-- listens can be a number, which will be used as the port of the mock server;
+-- or a string, which will be used as the param of listen directive of the mock server;
+-- or a table represents multiple listen ports.
+-- if the port is not specified, a random port will be used.
+-- call mock:get_default_port() to get the first port the mock server listens to.
+-- if the port is a number and opts.tls is set to ture, ssl will be appended.
+--
+-- routes can be a table like this:
+-- routes = {
+-- ["/"] = {
+-- access = [[
+-- ngx.req.set_header("X-Test", "test")
+-- ngx.print("hello world")
+-- ]],
+-- log = [[
+-- ngx.log(ngx.ERR, "log test!")
+-- ]],
+-- directives = {
+-- "rewrite ^/foo /bar break;",
+-- },
+-- },
+-- }
+--
+-- -- or single a string, which will be used as the access phase handler.
+-- routes = [[ ngx.print("hello world") ]]
+-- -- which is equivalent to:
+-- routes = {
+-- ["/"] = {
+-- access = [[ ngx.print("hello world") ]],
+-- },
+-- }
+function http_mock.new(listens, routes, opts)
+ opts = opts or {}
+
+ if listens == nil then
+ listens = helpers.get_available_port()
+ end
+
+ if type(listens) == "number" then
+ listens = "0.0.0.0:" .. listens .. (opts.tls and " ssl" or "")
+ end
+
+ if type(listens) == "string" then
+ listens = { listens, }
+ end
+
+ if routes == nil then
+ routes = default_mocking
+ elseif type(routes) == "string" then
+ routes = {
+ ["/"] = {
+ access = routes,
+ }
+ }
+ end
+
+ opts.log_opts = opts.log_opts or {}
+ local log_opts = opts.log_opts
+ default_field(log_opts, "req", true)
+ default_field(log_opts, "req_body_large", true)
+ -- usually we can check response from client side
+ default_field(log_opts, "resp", false)
+ default_field(log_opts, "resp_body", false)
+ default_field(log_opts, "err", true)
+
+ local prefix = opts.prefix or "servroot_mock"
+ local hostname = opts.hostname or "_"
+ local directives = opts.directives or {}
+
+ local _self = setmetatable({
+ prefix = prefix,
+ hostname = hostname,
+ listens = listens,
+ routes = routes,
+ directives = directives,
+ dicts = opts.dicts,
+ init = opts.init,
+ log_opts = log_opts,
+ logs = {},
+ tls = opts.tls,
+ eventually_timeout = opts.eventually_timeout or 5,
+ }, http_mock_MT)
+
+ local port = _self:get_default_port()
+
+ if port then
+ _self.client_opts = {
+ port = port,
+ tls = opts.tls,
+ }
+ end
+
+ _self:_set_eventually_table()
+ _self:_setup_debug()
+ return _self, port
+end
+
+--- @type http_mock
+
+--- returns the default port of the mock server.
+-- @function http_mock:get_default_port
+-- @treturn string the port of the mock server (from the first listen directive)
+function http_mock:get_default_port()
+ return self.listens[1]:match(":(%d+)")
+end
+
+--- retrieve the logs of HTTP sessions
+-- @function http_mock:retrieve_mocking_logs
+-- @treturn table the logs of HTTP sessions
+
+--- purge the logs of HTTP sessions
+-- @function http_mock:purge_mocking_logs
+
+--- clean the logs of HTTP sessions
+-- @function http_mock:clean
+
+--- wait until all the requests are finished
+-- @function http_mock:wait_until_no_request
+-- @tparam[opt=true,default=5] number timeout the timeout to wait for the nginx process to exit
+
+--- make assertions on HTTP requests.
+-- with a timeout to wait for the requests to arrive
+-- @table http_mock.eventually
+
+--- assert if the condition is true for one of the logs.
+--- Replace "session" in the name of the function to assert on fields of the log.
+--- The field can be one of "session", "request", "response", "error".
+-- @function http_mock.eventually:has_session_satisfy
+-- @tparam function check the check function, accept a log and throw error if the condition is not satisfied
+
+--- assert if the condition is true for all the logs.
+-- Replace "session" in the name of the function to assert on fields of the log.
+-- The field can be one of "session", "request", "response", "error".
+-- @function http_mock.eventually:all_session_satisfy
+-- @tparam function check the check function, accept a log and throw error if the condition is not satisfied
+
+--- assert if none of the logs satisfy the condition.
+-- Replace "session" in the name of the function to assert on fields of the log.
+-- The field can be one of "session", "request", "response", "error".
+-- @function http_mock.eventually:has_no_session_satisfy
+-- @tparam function check the check function, accept a log and throw error if the condition is not satisfied
+
+--- assert if not all the logs satisfy the condition.
+-- Replace "session" in the name of the function to assert on fields of the log.
+-- The field can be one of "session", "request", "response", "error".
+-- @function http_mock.eventually:not_all_session_satisfy
+-- @tparam function check the check function, accept a log and throw error if the condition is not satisfied
+
+--- alias for eventually:not_all_{session,request,response,error}_satisfy.
+-- Replace "session" in the name of the function to assert on fields of the log.
+-- The field can be one of "session", "request", "response", "error".
+-- @function http_mock.eventually:has_one_without_session_satisfy
+-- @tparam function check the check function, accept a log and throw error if the condition is not satisfied
+
+return http_mock
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/asserts.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/asserts.lua
new file mode 100644
index 00000000..5bd8de5e
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/asserts.lua
@@ -0,0 +1,171 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local setmetatable = setmetatable
+local ipairs = ipairs
+local pairs = pairs
+local pcall = pcall
+local error = error
+
+local http_mock = {}
+
+local build_in_checks = {}
+
+local eventually_MT = {}
+eventually_MT.__index = eventually_MT
+
+local step_time = 0.01
+
+-- example for a check function
+-- local function(session, status)
+-- -- must throw error if the assertion is not true
+-- -- instead of return false
+-- assert.same(session.resp.status, status)
+-- -- return a string to tell what condition is satisfied
+-- -- so we can construct an error message for reverse assertion
+-- -- in this case it would be "we don't expect that: has a response with status 200"
+-- return "has a response with status " .. status
+-- end
+
+local function eventually_has(check, mock, ...)
+ local time = 0
+ local ok, err
+ while time < mock.eventually_timeout do
+ local logs = mock:retrieve_mocking_logs()
+ for _, log in ipairs(logs) do
+ -- use pcall so the user may use lua assert like assert.same
+ ok, err = pcall(check, log, ...)
+ if ok then
+ return true
+ end
+ end
+
+ ngx.sleep(step_time)
+ time = time + step_time
+ end
+
+ error(err or "assertion fail. No request is sent and recorded.", 2)
+end
+
+-- wait until timeout to check if the assertion is true for all logs
+local function eventually_all(check, mock, ...)
+ local time = 0
+ local ok, err
+ while time < mock.eventually_timeout do
+ local logs = mock:retrieve_mocking_logs()
+ for _, log in ipairs(logs) do
+ ok, err = pcall(check, log, ...)
+ if not ok then
+ error(err or "assertion fail", 2)
+ end
+ end
+
+ ngx.sleep(step_time)
+ time = time + step_time
+ end
+
+ return true
+end
+
+-- a session is a request/response pair
+function build_in_checks.session_satisfy(session, f)
+ return f(session) or "session satisfy"
+end
+
+function build_in_checks.request_satisfy(session, f)
+ return f(session.req) or "request satisfy"
+end
+
+function build_in_checks.request()
+ return "request exist"
+end
+
+function build_in_checks.response_satisfy(session, f)
+ return f(session.resp) or "response satisfy"
+end
+
+function build_in_checks.error_satisfy(session, f)
+ return f(session.err) or "error satisfy"
+end
+
+function build_in_checks.error(session)
+ assert(session.err, "has no error")
+ return "has error"
+end
+
+local function register_assert(name, impl)
+ eventually_MT["has_" .. name] = function(self, ...)
+ return eventually_has(impl, self.__mock, ...)
+ end
+
+ eventually_MT["all_" .. name] = function(self, ...)
+ return eventually_all(impl, self.__mock, ...)
+ end
+
+ local function reverse_impl(session, ...)
+ local ok, err = pcall(impl, session, ...)
+ if ok then
+ error("we don't expect that: " .. (name or err), 2)
+ end
+ return true
+ end
+
+ eventually_MT["has_no_" .. name] = function(self, ...)
+ return eventually_all(reverse_impl, self.__mock, ...)
+ end
+
+ eventually_MT["not_all_" .. name] = function(self, ...)
+ return eventually_has(reverse_impl, self.__mock, ...)
+ end
+
+ eventually_MT["has_one_without_" .. name] = eventually_MT["not_all_" .. name]
+end
+
+for name, impl in pairs(build_in_checks) do
+ register_assert(name, impl)
+end
+
+
+function http_mock:_set_eventually_table()
+ local eventually = setmetatable({}, eventually_MT)
+ eventually.__mock = self
+ self.eventually = eventually
+ return eventually
+end
+
+-- usually this function is not called by a user. I will add more assertions in the future with it. @StarlightIbuki
+
+-- @function http_mock.register_assert()
+-- @param name: the name of the assertion
+-- @param impl: the implementation of the assertion
+-- implement a new eventually assertion
+-- @usage:
+-- impl is a function
+-- -- @param session: the session object, with req, resp, err, start_time, end_time as fields
+-- -- @param ...: the arguments passed to the assertion
+-- -- @return: human readable message if the assertion is true, or throw error if not
+--
+-- a session means a request/response pair.
+-- The impl callback throws error if the assertion is not true
+-- and returns a string to tell what condition is satisfied
+-- This design is to allow the user to use lua asserts in the callback
+-- (or even callback the registered assertion accept as argument), like the example;
+-- and for has_no/not_all assertions, we can construct an error message for it like:
+-- "we don't expect that: has header foo"
+-- @example:
+-- http_mock.register_assert("req_has_header", function(mock, name)
+-- assert.same(name, session.req.headers[name])
+-- return "has header " .. name
+-- end)
+-- mock.eventually:has_req_has_header("foo")
+-- mock.eventually:has_no_req_has_header("bar")
+-- mock.eventually:all_req_has_header("baz")
+-- mock.eventually:not_all_req_has_header("bar")
+http_mock.register_assert = register_assert
+
+return http_mock
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/clients.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/clients.lua
new file mode 100644
index 00000000..40e1c89b
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/clients.lua
@@ -0,0 +1,38 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--- part of http_mock
+-- @submodule spec.helpers.http_mock
+
+local helpers = require "spec.helpers"
+local http_client = helpers.http_client
+
+local http_mock = {}
+
+--- get a `helpers.http_client` to access the mock server
+-- @function http_mock:get_client
+-- @treturn http_client a `helpers.http_client` instance
+-- @within http_mock
+-- @usage
+-- httpc = http_mock:get_client()
+-- result = httpc:get("/services/foo", opts)
+function http_mock:get_client()
+ local client = self.client
+ if not client then
+ client = http_client({
+ scheme = self.client_opts.tls and "https" or "http",
+ host = "localhost",
+ port = self.client_opts.port,
+ })
+
+ self.client = client
+ end
+
+ return client
+end
+
+return http_mock
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/debug_port.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/debug_port.lua
new file mode 100644
index 00000000..3933e178
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/debug_port.lua
@@ -0,0 +1,125 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local helpers = require "spec.helpers"
+local http = require "resty.http"
+local cjson = require "cjson"
+local match = string.match
+local ipairs = ipairs
+local insert = table.insert
+local assert = assert
+
+local http_mock = {}
+
+-- POST as it's not idempotent
+local retrieve_mocking_logs_param = {
+ method = "POST",
+ path = "/logs",
+ headers = {
+ ["Host"] = "mock_debug"
+ }
+}
+
+local purge_mocking_logs_param = {
+ method = "DELETE",
+ path = "/logs",
+ headers = {
+ ["Host"] = "mock_debug"
+ }
+}
+
+local get_status_param = {
+ method = "GET",
+ path = "/status",
+ headers = {
+ ["Host"] = "mock_debug"
+ }
+}
+
+-- internal API
+function http_mock:_setup_debug(debug_param)
+ local debug_port = helpers.get_available_port()
+ local debug_client = assert(http.new())
+ local debug_connect = {
+ scheme = "http",
+ host = "localhost",
+ port = debug_port,
+ }
+
+ self.debug = {
+ port = debug_port,
+ client = debug_client,
+ connect = debug_connect,
+ param = debug_param,
+ }
+end
+
+function http_mock:debug_connect()
+ local debug = self.debug
+ local client = debug.client
+ assert(client:connect(debug.connect))
+ return client
+end
+
+function http_mock:retrieve_mocking_logs_json()
+ local debug = self:debug_connect()
+ local res = assert(debug:request(retrieve_mocking_logs_param))
+ assert(res.status == 200)
+ local body = assert(res:read_body())
+ debug:close()
+ return body
+end
+
+function http_mock:purge_mocking_logs()
+ local debug = self:debug_connect()
+ local res = assert(debug:request(purge_mocking_logs_param))
+ assert(res.status == 204)
+ debug:close()
+ return true
+end
+
+function http_mock:retrieve_mocking_logs()
+ local new_logs = cjson.decode(self:retrieve_mocking_logs_json())
+ for _, log in ipairs(new_logs) do
+ insert(self.logs, log)
+ end
+
+ return new_logs
+end
+
+function http_mock:wait_until_no_request(timeout)
+ local debug = self:debug_connect()
+
+ -- wait until we have no requests on going
+ helpers.wait_until(function()
+ local res = assert(debug:request(get_status_param))
+ assert(res.status == 200)
+ local body = assert(res:read_body())
+ local reading, writing, _ = match(body, "Reading: (%d+) Writing: (%d+) Waiting: (%d+)")
+ -- the status is the only request
+ return assert(reading) + assert(writing) <= 1
+ end, timeout)
+end
+
+function http_mock:get_all_logs(timeout)
+ self:wait_until_no_request(timeout)
+ self:retrieve_mocking_logs()
+ return self.logs
+end
+
+function http_mock:clean(timeout)
+ -- if we wait, the http_client may timeout and cause error
+ -- self:wait_until_no_request(timeout)
+
+ -- clean unwanted logs
+ self.logs = {}
+ self:purge_mocking_logs()
+ return true
+end
+
+return http_mock
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/nginx_instance.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/nginx_instance.lua
new file mode 100644
index 00000000..1a1cecc5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/nginx_instance.lua
@@ -0,0 +1,102 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--- part of http_mock
+-- @submodule spec.helpers.http_mock
+
+local template_str = require "spec.helpers.http_mock.template"
+local pl_template = require "pl.template"
+local pl_path = require "pl.path"
+local pl_dir = require "pl.dir"
+local pl_file = require "pl.file"
+local pl_utils = require "pl.utils"
+local shell = require "resty.shell"
+
+local print = print
+local error = error
+local assert = assert
+local ngx = ngx
+local io = io
+
+local shallow_copy
+do
+ local clone = require "table.clone"
+
+ shallow_copy = function(orig)
+ assert(type(orig) == "table")
+ return clone(orig)
+ end
+end
+
+local template = assert(pl_template.compile(template_str))
+local render_env = {ipairs = ipairs, pairs = pairs, error = error, }
+local http_mock = {}
+
+--- start a dedicate nginx instance for this mock
+-- @tparam[opt=false] bool error_on_exist whether to throw error if the directory already exists
+-- @within http_mock
+-- @usage http_mock:start(true)
+function http_mock:start(error_on_exist)
+ local ok = (pl_path.mkdir(self.prefix))
+ and (pl_path.mkdir(self.prefix .. "/logs"))
+ and (pl_path.mkdir(self.prefix .. "/conf"))
+ if error_on_exist then assert(ok, "failed to create directory " .. self.prefix) end
+
+ local render = assert(template:render(shallow_copy(self), render_env))
+ local conf_path = self.prefix .. "/conf/nginx.conf"
+ local conf_file = assert(io.open(conf_path, "w"))
+ assert(conf_file:write(render))
+ assert(conf_file:close())
+
+ local cmd = "nginx -p " .. self.prefix
+ local ok, code, _, stderr = pl_utils.executeex(cmd)
+ assert(ok and code == 0, "failed to start nginx: " .. stderr)
+ return true
+end
+
+local sleep_step = 0.01
+
+--- stop a dedicate nginx instance for this mock
+-- @function http_mock:stop
+-- @tparam[opt=false] bool no_clean whether to preserve the logs
+-- @tparam[opt="TERM"] string signal the signal name to send to the nginx process
+-- @tparam[opt=10] number timeout the timeout to wait for the nginx process to exit
+-- @within http_mock
+-- @usage http_mock:stop(false, "TERM", 10)
+function http_mock:stop(no_clean, signal, timeout)
+ signal = signal or "TERM"
+ timeout = timeout or 10
+ local pid_filename = self.prefix .. "/logs/nginx.pid"
+ local pid_file = assert(io.open(pid_filename, "r"))
+ local pid = assert(pid_file:read("*a"))
+ pid_file:close()
+
+ local kill_nginx_cmd = "kill -s " .. signal .. " " .. pid
+ if not shell.run(kill_nginx_cmd, nil, 0) then
+ error("failed to kill nginx at " .. self.prefix, 2)
+ end
+
+ local time = 0
+ while pl_file.access_time(pid_filename) ~= nil do
+ ngx.sleep(sleep_step)
+ time = time + sleep_step
+ if(time > timeout) then
+ error("nginx does not exit at " .. self.prefix, 2)
+ end
+ end
+
+ if no_clean then return true end
+
+ local _, err = pl_dir.rmtree(self.prefix)
+ if err then
+ print("could not remove ", self.prefix, ": ", tostring(err))
+ end
+
+ return true
+end
+
+return http_mock
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/tapping.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/tapping.lua
new file mode 100644
index 00000000..7db07e46
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/tapping.lua
@@ -0,0 +1,54 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+--- A http_mock subclass for tapping.
+-- @module spec.helpers.http_mock.tapping
+
+local http_mock = require "spec.helpers.http_mock"
+
+local tapping = {}
+
+-- create a new tapping route
+-- @tparam string|number target the target host/port of the tapping route
+-- @return the tapping route instance
+function tapping.new_tapping_route(target)
+ if tonumber(target) then
+ -- TODO: handle the resovler!
+ target = "http://127.0.0.1:" .. target
+ end
+
+ if not target:find("://") then
+ target = "http://" .. target
+ end
+
+ return {
+ ["/"] = {
+ directives = [[proxy_pass ]] .. target .. [[;]],
+ }
+ }
+end
+
+--- create a new `http_mock.tapping` instance with a tapping route
+-- @tparam string|number target the target host/port of the tapping route
+-- @tparam[opt] table|string|number listens see `http_mock.new`
+-- @tparam[opt="servroot_tapping"] string prefix the prefix of the mock server
+-- @tparam[opt={}] table log_opts see `http_mock.new`, uses the defaults, with `req_large_body` enabled
+-- @treturn http_mock.tapping a tapping instance
+-- @treturn string the port the mock server listens to
+function tapping.new(target, listens, prefix, log_opts)
+ ---@diagnostic disable-next-line: return-type-mismatch
+ return http_mock.new(listens, tapping.new_tapping_route(target), {
+ prefix = prefix or "servroot_tapping",
+ log_opts = log_opts or {
+ req = true,
+ req_body = true,
+ req_large_body = true,
+ },
+ })
+end
+
+return tapping
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/template.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/template.lua
new file mode 100644
index 00000000..91af44d5
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/http_mock/template.lua
@@ -0,0 +1,255 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+return [[
+# if not hostname then
+# hostname = "_"
+# end
+# if not debug.port then
+# error("debug.port is required")
+# end
+# if not shm_size then
+# shm_size = "20m"
+# end
+daemon on;
+# if not worker_num then
+# worker_num = 1
+# end
+worker_processes $(worker_num);
+error_log logs/error.log info;
+pid logs/nginx.pid;
+worker_rlimit_nofile 8192;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ lua_shared_dict mock_logs $(shm_size);
+
+# for dict, size in pairs(dicts or {}) do
+ lua_shared_dict $(dict) $(size);
+# end
+
+ init_by_lua_block {
+# if log_opts.err then
+ -- disable warning of global variable
+ local g_meta = getmetatable(_G)
+ setmetatable(_G, nil)
+
+ original_assert = assert -- luacheck: ignore
+
+ local function insert_err(err)
+ local err_t = ngx.ctx.err
+ if not err_t then
+ err_t = {}
+ ngx.ctx.err = err_t
+ end
+ table.insert(err_t, {err, debug.traceback("", 3)})
+ end
+
+ function assert(truthy, err, ...) -- luacheck: ignore
+ if not truthy and ngx.ctx then
+ insert_err(err)
+ end
+
+ return original_assert(truthy, err, ...)
+ end
+
+ original_error = error -- luacheck: ignore
+
+ function error(msg, ...) -- luacheck: ignore
+ if ngx.ctx then
+ insert_err(msg)
+ end
+
+ return original_error(msg, ...)
+ end
+
+ err_patched = true -- luacheck: ignore
+
+ setmetatable(_G, g_meta)
+# end
+# if init then
+$(init)
+# end
+ }
+
+ server {
+ listen 0.0.0.0:$(debug.port);
+ server_name mock_debug;
+
+ location = /status {
+ stub_status;
+ }
+
+ location /logs {
+ default_type application/json;
+
+ access_by_lua_block {
+ local mock_logs = ngx.shared.mock_logs
+
+ if ngx.req.get_method() == "DELETE" then
+ mock_logs:flush_all()
+ return ngx.exit(204)
+ end
+
+ if ngx.req.get_method() ~= "POST" then
+ return ngx.exit(405)
+ end
+
+ ngx.print("[")
+ local ele, err
+ repeat
+ local old_ele = ele
+ ele, err = mock_logs:lpop("mock_logs")
+ if old_ele and ele then
+ ngx.print(",", ele)
+ elseif ele then
+ ngx.print(ele)
+ end
+ if err then
+ return ngx.exit(500)
+ end
+ until not ele
+ ngx.print("]")
+ ngx.exit(200)
+ }
+ }
+ }
+
+ server {
+# for _, listen in ipairs(listens or {}) do
+ listen $(listen);
+# end
+ server_name $(hostname);
+
+# for _, directive in ipairs(directives or {}) do
+ $(directive)
+
+# end
+# if tls then
+ ssl_certificate ../../spec/fixtures/kong_spec.crt;
+ ssl_certificate_key ../../spec/fixtures/kong_spec.key;
+ ssl_protocols TLSv1.2;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+
+# end
+# for location, route in pairs(routes or {}) do
+ location $(location) {
+# if route.directives then
+ $(route.directives)
+
+# end
+# if route.access or log_opts.req then
+ access_by_lua_block {
+# if log_opts.req then
+ -- collect request
+ local method = ngx.req.get_method()
+ local uri = ngx.var.request_uri
+ local headers = ngx.req.get_headers(nil, true)
+
+
+ ngx.req.read_body()
+ local body
+# if log_opts.req_body then
+ -- collect body
+ body = ngx.req.get_body_data()
+ if not body then
+ local file = ngx.req.get_body_file()
+ if file then
+# if log_opts.req_large_body then
+ local f = io.open(file, "r")
+ if f then
+ body = f:read("*a")
+ f:close()
+ end
+# else
+ body = { "body is too large" }
+# end -- if log_opts.req_large_body
+ end
+ end
+# end -- if log_opts.req_body
+ ngx.ctx.req = {
+ method = method,
+ uri = uri,
+ headers = headers,
+ body = body,
+ }
+
+# end -- if log_opts.req
+# if route.access then
+ $(route.access)
+# end
+ }
+# end
+
+# if route.header_filter then
+ header_filter_by_lua_block {
+ $(route.header)
+ }
+
+# end
+# if route.content then
+ content_by_lua_block {
+ $(route.content)
+ }
+
+# end
+# if route.body_filter or log_opts.resp_body then
+ body_filter_by_lua_block {
+# if route.body_filter then
+ $(route.body)
+
+# end
+# if log_opts.resp_body then
+ -- collect body
+ ngx.ctx.resp_body = ngx.ctx.resp_body or {}
+ if not ngx.arg[2] then
+ table.insert(ngx.ctx.resp_body, ngx.arg[1])
+ end
+# end -- if log_opts.resp_body
+ }
+
+# end
+ log_by_lua_block {
+# if route.log then
+ $(route.log)
+
+# end
+ -- collect session data
+ local cjson = require "cjson"
+ local start_time = ngx.req.start_time()
+ local end_time = ngx.now()
+
+ local req = ngx.ctx.req or {}
+ local resp
+# if log_opts.resp then
+ resp = {
+ status = ngx.status,
+ headers = ngx.resp.get_headers(nil, true),
+ body = ngx.ctx.resp_body and table.concat(ngx.ctx.resp_body),
+ }
+# else -- if log_opts.resp
+ resp = {}
+# end -- if log_opts.resp
+ local err = ngx.ctx.err
+
+ ngx.shared.mock_logs:rpush("mock_logs", cjson.encode({
+ start_time = start_time,
+ end_time = end_time,
+ req = req,
+ resp = resp,
+ err = err,
+ }))
+ }
+ }
+# end -- for location, route in pairs(routes)
+ }
+}
+]]
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/oauth_client.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/oauth_client.lua
new file mode 100644
index 00000000..ad3ae4f1
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/oauth_client.lua
@@ -0,0 +1,493 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local utils = require("kong.tools.utils")
+local resty_http = require("resty.http")
+local cjson = require("cjson")
+local jwks = require "kong.openid-connect.jwks"
+local jwa = require("kong.openid-connect.jwa")
+local uuid = require("resty.jit-uuid")
+local base64url_encode = require("ngx.base64").encode_base64url
+local json_encode = cjson.encode
+
+
+local WELL_KNOWN_PATH = "/.well-known/openid-configuration"
+local ENDPOINT_SUFFIX = "_endpoint"
+
+
+local _M = {}
+
+
+---- utility functions
+
+local function dump_request(url, opts, is_rp)
+ print("\n==", is_rp and "RP" or "IDP", " request==")
+ print(opts.method, " ", url)
+ for k, v in pairs(opts.headers) do
+ print(k, ": ", v)
+ end
+ print()
+ print(opts.body)
+ print("===============")
+end
+
+
+local function dump_response(resp, is_rp)
+ print("\n==", is_rp and "RP" or "IDP", " response==")
+ print("HTTP ", resp.status)
+ for k, v in pairs(resp.headers) do
+ print(k, ": ", v)
+ end
+ print()
+ print(resp.body)
+ print("===============")
+end
+
+
+local function get_value_fallback(key, ...)
+ for i = 1, select("#", ...) do
+ local source = select(i, ...)
+ if source[key] then
+ return source[key]
+ end
+ end
+end
+
+
+function _M.update_cookies(self, res)
+ local set_cookie_headers = res.headers["Set-Cookie"]
+ if type(set_cookie_headers) ~= "table" then
+ set_cookie_headers = { set_cookie_headers }
+ end
+
+ for _, cookie in ipairs(set_cookie_headers) do
+ local k, v = cookie:match("^([^;=]*)=([^;=]*)")
+ self.cookies[k] = v or ""
+ end
+end
+
+
+local function cookies_to_header(cookies)
+ local cookie = {}
+ for k, v in pairs(cookies) do
+ cookie[#cookie + 1] = k .. "=" .. v
+ end
+ return table.concat(cookie, "; ")
+end
+
+
+local function append_query(url, args)
+ if url:find("?") then
+ return url .. "&" .. args
+ else
+ return url .. "?" .. args
+ end
+end
+
+
+---- end of utility functions
+
+---- methods
+
+-- to use a public client, set client_secret to false explicitly
+function _M.new(opts)
+ opts = opts or {}
+ opts.http_client = opts.http_client or resty_http.new()
+ opts.issuer = opts.issuer or "http://localhost:8080/realms/master"
+ opts.client_uri = opts.client_uri or "http://localhost"
+ opts.auth_callback_uri = opts.auth_callback_uri or "/callback/auth/"
+ opts.user_login_simulator = opts.user_login_simulator or _M.keycloak_login
+ opts.cookies = opts.cookies or {}
+
+ -- default to using end-to-end binding unless explicitly set to false
+ if opts.using_dpop and opts.auth_dpop == nil then
+ opts.auth_dpop = true
+ end
+
+ return setmetatable(opts, _M)
+end
+
+function _M.__index(self, key)
+ if _M[key] then
+ return _M[key]
+ end
+
+ if key:sub(-#ENDPOINT_SUFFIX) == ENDPOINT_SUFFIX then
+ local ret = self:get_endpoint(key)
+ self[key] = ret
+ return ret
+ end
+
+ local get_f = "get_" .. key
+ if _M[get_f] then
+ self[key] = _M[get_f](self)
+ return self[key]
+ end
+end
+
+function _M:get_idp_config()
+ local issuer = self.issuer
+ if not issuer then
+ error("no discovery_url set")
+ end
+
+ if issuer:sub(-#WELL_KNOWN_PATH) ~= WELL_KNOWN_PATH then
+ issuer = issuer .. WELL_KNOWN_PATH
+ end
+
+ local res, err = self:idp_request(issuer, {
+ method = "GET",
+ headers = {
+ ["Accept"] = "application/json",
+ },
+ })
+
+ if not res then
+ error("failed to fetch openid-configuration: " .. err)
+ end
+
+ if res.status ~= 200 then
+ error("failed to fetch openid-configuration: status " .. res.status)
+ end
+
+ return cjson.decode(res.body)
+end
+
+
+function _M:generate_dpop_proof(req, payload, alg)
+ local jwk = assert(self.client_jwk, "no client_jwk set")
+ local jwk_public = assert(self.client_jwk_public, "no client_jwk set")
+ local htm = assert(req.method, "request method required to sign DPoP header")
+ local htu = assert(req.url, "request uri required to sign DPoP header")
+ htu = htu:match("^[^?#]*") -- remove query string and fragment
+ payload = payload or {}
+
+ if (not payload.ath) and payload.access_token then
+ payload.ath = base64url_encode(jwa.S256(payload.access_token))
+ payload.access_token = nil
+ end
+
+ local header = {
+ typ = "dpop+jwt",
+ alg = alg or jwk.alg,
+ jwk = jwk_public,
+ }
+
+ for k, v in pairs(payload) do
+ payload[k] = v
+ end
+
+ payload.iat = payload.iat or ngx.now()
+ payload.jti = payload.jti or uuid.generate_v4()
+ payload.htm = payload.htm or htm
+ payload.htu = payload.htu or htu
+
+ local str_to_sign = base64url_encode(json_encode(header)) .. "." .. base64url_encode(json_encode(payload))
+
+ local jwt_token = assert(jwa.sign(header.alg, jwk, str_to_sign))
+
+ return str_to_sign .. "." .. jwt_token
+end
+
+
+function _M:get_endpoint(name)
+ local idp_config = assert(self.idp_config, "no idp_config set")
+ local endpoint
+
+ if self.using_mtls and idp_config.mtls_endpoint_aliases then
+ endpoint = idp_config.mtls_endpoint_aliases[name]
+ end
+
+ if not endpoint then
+ endpoint = idp_config[name]
+ end
+
+ return endpoint
+end
+
+
+function _M:request(url, opts, is_rp)
+ local access_token = is_rp and self:get_access_token() or nil
+ local nonce_name = is_rp and "rp_last_dpop_nonce" or "idp_last_dpop_nonce"
+ local desigated_dpop = opts.headers["DPoP"]
+ local res
+
+ -- opts.headers["Host"] = opts.headers["Host"] or url:match("^https?://([^/]+)")
+
+ -- retry if nonce is required and we are recieving one for the first time
+ local retried = false
+ ::retry::
+ if self.using_dpop then
+ opts.headers["DPoP"] = desigated_dpop or assert(self:generate_dpop_proof({
+ method = opts.method,
+ url = url,
+ }, {
+ access_token = access_token,
+ nonce = self[nonce_name], -- if exists
+ }), "failed to generate DPoP proof")
+ end
+
+ if self.dump_req then
+ dump_request(url, opts, is_rp)
+ end
+
+ res = assert(self.http_client:request_uri(url, opts))
+ self:update_cookies(res)
+
+ if res.headers["DPoP-Nonce"] then
+ self[nonce_name] = res.headers["DPoP-Nonce"]
+ end
+
+ if self.dump_req or (res.status >= 400 and self.dump_on_err) then
+ dump_response(res, is_rp)
+ end
+
+ if (res.status == 400 or res.status == 401) and res.headers["DPoP-Nonce"] and not retried then
+ if self.dump_req then
+ print("retrying with nonce")
+ end
+
+ retried = true
+ goto retry
+ end
+
+ return res
+end
+
+
+function _M:idp_request(url, opts, query_args, client_cred)
+ opts.headers = opts.headers or {}
+ local args = opts.args or {}
+
+ if client_cred == "public" or "secret" then
+ args.client_id = get_value_fallback("client_id", args, self)
+ end
+
+ if client_cred == "secret" then
+ args.client_secret = get_value_fallback("client_secret", args, self)
+ end
+
+ local cookies = get_value_fallback("cookies", opts, self)
+ if cookies then
+ opts.headers["Cookie"] = cookies_to_header(cookies)
+ end
+ opts.cookies = nil
+
+ local string_args = ngx.encode_args(args)
+
+ if opts.args and opts.method == "GET" then
+ url = append_query(url, string_args)
+ else
+ opts.headers["Content-Type"] = opts.headers["Content-Type"] or "application/x-www-form-urlencoded"
+ opts.body = opts.body or string_args
+ end
+
+ if query_args then
+ url = append_query(url, ngx.encode_args(query_args))
+ end
+
+ opts.args = nil
+
+ return self:request(url, opts)
+end
+
+
+function _M:token_request(args, query_args)
+ args = args or {}
+ local token_endpoint = assert(self.token_endpoint, "no token_endpoint found")
+ args.auth_method = get_value_fallback("auth_method", args, self) or "client_secret_basic"
+
+ local res = self:idp_request(token_endpoint, {
+ method = "POST",
+ args = args,
+ }, query_args, "secret", self.using_dpop and "header")
+
+ assert(res.status == 200, "failed to fetch token: status " .. res.status)
+
+ return res.body
+end
+
+
+function _M:get_token(args, query_args)
+ local ret = cjson.decode(self:token_request(args, query_args))
+ self.token_response = ret
+ self.token_time = ngx.now()
+ assert(ret.access_token, "no access_token found in token response")
+ return ret
+end
+
+
+function _M:refresh_token()
+ local token_response = assert(self.token_response, "no token_response found")
+ local refresh_token = assert(token_response.refresh_token, "no refresh_token found in token_response")
+
+ local refresh_expires_in = token_response.refresh_expires_in
+
+ if refresh_expires_in and ngx.now() - self.token_time > refresh_expires_in then
+ if self.auto_login then
+ return self:login()
+ else
+ error("refresh token expired")
+ end
+ end
+
+ return self:get_token({
+ grant_type = "refresh_token",
+ refresh_token = refresh_token,
+ })
+end
+
+
+function _M:authenticate_request(args, query_args)
+ args = args or {}
+ local auth_endpoint = assert(self.authorization_endpoint, "no authorization_endpoint found")
+
+ if args.client_secret == nil then
+ args.client_secret = false
+ end
+
+ if args.redirect_uri == nil then
+ args.redirect_uri = self.client_uri .. self.auth_callback_uri
+ end
+
+ args.response_type = args.response_type or "code"
+ args.code_challenge_method = self.code_challenge_method or args.code_challenge_method or "S256"
+ if args.code_challenge_method == "S256" then
+ args.code_challenge = base64url_encode(jwa.S256(args.code_challenge))
+ end
+
+ local dpop_jkt
+
+ if self.auth_dpop then
+ dpop_jkt = jwks.compute_thumbprint(assert(self.client_jwk_public))
+ end
+
+ local res = self:idp_request(auth_endpoint, {
+ method = "POST",
+ args = args,
+ dpop_jkt = dpop_jkt,
+ }, query_args, "public")
+
+ if res.status == 302 then
+ local redirect_uri = assert(res.headers["Location"], "no redirect_uri found")
+ local callback_args = redirect_uri:match(args.redirect_uri .. "%?([^?]+)")
+ callback_args = ngx.decode_args(callback_args)
+ if callback_args.error then
+ error("authenticate failed: " .. (callback_args.error_description or callback_args.error))
+ end
+
+ return res
+ end
+
+ assert(res.status == 200, "failed to request for authentication: status " .. res.status)
+
+ return res
+end
+
+
+-- auth_code flow
+function _M:login(user_login_simulator)
+ user_login_simulator = user_login_simulator or self.user_login_simulator
+
+ local code_verifier
+ if self.use_pkce then
+ code_verifier = base64url_encode(utils.random_string(64))
+ end
+
+ local res = self:authenticate_request({
+ grant_type = "authorization_code",
+ code_challenge = code_verifier
+ })
+
+ local args = assert(user_login_simulator(self, res))
+ local code = assert(args.code, "no code found")
+
+ return self:get_token({
+ grant_type = "authorization_code",
+ code = code,
+ code_verifier = code_verifier,
+ auth_method = "code",
+ redirect_uri = self.client_uri .. self.auth_callback_uri,
+ })
+end
+
+
+local function extract_args(res)
+ local redirect_uri = assert(res.headers["Location"], "no redirect_uri found")
+ return ngx.decode_args(redirect_uri:match("%?(.*)"))
+end
+
+
+function _M.keycloak_login(client, res)
+ if res.status == 302 then
+ return extract_args(res)
+ end
+
+ local login_page = res.body
+ local login_url = login_page:match('action="([^"]+)"')
+ local login_args = {
+ username = client.username,
+ password = client.password,
+ }
+
+ local login_res = client:idp_request(login_url, {
+ method = "POST",
+ args = login_args,
+ })
+
+ if login_res.status == 200 then
+ local failure_reason = login_res.body:match(']*>%s*([^<]-)%s* ')
+ if failure_reason then
+ error("login failed: " .. failure_reason)
+
+ else
+ error("login failed with unknown reason")
+ end
+ end
+
+ assert(login_res.status == 302, "failed to login: status " .. login_res.status)
+
+ return extract_args(login_res)
+end
+
+
+function _M.rp_request(self, url, opts)
+ opts = opts or {}
+ opts.headers = opts.headers or {}
+
+ local access_token = self:get_access_token()
+
+ opts.headers["Authorization"] = opts.headers["Authorization"] or self.token_response.token_type .. " " .. access_token
+ opts.headers["Cookie"] = cookies_to_header(self.cookies)
+ opts.method = opts.method or "GET"
+
+ if not url:match("^https?://") then
+ url = self.client_uri .. url
+ end
+
+ return self:request(url, opts, true)
+end
+
+
+function _M.get_access_token(self)
+ if not self.token_response then
+ if self.auto_login then
+ self:login()
+ else
+ error("no token")
+ end
+ end
+
+ if ngx.now() - self.token_time > self.token_response.expires_in then
+ self:refresh_token()
+ end
+
+ return assert(self.token_response.access_token)
+end
+
+
+return _M
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/perf.lua
new file mode 100644
index 00000000..46de48be
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf.lua
@@ -0,0 +1,581 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+--- Module with performance test tools
+-- @module spec.helpers.perf
+
+local pl_tablex = require("pl.tablex")
+
+local logger = require("spec.helpers.perf.logger")
+local utils = require("spec.helpers.perf.utils")
+local git = require("spec.helpers.perf.git")
+local charts = require("spec.helpers.perf.charts")
+local read_all_env = require("kong.cmd.utils.env").read_all
+
+local my_logger = logger.new_logger("[controller]")
+
+utils.register_busted_hook()
+utils.add_lua_package_paths()
+
+charts.register_busted_hook()
+
+-- how many times for each "driver" operation
+local RETRY_COUNT = 3
+local DRIVER
+local DRIVER_NAME
+local LAST_KONG_VERSION
+
+-- Real user facing functions
+local driver_functions = {
+ "start_worker", "start_kong", "stop_kong", "setup", "setup_kong", "teardown",
+ "get_start_load_cmd", "get_start_stapxx_cmd", "get_wait_stapxx_cmd",
+ "generate_flamegraph", "save_error_log", "get_admin_uri",
+ "save_pgdump", "load_pgdump", "get_based_version", "remote_execute",
+}
+
+local function check_driver_sanity(mod)
+ if type(mod) ~= "table" then
+ error("Driver must return a table")
+ end
+
+ for _, func in ipairs(driver_functions) do
+ if not mod[func] then
+ error("Driver " .. debug.getinfo(mod.new, "S").source ..
+ " must implement function " .. func, 2)
+ end
+ end
+end
+
+local known_drivers = { "docker", "terraform" }
+
+--- Set the driver to use.
+-- @function use_driver
+-- @tparam string name name of the driver to use
+-- @tparam[opt] table opts config parameters passed to the driver
+-- @return nothing. Throws an error if any.
+local function use_driver(name, opts)
+ name = name or "docker"
+
+ if not pl_tablex.find(known_drivers, name) then
+ local err = ("Unknown perf test driver \"%s\", expect one of \"%s\""):format(
+ name, table.concat(known_drivers, "\", \"")
+ )
+ error(err, 2)
+ end
+
+ local pok, mod = pcall(require, "spec.helpers.perf.drivers." .. name)
+
+ if not pok then
+ error(("Unable to load perf test driver %s: %s"):format(name, mod))
+ end
+
+ check_driver_sanity(mod)
+
+ DRIVER = mod.new(opts)
+ DRIVER_NAME = name
+end
+
+--- Set driver operation retry count
+-- @function set_retry_count
+-- @tparam number try the number of retries for each driver operation
+-- @return nothing.
+local function set_retry_count(try)
+ if type(try) ~= "number" then
+ error("expect a number, got " .. type(try))
+ end
+ RETRY_COUNT = try
+end
+
+--- Setup a default perf test instance.
+-- Creates an instance that's ready to use on most common cases including Github Actions
+-- @function use_defaults
+-- @return nothing.
+local function use_defaults()
+ logger.set_log_level(ngx.DEBUG)
+ set_retry_count(3)
+
+ local driver = os.getenv("PERF_TEST_DRIVER") or "docker"
+ local use_daily_image = os.getenv("PERF_TEST_USE_DAILY_IMAGE")
+ local ssh_user
+
+ if driver == "terraform" then
+ local seperate_db_node = not not os.getenv("PERF_TEST_SEPERATE_DB_NODE")
+
+ local tf_provider = os.getenv("PERF_TEST_TERRAFORM_PROVIDER") or "equinix-metal"
+ local tfvars = {}
+ if tf_provider == "equinix-metal" then
+ tfvars = {
+ -- Kong Benchmarking
+ metal_project_id = os.getenv("PERF_TEST_METAL_PROJECT_ID"),
+ -- TODO: use an org token
+ metal_auth_token = os.getenv("PERF_TEST_METAL_AUTH_TOKEN"),
+ metal_plan = os.getenv("PERF_TEST_METAL_PLAN"), -- "c3.small.x86"
+ -- metal_region = ["sv15", "sv16", "la4"], -- not support setting from lua for now
+ metal_os = os.getenv("PERF_TEST_METAL_OS"), -- "ubuntu_20_04",
+ }
+ elseif tf_provider == "digitalocean" then
+ tfvars = {
+ do_project_name = os.getenv("PERF_TEST_DIGITALOCEAN_PROJECT_NAME"), -- "Benchmark",
+ do_token = os.getenv("PERF_TEST_DIGITALOCEAN_TOKEN"),
+ do_size = os.getenv("PERF_TEST_DIGITALOCEAN_SIZE"), -- "c2-8vpcu-16gb",
+ do_region = os.getenv("PERF_TEST_DIGITALOCEAN_REGION"), --"sfo3",
+ do_os = os.getenv("PERF_TEST_DIGITALOCEAN_OS"), -- "ubuntu-20-04-x64",
+ }
+ elseif tf_provider == "aws-ec2" then
+ tfvars = {
+ aws_region = os.getenv("PERF_TEST_AWS_REGION"), -- "us-east-2",
+ ec2_instance_type = os.getenv("PERF_TEST_EC2_INSTANCE_TYPE"), -- "c5a.2xlarge",
+ ec2_os = os.getenv("PERF_TEST_EC2_OS"), -- "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*",
+ }
+ ssh_user = "ubuntu"
+ elseif tf_provider == "bring-your-own" then
+ tfvars = {
+ kong_ip = os.getenv("PERF_TEST_BYO_KONG_IP"),
+ kong_internal_ip = os.getenv("PERF_TEST_BYO_KONG_INTERNAL_IP"), -- fallback to kong_ip
+ db_ip = os.getenv("PERF_TEST_BYO_DB_IP"),
+ db_internal_ip = os.getenv("PERF_TEST_BYO_DB_INTERNAL_IP"), -- fallback to db_ip
+ worker_ip = os.getenv("PERF_TEST_BYO_WORKER_IP"),
+ worker_internal_ip = os.getenv("PERF_TEST_BYO_WORKER_INTERNAL_IP"), -- fallback to worker_ip
+ ssh_key_path = os.getenv("PERF_TEST_BYO_SSH_KEY_PATH") or "root",
+ }
+ ssh_user = os.getenv("PERF_TEST_BYO_SSH_USER")
+ end
+
+ tfvars.seperate_db_node = seperate_db_node
+
+ use_driver("terraform", {
+ provider = tf_provider,
+ tfvars = tfvars,
+ use_daily_image = use_daily_image,
+ seperate_db_node = seperate_db_node,
+ ssh_user = ssh_user,
+ })
+ else
+ use_driver(driver, {
+ use_daily_image = use_daily_image,
+ })
+ end
+end
+
+local function invoke_driver(method, ...)
+ if not DRIVER then
+ error("No driver selected, call use_driver first", 2)
+ end
+
+ if not DRIVER[method] then
+ my_logger.warn(method, " not implemented by driver ", DRIVER_NAME)
+ return
+ end
+
+ local happy
+ local r, err
+ for i = 1, RETRY_COUNT + 1 do
+ r, err = DRIVER[method](DRIVER, ...)
+ if not err then
+ happy = true
+ break
+ end
+
+ my_logger.warn("failed in ", method, ": ", err or "nil", ", tries: ", i)
+ end
+
+ if not happy then
+ error(method .. " finally failed" .. (RETRY_COUNT > 0 and " after " .. RETRY_COUNT .. " retries" or ""), 2)
+ end
+
+ return r
+end
+
+local _M = {
+ use_driver = use_driver,
+ set_retry_count = set_retry_count,
+ use_defaults = use_defaults,
+
+ new_logger = logger.new_logger,
+ set_log_level = logger.set_log_level,
+
+ setenv = utils.setenv,
+ unsetenv = utils.unsetenv,
+ execute = utils.execute,
+ wait_output = utils.wait_output,
+ parse_docker_image_labels = utils.parse_docker_image_labels,
+ clear_loaded_package = utils.clear_loaded_package,
+
+ git_checkout = git.git_checkout,
+ git_restore = git.git_restore,
+ get_kong_version = git.get_kong_version,
+}
+
+--- Start the worker (nginx) with given conf with multiple ports.
+-- @tparam string conf the Nginx snippet under server{} context
+-- @tparam[opt=1] number port_count number of ports the upstream listens to
+-- @return upstream_uri string or table if `port_count` is more than 1
+function _M.start_worker(conf, port_count)
+ port_count = port_count or 1
+ local ret = invoke_driver("start_worker", conf, port_count)
+ return port_count == 1 and ret[1] or ret
+end
+
+--- Start Kong with given version and conf.
+-- @tparam[opt] table kong_confs Kong configuration as a lua table
+-- @tparam[opt] table driver_confs driver configuration as a lua table
+-- @return nothing. Throws an error if any.
+function _M.start_kong(kong_confs, driver_confs)
+ kong_confs = kong_confs or {}
+ for k, v in pairs(read_all_env()) do
+ k = k:match("^KONG_([^=]+)")
+ k = k and k:lower()
+ if k then
+ kong_confs[k] = os.getenv("KONG_" .. k:upper())
+ end
+ end
+ return invoke_driver("start_kong", kong_confs, driver_confs or {})
+end
+
+--- Stop Kong.
+-- @param ... args passed to the driver "stop_kong" command
+-- @return nothing. Throws an error if any.
+function _M.stop_kong(...)
+ return invoke_driver("stop_kong", ...)
+end
+
+--- Setup environment.
+-- This is not necessary if `setup_kong` is called
+-- @return nothing. Throws an error if any.
+function _M.setup()
+ return invoke_driver("setup")
+end
+
+--- Installs Kong. Setup env vars and return the configured helpers utility
+-- @tparam string version Kong version
+-- @return table the `helpers` utility as if it's require("spec.helpers")
+function _M.setup_kong(version)
+ LAST_KONG_VERSION = version
+ return invoke_driver("setup_kong", version)
+end
+
+--- Cleanup the test.
+-- @tparam[opt=false] boolean full teardown all stuff, including those will make next test spin up faster
+-- @return nothing. Throws an error if any.
+function _M.teardown(full)
+ LAST_KONG_VERSION = nil
+ return invoke_driver("teardown", full)
+end
+
+local load_thread
+local load_should_stop
+
+--- Start to send load to Kong.
+-- @tparam table opts options table
+-- @tparam[opt="/"] string opts.path request path
+-- @tparam[opt="http://kong-ip:kong-port/"] string opts.uri base URI except path
+-- @tparam[opt=1000] number opts.connections connection count
+-- @tparam[opt=5] number opts.threads request thread count
+-- @tparam[opt=10] number opts.duration perf test duration in seconds
+-- @tparam[opt] string opts.script content of wrk script
+-- @tparam[opt] string opts.kong_name specify the kong name to send load to; will automatically pick one if not specified
+-- @return nothing. Throws an error if any.
+function _M.start_load(opts)
+ if load_thread then
+ error("load is already started, stop it using wait_result() first", 2)
+ end
+
+ local path = opts.path or ""
+ -- strip leading /
+ if path:sub(1, 1) == "/" then
+ path = path:sub(2)
+ end
+
+ local prog = opts.wrk2 and "wrk2" or "wrk"
+ if opts.wrk2 then
+ if DRIVER_NAME ~= "terraform" then
+ error("wrk2 not supported in docker driver", 2)
+ elseif not opts.rate then
+ error("wrk2 requires rate", 2)
+ end
+ end
+
+ local load_cmd_stub = prog .. " -c " .. (opts.connections or 1000) ..
+ " -t " .. (opts.threads or 5) ..
+ " -d " .. (opts.duration or 10) .. "s" ..
+ (opts.wrk2 and " -R " .. opts.rate or "") ..
+ " %s " .. -- script place holder
+ " %s/" .. path ..
+ " --latency"
+
+ local load_cmd = invoke_driver("get_start_load_cmd", load_cmd_stub, opts.script, opts.uri, opts.kong_name)
+ load_should_stop = false
+
+ load_thread = ngx.thread.spawn(function()
+ return utils.execute(load_cmd,
+ {
+ stop_signal = function() if load_should_stop then return 9 end end,
+ })
+ end)
+end
+
+local stapxx_thread
+local stapxx_should_stop
+
+--- Start to send load to Kong.
+-- @tparam string sample_name stapxx sample name
+-- @tparam string arg extra arguments passed to stapxx script
+-- @tparam table driver_confs driver configuration as a lua table
+-- @return nothing. Throws an error if any.
+function _M.start_stapxx(sample_name, arg, driver_confs)
+ if stapxx_thread then
+ error("stapxx is already started, stop it using wait_result() first", 2)
+ end
+
+ local start_cmd = invoke_driver("get_start_stapxx_cmd", sample_name, arg, driver_confs or {})
+ stapxx_should_stop = false
+
+ stapxx_thread = ngx.thread.spawn(function()
+ return utils.execute(start_cmd,
+ {
+ stop_signal = function() if stapxx_should_stop then return 3 end end,
+ })
+ end)
+
+ local wait_cmd = invoke_driver("get_wait_stapxx_cmd")
+ if not utils.wait_output(wait_cmd, "stap_", 30) then
+ return false, "timeout waiting systemtap probe to load"
+ end
+
+ return true
+end
+
+--- Wait for the load test to finish.
+-- @treturn string the test report text
+function _M.wait_result()
+ if not load_thread then
+ error("load haven't been started or already collected, " ..
+ "start it using start_load() first", 2)
+ end
+
+ -- local timeout = opts and opts.timeout or 3
+ -- local ok, res, err
+
+ -- ngx.update_time()
+ -- local s = ngx.now()
+ -- while not found and ngx.now() - s <= timeout do
+ -- ngx.update_time()
+ -- ngx.sleep(0.1)
+ -- if coroutine.status(self.load_thread) ~= "running" then
+ -- break
+ -- end
+ -- end
+ -- print(coroutine.status(self.load_thread), coroutine.running(self.load_thread))
+
+ -- if coroutine.status(self.load_thread) == "running" then
+ -- self.load_should_stop = true
+ -- return false, "timeout waiting for load to stop (" .. timeout .. "s)"
+ -- end
+
+ if stapxx_thread then
+ local ok, res, err = ngx.thread.wait(stapxx_thread)
+ stapxx_should_stop = true
+ stapxx_thread = nil
+ if not ok or err then
+ my_logger.warn("failed to wait stapxx to finish: ",
+ (res or "nil"),
+ " err: " .. (err or "nil"))
+ end
+ my_logger.debug("stap++ output: ", res)
+ end
+
+ local ok, res, err = ngx.thread.wait(load_thread)
+ load_should_stop = true
+ load_thread = nil
+
+ if not ok or err then
+ error("failed to wait result: " .. (res or "nil") ..
+ " err: " .. (err or "nil"))
+ end
+
+ return res
+end
+
+local function sum(t)
+ local s = 0
+ for _, i in ipairs(t) do
+ if type(i) == "number" then
+ s = s + i
+ end
+ end
+
+ return s
+end
+
+-- Note: could also use custom lua code in wrk
+local nan = 0/0
+local function parse_wrk_result(r)
+ local rps = string.match(r, "Requests/sec:%s+([%d%.]+)")
+ rps = tonumber(rps)
+ local count = string.match(r, "([%d]+)%s+requests in")
+ count = tonumber(count)
+
+ local lat_avg, avg_m, lat_max, max_m = string.match(r, "Latency%s+([%d%.]+)([mu]?)s%s+[%d%.]+[mu]?s%s+([%d%.]+)([mu]?)s")
+ lat_avg = tonumber(lat_avg or nan) * (avg_m == "u" and 0.001 or (avg_m == "m" and 1 or 1000))
+ lat_max = tonumber(lat_max or nan) * (max_m == "u" and 0.001 or (max_m == "m" and 1 or 1000))
+
+ local p90, p90_m = string.match(r, "90%%%s+([%d%.]+)([mu]?)s")
+ local p99, p99_m = string.match(r, "99%%%s+([%d%.]+)([mu]?)s")
+ p90 = tonumber(p90 or nan) * (p90_m == "u" and 0.001 or (p90_m == "m" and 1 or 1000))
+ p99 = tonumber(p99 or nan) * (p99_m == "u" and 0.001 or (p99_m == "m" and 1 or 1000))
+
+ return rps, count, lat_avg, lat_max, p90, p99
+end
+
+--- Compute average of RPS and latency from multiple wrk output.
+-- @tparam table results the table holds raw wrk outputs
+-- @tparam string suite xaxis suite name
+-- @treturn string The human readable result of average RPS and latency
+function _M.combine_results(results, suite)
+ local count = #results
+ if count == 0 then
+ return "(no results)"
+ end
+
+ local rpss = table.new(count, 0)
+ local latencies_avg = table.new(count, 0)
+ local latencies_max = table.new(count, 0)
+ local latencies_p90 = table.new(count, 0)
+ local latencies_p99 = table.new(count, 0)
+ local count = 0
+
+ for i, result in ipairs(results) do
+ local r, c, la, lm, p90, p99 = parse_wrk_result(result)
+ rpss[i] = r
+ count = count + c
+ latencies_avg[i] = la * c
+ latencies_max[i] = lm
+ latencies_p90[i] = p90
+ latencies_p99[i] = p99
+ end
+
+ local rps = sum(rpss) / #results
+ local latency_avg = sum(latencies_avg) / count
+ local latency_max = math.max(unpack(latencies_max))
+
+ if LAST_KONG_VERSION then
+ charts.ingest_combined_results(LAST_KONG_VERSION, {
+ rpss = rpss,
+ rps = rps,
+ latencies_p90 = latencies_p90,
+ latencies_p99 = latencies_p99,
+ latency_max = latency_max,
+ latency_avg = latency_avg,
+ }, suite)
+ end
+
+ return ([[
+RPS Avg: %3.2f
+Latency Avg: %3.2fms Max: %3.2fms
+ P90 (ms): %s
+ P99 (ms): %s
+ ]]):format(rps, latency_avg, latency_max, table.concat(latencies_p90, ", "), table.concat(latencies_p99, ", "))
+end
+
+--- Wait until the systemtap probe is loaded.
+-- @tparam[opt=20] number timeout in seconds
+function _M.wait_stap_probe(timeout)
+ return invoke_driver("wait_stap_probe", timeout or 20)
+end
+
+--- Generate the flamegraph and return SVG.
+-- @tparam string filename the target filename to store the generated SVG.
+-- @tparam[opt] string title the title for flamegraph
+-- @tparam[opt] string opts the command line options string (not a table) for `flamegraph.pl`
+-- @return Nothing. Throws an error if any.
+function _M.generate_flamegraph(filename, title, opts)
+ if not filename then
+ error("filename must be specified for generate_flamegraph")
+ end
+ if string.sub(filename, #filename-3, #filename):lower() ~= ".svg" then
+ filename = filename .. ".svg"
+ end
+
+ if not title then
+ title = "Flame graph"
+ end
+
+ -- If current test is git-based, also attach the Kong binary package
+ -- version it based on
+ if git.is_git_repo() and git.is_git_based() then
+ -- use driver to get the version; driver could implement version override
+ -- based on setups (like using the daily image)
+ local v = invoke_driver("get_based_version")
+ title = title .. " (based on " .. v .. ")"
+ end
+
+ local out = invoke_driver("generate_flamegraph", title, opts)
+
+ local f, err = io.open(filename, "w")
+ if not f then
+ error("failed to open " .. filename .. " for writing flamegraph: " .. err)
+ end
+
+ f:write(out)
+ f:close()
+
+ my_logger.debug("flamegraph written to ", filename)
+end
+
+--- Enable or disable charts generation.
+-- @tparam[opt=false] boolean enabled enable or not
+-- @return Nothing. Throws an error if any.
+function _M.enable_charts(enabled)
+ return enabled and charts.on() or charts.off()
+end
+
+
+--- Save Kong error log locally.
+-- @tparam string filename filename where to save the log
+-- @return Nothing. Throws an error if any.
+function _M.save_error_log(filename)
+ if not filename then
+ error("filename must be specified for save_error_log")
+ end
+
+ invoke_driver("save_error_log", filename)
+
+ my_logger.debug("Kong error log written to ", filename)
+end
+
+--- Get the Admin URI accessible from worker.
+-- @tparam[opt] string kong_name specify the kong name; will automatically pick one if not specified
+-- @return Nothing. Throws an error if any.
+function _M.get_admin_uri(kong_name)
+ return invoke_driver("get_admin_uri", kong_name)
+end
+
+--- Save a .sql file of the database.
+-- @tparam string path the .sql file path to save to
+-- @return Nothing. Throws an error if any.
+function _M.save_pgdump(path)
+ return invoke_driver("save_pgdump", path)
+end
+
+--- Load a .sql file into the database.
+-- @tparam string path the .sql file path
+-- @tparam[opt=false] boolean dont_patch_service set to true to skip update all services to upstream started by this framework
+-- @return Nothing. Throws an error if any.
+function _M.load_pgdump(path, dont_patch_service)
+ return invoke_driver("load_pgdump", path, dont_patch_service)
+end
+
+--- Execute command on remote instance.
+-- @tparam string node_type the node to exeute the command on, can be; "kong", "db", or "worker"
+-- @tparam array cmds array of commands to execute
+-- @tparam boolean continue_on_error if true, will continue on error
+function _M.remote_execute(node_type, cmds, continue_on_error)
+ return invoke_driver("remote_execute", node_type, cmds, continue_on_error)
+end
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts.lua
new file mode 100644
index 00000000..4aa2c922
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts.lua
@@ -0,0 +1,121 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local math = require "math"
+local utils = require("spec.helpers.perf.utils")
+local logger = require("spec.helpers.perf.logger")
+local cjson = require "cjson"
+local cycle_aware_deep_copy = require("kong.tools.table").cycle_aware_deep_copy
+
+local fmt = string.format
+local my_logger = logger.new_logger("[charts]")
+
+math.randomseed(ngx.now())
+
+local options
+local current_test_element
+local enabled = true
+local unsaved_results_lookup = {}
+local unsaved_results = {}
+
+local function gen_plots(results, fname, opts)
+ local shell = require "resty.shell"
+ opts = opts or options
+
+ if not results or not next(results) then
+ my_logger.warn("no result found, skipping plot generation")
+ return
+ end
+
+ shell.run("mkdir -p output", nil, 0)
+
+ local output_data = {
+ options = opts,
+ data = results,
+ }
+
+ local f = io.open(fmt("output/%s.data.json", fname), "w")
+ f:write(cjson.encode(output_data))
+ f:close()
+ my_logger.info(fmt("parsed result saved to output/%s.json", fname))
+
+ return true
+end
+
+local function on_test_start(element, parent, status, debug)
+ if not enabled then
+ return true
+ end
+
+ current_test_element = element
+end
+
+local function on_test_end(element, parent, status, debug)
+ if not enabled then
+ return true
+ end
+
+end
+
+local function on_file_end(file)
+ if not enabled then
+ return true
+ end
+
+ local results = unsaved_results
+ unsaved_results = {}
+
+ local fname = file.name:gsub("[:/]", "#"):gsub("[ ,]", "_"):gsub("__", "_")
+ return gen_plots(results, fname, options)
+end
+
+local function ingest_combined_results(ver, results, suite_name)
+ if not suite_name then
+ local desc = utils.get_test_descriptor(false, current_test_element)
+ -- escape lua patterns
+ local pattern = ver:gsub([=[[%[%(%)%.%%%+%-%*%?%[%^%$%]]]=], "%%%1")
+ -- remove version and surround string from title
+ suite_name = desc:gsub("%s?"..pattern, ""):gsub(pattern.."%s?", "")
+ end
+
+ if not unsaved_results_lookup[suite_name] then
+ unsaved_results_lookup[suite_name] = {}
+
+ elseif unsaved_results_lookup[suite_name][ver] then
+ my_logger.warn(fmt("version %s for \"%s\" already has results, current result will be discarded",
+ ver, suite_name))
+ return false
+ end
+
+ local row = cycle_aware_deep_copy(results)
+ row.version = ver
+ row.suite = suite_name
+
+ -- save as ordered-array
+ table.insert(unsaved_results, row)
+
+ return true
+end
+
+local function register_busted_hook(opts)
+ local busted = require("busted")
+
+ busted.subscribe({'file', 'end' }, on_file_end)
+ busted.subscribe({'test', 'start'}, on_test_start)
+ busted.subscribe({'test', 'end'}, on_test_end)
+end
+
+return {
+ gen_plots = gen_plots,
+ register_busted_hook = register_busted_hook,
+ ingest_combined_results = ingest_combined_results,
+ on = function() enabled = true end,
+ off = function() enabled = false end,
+ options = function(opts)
+ options = opts
+ end,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/.gitignore b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/.gitignore
new file mode 100644
index 00000000..6e4266f3
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/.gitignore
@@ -0,0 +1,3 @@
+__pycache__/
+*.py[cod]
+*$py.class
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/charts.py b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/charts.py
new file mode 100644
index 00000000..3d1c2454
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/charts.py
@@ -0,0 +1,127 @@
+import argparse
+import pprint
+from pathlib import Path
+import plotly.express as px
+from plotly.subplots import make_subplots
+import pandas as pd
+import textwrap
+import json
+
+pprint = pprint.PrettyPrinter(indent=4).pprint
+
+def adjust_fig_tick_y(fig, min_y, max_y, row):
+ if max_y - min_y <= 5:
+ fig.update_yaxes(range=[min_y*0.9, max_y*1.1], row=row)
+
+def main(args: dict):
+ fname = Path(args.file).stem
+ output_dir = args.output_dir
+
+ with open(args.file) as f:
+ input_json = json.load(f)
+
+ df = pd.DataFrame(input_json["data"])
+
+ pprint(df)
+
+ df["rps_error"] = df["rpss"].apply(max) - df["rpss"].apply(min)
+ df["latency_p99_error"] = df["latencies_p99"].apply(
+ max) - df["latencies_p99"].apply(min)
+ df["latency_p90_error"] = df["latencies_p90"].apply(
+ max) - df["latencies_p90"].apply(min)
+
+ suite_sequential = "options" in input_json and \
+ "suite_sequential" in input_json["options"] and \
+ input_json["options"]["suite_sequential"]
+
+ if suite_sequential:
+ # Suite must be int if suite_sequential is True, plotly uses suites as x-axis
+ df["suite"] = df["suite"].apply(int)
+ else:
+ # Wrap long labels as suites are string types
+ df["suite"] = df["suite"].apply(
+ lambda x: " ".join(textwrap.wrap(x, width=40)))
+
+ df.sort_values(by=["version", "suite"], inplace=True)
+
+ xaxis_title = "options" in input_json and \
+ "xaxis_title" in input_json["options"] and \
+ input_json["options"]["xaxis_title"] or "Test Suites"
+
+ # RPS plot
+ fig_rps = px.bar(df, x="suite", y="rps", error_y="rps_error",
+ color="version", barmode="group", title="RPS",
+ labels={"suite": xaxis_title})
+
+ # flatten multiple values of each role into separate rows
+ df_p99 = df.explode("latencies_p99")
+ df_p90 = df.explode("latencies_p90")
+
+ # P99/90 plot
+ fig_p99 = px.box(df_p99, x="suite", y="latencies_p99", color="version",
+ points="all", title="P99 Latency", boxmode="group",
+ labels={"suite": xaxis_title, "latencies_p99": "P99 Latency (ms)"})
+ adjust_fig_tick_y(fig_p99, min(df_p99['latencies_p99']), max(df_p99['latencies_p99']), 1)
+
+ fig_p90 = px.box(df_p90, x="suite", y="latencies_p90", color="version",
+ points="all", title="P90 Latency", boxmode="group",
+ labels={"suite": xaxis_title, "latencies_p90": "P90 Latency (ms)"})
+ adjust_fig_tick_y(fig_p90, min(df_p90['latencies_p90']), max(df_p90['latencies_p90']), 1)
+
+ # Max latency
+ fig_max_latency = px.bar(df, x="suite", y="latency_max", color="version",
+ barmode="group", title="Max Latency",
+ labels={"suite": xaxis_title, "latency_max": "Max Latency (ms)"})
+
+ if suite_sequential:
+ # Ordinary Least Square Regression
+ fig_p99 = px.scatter(
+ df_p99, x="suite", y="latencies_p99", color="version", trendline="ols",
+ labels={"suite": xaxis_title, "latencies_p99": "P99 Latency (ms)"},
+ title="P99 Latency")
+ fig_p90 = px.scatter(
+ df_p90, x="suite", y="latencies_p90", color="version", trendline="ols",
+ labels={"suite": xaxis_title, "latencies_p90": "P90 Latency (ms)"},
+ title="P90 Latency")
+ fig_max_latency = px.scatter(
+ df, x="suite", y="latency_max", color="version", trendline="ols",
+ labels={"suite": xaxis_title, "latency_max": "Max Latency (ms)"},
+ title="Max Latency")
+
+ # RPS and P99 plot
+ combined = make_subplots(rows=2, cols=1, subplot_titles=[
+ fig_rps.layout.title.text, fig_p99.layout.title.text], vertical_spacing=0.12)
+ combined.add_traces(fig_rps.data)
+ combined.add_traces(fig_p99.data, rows=[
+ 2]*len(fig_p99.data), cols=[1]*len(fig_p99.data))
+ combined.update_xaxes(title_text=xaxis_title)
+
+ # Adjust y-axis ticks only if tickes are too close
+ if not suite_sequential:
+ adjust_fig_tick_y(combined, min(df_p99['latencies_p99']), max(df_p99['latencies_p99']), 2)
+
+ combined.update_yaxes(title_text="RPS")
+ combined.update_yaxes(title_text="P99 Latency (ms)", row=2)
+ combined.update_layout(title_text=fname, boxmode="group")
+ combined.write_image(
+ Path(output_dir, fname + ".combined.png"), width=1080, height=1080, scale=2)
+ combined.write_image(
+ Path(output_dir, fname + ".combined.svg"), width=1080, height=1080, scale=2)
+
+ # HTML is seperated and interactive graphs
+ with open(Path(output_dir, fname + ".plots.html"), "w") as f:
+ f.write("" + fname + " Report: ")
+ f.write(fig_rps.to_html(include_plotlyjs="cdn", full_html=False))
+ f.write(fig_p99.to_html(include_plotlyjs=False, full_html=False))
+ f.write(fig_p90.to_html(include_plotlyjs=False, full_html=False))
+ f.write(fig_max_latency.to_html(
+ include_plotlyjs=False, full_html=False))
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("file", help="path of json result file")
+ parser.add_argument("-o", "--output-dir", default="",
+ help="whether the suite is sequential")
+ args = parser.parse_args()
+
+ main(args)
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/requirements.txt b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/requirements.txt
new file mode 100644
index 00000000..1e554d3a
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/charts/requirements.txt
@@ -0,0 +1,4 @@
+pandas==1.4.3
+plotly==5.9.0
+statsmodels==0.13.2
+kaleido==0.2.1
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/drivers/docker.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/drivers/docker.lua
new file mode 100644
index 00000000..bf3d3f71
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/drivers/docker.lua
@@ -0,0 +1,629 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local nkeys = require "table.nkeys"
+local perf = require("spec.helpers.perf")
+local tools = require("kong.tools.rand")
+local helpers
+
+local _M = {}
+local mt = {__index = _M}
+
+local UPSTREAM_PORT = 18088
+local KONG_DEFAULT_HYBRID_CERT = "/etc/kong-hybrid-cert.pem"
+local KONG_DEFAULT_HYBRID_CERT_KEY = "/etc/kong-hybrid-key.pem"
+
+function _M.new(opts)
+ return setmetatable({
+ opts = opts,
+ log = perf.new_logger("[docker]"),
+ psql_ct_id = nil,
+ kong_ct_ids = {},
+ worker_ct_id = nil,
+ daily_image_desc = nil,
+ }, mt)
+end
+
+local function start_container(cid)
+ if not cid then
+ return false, "container does not exist"
+ end
+
+ local _, err = perf.execute("docker start " .. cid)
+ if err then
+ return false, "docker start:" .. err
+ end
+
+ local out, err = perf.execute("docker inspect --format='{{.State.Running}}' " .. cid)
+ if err then
+ return false, "docker inspect:" .. err
+ end
+
+ if out:gsub("\n", "") ~= "true" then
+ local out, err = perf.execute("docker logs -n5 " .. cid)
+ if err then
+ return false, "docker logs:" .. err
+ end
+ return false, out
+ end
+
+ return true
+end
+
+local function create_container(self, args, img, cmd)
+ local out, err = perf.execute("docker images --format '{{.Repository}}:{{.Tag}}' " .. img)
+ -- plain pattern find
+ if err or not out:find(img, nil, true) then
+ local _, err = perf.execute("docker pull " .. img, { logger = self.log.log_exec })
+ if err then
+ return false, err
+ end
+ end
+
+ args = args or ""
+ cmd = cmd or ""
+ out, err = perf.execute("docker create " .. args .. " " .. img .. " " .. cmd)
+ if err then
+ return false, err
+ end
+ local cid = out:match("^[a-f0-9]+$")
+ if not cid then
+ return false, "invalid container ID: " .. out
+ end
+ return cid
+end
+
+local function get_container_port(cid, ct_port)
+ local out, err = perf.execute(
+ "docker inspect " ..
+ "--format='{{range $p, $conf := .NetworkSettings.Ports}}" ..
+ "{{if eq $p \"" .. ct_port .. "\" }}{{(index $conf 0).HostPort}}{{end}}" ..
+ "{{end}}' " .. cid)
+ if err then
+ return false, "docker inspect:" .. err .. ": " .. (out or "nil")
+ end
+
+ return tonumber(out)
+end
+
+local function get_container_vip(cid)
+ local out, err = perf.execute("docker inspect --format='{{.NetworkSettings.Networks.bridge.IPAddress}}' " .. cid)
+ if err then
+ return false, "docker inspect:" .. err .. ": " .. (out or "nil")
+ end
+
+ return out
+end
+
+function _M:teardown()
+ self.setup_kong_called = false
+
+ local ct_ids = {"worker_ct_id", "psql_ct_id" }
+ for _, cid in ipairs(ct_ids) do
+ if self[cid] then
+ perf.execute("docker rm -f " .. self[cid], { logger = self.log.log_exec })
+ self[cid] = nil
+ end
+ end
+
+ for conf_id, kong_ct_id in pairs(self.kong_ct_ids) do
+ perf.execute("docker rm -f " .. kong_ct_id, { logger = self.log.log_exec })
+ self.kong_ct_ids[conf_id] = nil
+ end
+
+ perf.git_restore()
+
+ return true
+end
+
+local function prepare_spec_helpers(self, use_git, version)
+ local psql_port, err = get_container_port(self.psql_ct_id, "5432/tcp")
+ if not psql_port then
+ return false, "failed to get psql port: " .. (err or "nil")
+ end
+
+ -- wait
+ if not perf.wait_output("docker logs -f " .. self.psql_ct_id, "is ready to accept connections") then
+ return false, "timeout waiting psql to start (5s)"
+ end
+
+ self.log.info("psql is started to listen at port ", psql_port)
+ perf.setenv("KONG_PG_PORT", ""..psql_port)
+
+ ngx.sleep(3) -- TODO: less flaky
+
+ if not use_git then
+ local current_spec_helpers_version = perf.get_kong_version(true)
+ if current_spec_helpers_version ~= version then
+ self.log.info("Current spec helpers version " .. current_spec_helpers_version ..
+ " doesn't match with version to be tested " .. version .. ", checking out remote version")
+
+ version = version:match("%d+%.%d+%.%d+%.%d+") or version:match("%d+%.%d+%.%d+")
+
+ perf.git_checkout(version) -- throws
+ end
+ end
+
+ -- reload the spec.helpers module, since it may have been loaded with
+ -- a different set of env vars
+ perf.clear_loaded_package()
+
+ -- just to let spec.helpers happy, we are not going to start kong locally
+ require("kong.meta")._DEPENDENCIES.nginx = {"0.0.0.0", "9.9.9.9"}
+
+ helpers = require("spec.helpers")
+
+ package.loaded['kong.meta'] = nil
+ require("kong.meta")
+
+ perf.unsetenv("KONG_PG_PORT")
+
+ helpers.admin_client = function(timeout)
+ if nkeys(self.kong_ct_ids) < 1 then
+ error("helpers.admin_client can only be called after perf.start_kong")
+ end
+
+ -- find all kong containers with first one that exposes admin port
+ for _, kong_id in pairs(kong.ct_ids) do
+ local admin_port, err = get_container_port(kong_id, "8001/tcp")
+ if err then
+ error("failed to get kong admin port: " .. (err or "nil"))
+ end
+ if admin_port then
+ return helpers.http_client("127.0.0.1", admin_port, timeout or 60000)
+ end
+ -- not admin_port, it's fine, maybe it's a dataplane
+ end
+
+ error("failed to get kong admin port from all Kong containers")
+ end
+ return helpers
+end
+
+function _M:setup()
+ if not self.psql_ct_id then
+ local cid, err = create_container(self, "-p5432 " ..
+ "-e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_DB=kong_tests " ..
+ "-e POSTGRES_USER=kong " ..
+ "--name kong_perf_psql_$(date +%s)",
+ "postgres:13",
+ "-c max_connections=5000")
+ if err then
+ return false, "error running docker create when creating kong container: " .. err
+ end
+ self.psql_ct_id = cid
+ end
+
+ self.log.info("psql container ID is ", self.psql_ct_id)
+ local ok, err = start_container(self.psql_ct_id)
+ if not ok then
+ return false, "psql is not running: " .. err
+ end
+
+ return true
+end
+
+function _M:start_worker(conf, port_count)
+ conf = conf or [[
+ location = /test {
+ return 200;
+ }
+ ]]
+
+ local listeners = {}
+ for i=1,port_count do
+ listeners[i] = ("listen %d reuseport;"):format(UPSTREAM_PORT+i-1)
+ end
+ listeners = table.concat(listeners, "\n")
+
+ if not self.worker_ct_id then
+ local _, err = perf.execute(
+ "docker build --progress plain -t perf-test-worker -",
+ {
+ logger = self.log.log_exec,
+ stdin = ([[
+ FROM nginx:alpine
+ RUN apk update && apk add wrk
+ RUN echo -e '\
+ server {\
+ %s\
+ access_log off;\
+ location =/health { \
+ return 200; \
+ } \
+ %s \
+ }' > /etc/nginx/conf.d/perf-test.conf
+
+ # copy paste
+ ENTRYPOINT ["/docker-entrypoint.sh"]
+
+ STOPSIGNAL SIGQUIT
+
+ CMD ["nginx", "-g", "daemon off;"]
+ ]]):format(listeners:gsub("\n", "\\n"), conf:gsub("\n", "\\n"))
+ }
+ )
+ if err then
+ return false, err
+ end
+
+ local cid, err = create_container(self, "--name kong_perf_worker_$(date +%s) -p " .. UPSTREAM_PORT, "perf-test-worker")
+ if err then
+ return false, "error running docker create when creating worker: " .. err
+ end
+ self.worker_ct_id = cid
+ end
+
+ self.log.info("worker container ID is ", self.worker_ct_id)
+
+ local ok, err = start_container(self.worker_ct_id)
+ if not ok then
+ return false, "worker is not running: " .. err
+ end
+ ngx.sleep(3) -- TODO: less flaky
+
+ local worker_vip, err = get_container_vip(self.worker_ct_id)
+ if err then
+ return false, "unable to read worker container's private IP: " .. err
+ end
+
+ if not perf.wait_output("docker logs -f " .. self.worker_ct_id, " start worker process") then
+ self.log.info("worker container logs:")
+ perf.execute("docker logs " .. self.worker_ct_id, { logger = self.log.log_exec })
+ return false, "timeout waiting worker(nginx) to start (5s)"
+ end
+
+ self.log.info("worker is started")
+
+ local uris = {}
+ for i=1,port_count do
+ uris[i] = "http://" .. worker_vip .. ":" .. UPSTREAM_PORT+i-1
+ end
+ return uris
+end
+
+
+function _M:setup_kong(version)
+ local ok, err = _M.setup(self)
+ if not ok then
+ return ok, err
+ end
+
+ local git_repo_path
+
+ self.daily_image_desc = nil
+ if version:startswith("git:") then
+ git_repo_path = perf.git_checkout(version:sub(#("git:")+1))
+ version = perf.get_kong_version()
+
+ if self.opts.use_daily_image then
+ self.kong_image = "kong/kong-gateway-internal:master-ubuntu"
+ perf.execute("docker pull " .. self.kong_image, { logger = self.log.log_exec })
+ local manifest, err = perf.execute("docker inspect " .. self.kong_image)
+ if err then
+ return nil, "failed to inspect daily image: " .. err
+ end
+ local labels, err = perf.parse_docker_image_labels(manifest)
+ if err then
+ return nil, "failed to use parse daily image manifest: " .. err
+ end
+ self.log.debug("daily image " .. labels.version .." was pushed at ", labels.created)
+ self.daily_image_desc = labels.version .. ", " .. labels.created
+
+ else
+ self.kong_image = "kong:" .. version
+ end
+ self.log.debug("current git hash resolves to docker version ", version)
+
+ elseif version:match("rc") or version:match("beta") then
+ self.kong_image = "kong/kong:" .. version
+ elseif version:match("%d+%.%d+%.%d+%.%d+") then -- EE
+ if version:match("internal%-preview") then
+ self.kong_image = "kong/kong-gateway-internal:" .. version
+ else
+ self.kong_image = "kong/kong-gateway:" .. version
+ end
+ else
+ self.kong_image = "kong:" .. version
+ end
+
+ self.git_repo_path = git_repo_path
+
+ local docker_args = "--link " .. self.psql_ct_id .. ":postgres " ..
+ "-e KONG_PG_HOST=postgres " ..
+ "-e KONG_PG_DATABASE=kong_tests "
+
+ local _, err = perf.execute("docker run --rm " .. docker_args .. " " .. self.kong_image .. " kong migrations bootstrap",
+ { logger = self.log.log_exec })
+ if err then
+ return nil, "error running initial migration: " .. err
+ end
+
+ self.setup_kong_called = true
+
+ return prepare_spec_helpers(self, git_repo_path, version)
+end
+
+function _M:start_kong(kong_conf, driver_conf)
+ if not self.setup_kong_called then
+ return false, "setup_kong() must be called before start_kong()"
+ end
+
+ local kong_name = driver_conf.name
+ or 'default'
+
+ if not driver_conf.ports then
+ driver_conf.ports = { 8000 }
+ end
+
+ if self.kong_ct_ids[kong_name] == nil then
+ if not kong_conf['cluster_cert'] then
+ kong_conf['cluster_cert'] = KONG_DEFAULT_HYBRID_CERT
+ kong_conf['cluster_cert_key'] = KONG_DEFAULT_HYBRID_CERT_KEY
+ end
+
+ local docker_args = "--name kong_perf_kong_$(date +%s)_" .. kong_name .. " "
+ for k, v in pairs(kong_conf) do
+ docker_args = docker_args .. string.format("-e KONG_%s=%s ", k:upper(), v)
+ end
+ docker_args = docker_args .. "-e KONG_PROXY_ACCESS_LOG=/dev/null "
+
+ -- adds database configuration
+ if kong_conf['database'] == nil then
+ docker_args = docker_args .. "--link " .. self.psql_ct_id .. ":postgres " ..
+ "-e KONG_PG_HOST=postgres " ..
+ "-e KONG_PG_DATABASE=kong_tests "
+ end
+
+ -- link to other kong instances
+ for name, ctid in pairs(self.kong_ct_ids) do
+ docker_args = docker_args .. string.format("--link %s:%s ", ctid, name)
+ end
+
+ for _, port in ipairs(driver_conf.ports) do
+ docker_args = docker_args .. string.format("-p %d ", port)
+ end
+
+ local cid, err = create_container(self, docker_args, self.kong_image,
+ "/bin/bash -c 'kong migrations bootstrap; kong migrations up -y; kong migrations finish -y; /docker-entrypoint.sh kong docker-start'")
+
+ if err then
+ return false, "error running docker create when creating kong container: " .. err
+ end
+
+ self.kong_ct_ids[kong_name] = cid
+ perf.execute("docker cp ./spec/fixtures/kong_clustering.crt " .. cid .. ":" .. KONG_DEFAULT_HYBRID_CERT)
+ perf.execute("docker cp ./spec/fixtures/kong_clustering.key " .. cid .. ":" .. KONG_DEFAULT_HYBRID_CERT_KEY)
+
+ if self.git_repo_path then
+ perf.execute("docker exec --user=root " .. cid ..
+ " find /usr/local/share/lua/5.1/kong -name '*.ljbc' -delete; true")
+ perf.execute("docker cp " .. self.git_repo_path .. "/kong " .. cid .. ":/usr/local/share/lua/5.1/")
+ -- TODO: folllowing doesn't work
+ perf.execute("(cd " .. self.git_repo_path .. " && tar zc plugins-ee/*/kong/plugins/* --transform='s,plugins-ee/[^/]*/kong,kong,') | "
+ .. "docker exec --user=root " .. cid .. " tar zx -C /usr/local/share/lua/5.1/")
+ end
+ end
+
+ self.log.info("starting kong container \"" .. kong_name .. "\" with ID ", self.kong_ct_ids[kong_name])
+ local ok, err = start_container(self.kong_ct_ids[kong_name])
+ if not ok then
+ return false, "kong is not running: " .. err
+ end
+
+ -- wait
+ if not perf.wait_output("docker logs -f " .. self.kong_ct_ids[kong_name], " start worker process", 30) then
+ self.log.info("kong container logs:")
+ perf.execute("docker logs " .. self.kong_ct_ids[kong_name], { logger = self.log.log_exec })
+ return false, "timeout waiting kong to start (5s)"
+ end
+
+ local ports = driver_conf.ports
+ local port_maps = {}
+ for _, port in ipairs(ports) do
+ local mport, err = get_container_port(self.kong_ct_ids[kong_name], port .. "/tcp")
+ if not mport then
+ return false, "can't find exposed port " .. port .. " for kong " ..
+ self.kong_ct_ids[kong_name] .. " :" .. err
+ end
+ table.insert(port_maps, string.format("%s->%s/tcp", mport, port))
+ end
+
+ self.log.info("kong container \"" .. kong_name .. "\" is started to listen at port ", table.concat(port_maps, ", "))
+ return self.kong_ct_ids[kong_name]
+end
+
+function _M:stop_kong()
+ for conf_id, kong_ct_id in pairs(self.kong_ct_ids) do
+ local _, err = perf.execute("docker stop " .. kong_ct_id)
+ if err then
+ return false
+ end
+ end
+
+ return true
+end
+
+function _M:get_start_load_cmd(stub, script, uri, kong_name)
+ if not self.worker_ct_id then
+ return false, "worker container is not started, 'start_worker' must be called first"
+ end
+
+ local kong_id
+ if not uri then
+ if not kong_name then
+ -- find all kong containers with first one that exposes proxy port
+ for name, ct_id in pairs(self.kong_ct_ids) do
+ local admin_port, err = get_container_port(ct_id, "8000/tcp")
+ if err then
+ -- this is fine, it means this kong doesn't have a proxy port
+ self.log.debug("failed to get kong proxy port for " .. ct_id .. ": " .. (err or "nil"))
+ elseif admin_port then
+ kong_id = ct_id
+ self.log.info("automatically picked kong container \"", name, "\" with ID " .. ct_id .. " for proxy port")
+ break
+ end
+ end
+ if not kong_id then
+ return false, "failed to find kong proxy port"
+ end
+ else
+ kong_id = self.kong_ct_ids[kong_name]
+ if not kong_id then
+ return false, "kong container \"" .. kong_name .. "\" is not found"
+ end
+ end
+
+ local kong_vip, err = get_container_vip(kong_id)
+ if err then
+ return false, "unable to read kong container's private IP: " .. err
+ end
+ uri = string.format("http://%s:8000", kong_vip)
+ end
+
+ local script_path
+ if script then
+ script_path = string.format("/tmp/wrk-%s.lua", tools.random_string())
+ local out, err = perf.execute(string.format(
+ "docker exec -i %s tee %s", self.worker_ct_id, script_path),
+ {
+ stdin = script,
+ })
+ if err then
+ return false, "failed to write script in " .. self.worker_ct_id .. " container: " .. (out or err)
+ end
+ end
+
+ script_path = script_path and ("-s " .. script_path) or ""
+
+ return "docker exec " .. self.worker_ct_id .. " " ..
+ stub:format(script_path, uri)
+end
+
+function _M:get_admin_uri(kong_name)
+ local kong_id
+ if not kong_name then
+ -- find all kong containers with first one that exposes admin port
+ for name, ct_id in pairs(self.kong_ct_ids) do
+ local admin_port, err = get_container_port(ct_id, "8001/tcp")
+ if err then
+ -- this is fine, it means this kong doesn't have an admin port
+ self.log.warn("failed to get kong admin port for " .. ct_id .. ": " .. (err or "nil"))
+ elseif admin_port then
+ kong_id = ct_id
+ self.log.info("automatically picked kong container \"", name, "\" with ID " .. ct_id .. " for admin port")
+ break
+ end
+ end
+ if not kong_id then
+ return nil, "failed to find kong admin port"
+ end
+ else
+ kong_id = self.kong_ct_ids[kong_name]
+ if not kong_id then
+ return false, "kong container \"" .. kong_name .. "\" is not found"
+ end
+ end
+
+ local kong_vip, err = get_container_vip(kong_id)
+ if err then
+ return false, "unable to read kong container's private IP: " .. err
+ end
+
+ return string.format("http://%s:8001", kong_vip)
+end
+
+function _M:get_start_stapxx_cmd()
+ error("SystemTap support not yet implemented in docker driver")
+end
+
+function _M:get_wait_stapxx_cmd()
+ error("SystemTap support not yet implemented in docker driver")
+end
+
+function _M:generate_flamegraph()
+ error("SystemTap support not yet implemented in docker driver")
+end
+
+function _M:save_error_log(path)
+ for _, kong_ct_id in pairs(self.kong_ct_ids) do
+ local _, err = perf.execute("docker logs " .. kong_ct_id .. " 2>'" .. path .. "-" .. kong_ct_id .. "'",
+ { logger = self.log.log_exec })
+ if err then
+ return false, "failed to save error log for kong " .. kong_ct_id .. ": " .. err
+ end
+ end
+
+ return true
+end
+
+function _M:save_pgdump(path)
+ if not self.psql_ct_id then
+ return false, "postgres container not started"
+ end
+
+ return perf.execute("docker exec -i " .. self.psql_ct_id .. " pg_dump -Ukong kong_tests --data-only >'" .. path .. "'",
+ { logger = self.log.log_exec })
+end
+
+function _M:load_pgdump(path, dont_patch_service)
+ if not self.psql_ct_id then
+ return false, "postgres container not started"
+ end
+
+ local _, err = perf.execute("cat " .. path .. " |docker exec -i " .. self.psql_ct_id .. " psql -Ukong kong_tests",
+ { logger = self.log.log_exec })
+ if err then
+ return false, err
+ end
+
+ if dont_patch_service then
+ return true
+ end
+
+ if not self.worker_ct_id then
+ return false, "worker not started, can't patch_service; call start_worker first"
+ end
+
+ local worker_vip, err = get_container_vip(self.worker_ct_id)
+ if err then
+ return false, "unable to read worker container's private IP: " .. err
+ end
+
+ return perf.execute("echo \"UPDATE services set host='" .. worker_vip ..
+ "', port=" .. UPSTREAM_PORT ..
+ ", protocol='http';\" | docker exec -i " .. self.psql_ct_id .. " psql -Ukong kong_tests",
+ { logger = self.log.log_exec })
+end
+
+function _M:get_based_version()
+ return self.daily_image_desc or perf.get_kong_version()
+end
+
+function _M:remote_execute(node_type, cmds, continue_on_error)
+ local ct_id
+ if node_type == "kong" then
+ ct_id = self.kong_ct_ids[next(self.kong_ct_ids)]
+ elseif node_type == "worker" then
+ ct_id = self.worker_ct_id
+ elseif node_type == "db" then
+ ct_id = self.psql_ct_id
+ else
+ return false, "unknown node type: " .. node_type
+ end
+ for _, cmd in ipairs(cmds) do
+ local c = string.gsub(cmd, "'", "'\\''")
+ local out, err = perf.execute("docker exec -i " .. ct_id .. " '" .. c .. "'",
+ { logger = self.log.log_exec })
+ if err and not continue_on_error then
+ return false, "failed to execute command: " .. cmd .. ": " .. (out or err)
+ end
+ end
+ return true
+end
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/drivers/terraform.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/drivers/terraform.lua
new file mode 100644
index 00000000..44fb81a8
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/drivers/terraform.lua
@@ -0,0 +1,781 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local perf = require("spec.helpers.perf")
+local pl_path = require("pl.path")
+local cjson = require("cjson")
+local tools = require("kong.tools.rand")
+math.randomseed(os.time())
+
+local _M = {}
+local mt = {__index = _M}
+
+local UPSTREAM_PORT = 8088
+local KONG_ADMIN_PORT
+local PG_PASSWORD = tools.random_string()
+local KONG_ERROR_LOG_PATH = "/tmp/error.log"
+local KONG_DEFAULT_HYBRID_CERT = "/tmp/kong-hybrid-cert.pem"
+local KONG_DEFAULT_HYBRID_CERT_KEY = "/tmp/kong-hybrid-key.pem"
+-- threshold for load_avg / nproc, not based on specific research,
+-- just a arbitrary number to ensure test env is normalized
+local LOAD_NORMALIZED_THRESHOLD = 0.2
+
+function _M.new(opts)
+ local provider = opts and opts.provider or "equinix-metal"
+ local work_dir = "./spec/fixtures/perf/terraform/" .. provider
+ if not pl_path.exists(work_dir) then
+ error("Hosting provider " .. provider .. " unsupported: expect " .. work_dir .. " to exists", 2)
+ end
+
+ local tfvars = ""
+ if opts and opts.tfvars then
+ for k, v in pairs(opts.tfvars) do
+ tfvars = string.format("%s -var '%s=%s' ", tfvars, k, v)
+ end
+ end
+
+ local ssh_user = opts.ssh_user or "root"
+
+ return setmetatable({
+ opts = opts,
+ log = perf.new_logger("[terraform]"),
+ ssh_log = perf.new_logger("[terraform][ssh]"),
+ provider = provider,
+ work_dir = work_dir,
+ tfvars = tfvars,
+ kong_ip = nil,
+ kong_internal_ip = nil,
+ worker_ip = nil,
+ worker_internal_ip = nil,
+ systemtap_sanity_checked = false,
+ systemtap_dest_path = nil,
+ daily_image_desc = nil,
+ ssh_user = ssh_user,
+ }, mt)
+end
+
+local function ssh_execute_wrap(self, ip, cmd)
+ -- to quote a ', one need to finish the current ', quote the ' then start a new '
+ cmd = string.gsub(cmd, "'", "'\\''")
+ return "ssh " ..
+ "-o IdentityFile=" .. self.work_dir .. "/id_rsa " .. -- TODO: no hardcode
+ -- timeout is detected 3xServerAliveInterval
+ "-o TCPKeepAlive=yes -o ServerAliveInterval=10 " ..
+ -- turn on connection multiplexing
+ "-o ControlPath=" .. self.work_dir .. "/cm-%r@%h:%p " ..
+ "-o ControlMaster=auto -o ControlPersist=5m " ..
+ -- no interactive prompt for saving hostkey
+ "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " ..
+ -- silence warnings like "Permanently added xxx"
+ "-o LogLevel=ERROR " ..
+ self.ssh_user .. "@" .. ip .. " '" .. cmd .. "'"
+end
+
+-- if remote_ip is set, run remotely; else run on host machine
+local function execute_batch(self, remote_ip, cmds, continue_on_error)
+ for _, cmd in ipairs(cmds) do
+ if remote_ip then
+ cmd = ssh_execute_wrap(self, remote_ip, cmd)
+ end
+ local _, err = perf.execute(cmd, {
+ logger = (remote_ip and self.ssh_log or self.log).log_exec
+ })
+ if err then
+ if not continue_on_error then
+ return false, "failed in \"" .. cmd .. "\": ".. (err or "nil")
+ end
+ self.log.warn("execute ", cmd, " has error: ", (err or "nil"))
+ end
+ end
+ return true
+end
+
+function _M:remote_execute(node_type, cmds, continue_on_error)
+ local ip
+ if node_type == "kong" then
+ ip = self.kong_ip
+ elseif node_type == "worker" then
+ ip = self.worker_ip
+ elseif node_type == "db" then
+ ip = self.db_ip
+ else
+ return false, "unknown node type: " .. node_type
+ end
+ return execute_batch(self, ip, cmds, continue_on_error)
+end
+
+function _M:setup(opts)
+ local bin, err = perf.execute("which terraform")
+ if err or #bin == 0 then
+ return nil, "terraform binary not found"
+ end
+
+ local ok, _
+ -- terraform apply
+ self.log.info("Running terraform to provision instances...")
+
+ _, err = execute_batch(self, nil, {
+ "terraform version",
+ "cd " .. self.work_dir .. " && terraform init",
+ "cd " .. self.work_dir .. " && terraform apply -auto-approve " .. self.tfvars,
+ })
+ if err then
+ return false, err
+ end
+
+ -- grab outputs
+ local res
+ res, err = perf.execute("cd " .. self.work_dir .. " && terraform output -json")
+ if err then
+ return false, "terraform show: " .. err
+ end
+ res = cjson.decode(res)
+
+ self.kong_ip = res["kong-ip"].value
+ self.kong_internal_ip = res["kong-internal-ip"].value
+ if self.opts.seperate_db_node then
+ self.db_ip = res["db-ip"].value
+ self.db_internal_ip = res["db-internal-ip"].value
+ else
+ self.db_ip = self.kong_ip
+ self.db_internal_ip = self.kong_internal_ip
+ end
+ self.worker_ip = res["worker-ip"].value
+ self.worker_internal_ip = res["worker-internal-ip"].value
+
+ -- install psql docker on db instance
+ ok, err = execute_batch(self, self.db_ip, {
+ "sudo apt-get purge unattended-upgrades -y",
+ "sudo apt-get update -qq", "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install -y --force-yes docker.io",
+ "sudo docker rm -f kong-database || true", -- if exist remove it
+ "sudo docker volume rm $(sudo docker volume ls -qf dangling=true) || true", -- cleanup postgres volumes if any
+ "sudo docker run -d -p5432:5432 "..
+ "-e POSTGRES_PASSWORD=" .. PG_PASSWORD .. " " ..
+ "-e POSTGRES_DB=kong_tests " ..
+ "-e POSTGRES_USER=kong --name=kong-database postgres:13 -c max_connections=5000",
+ })
+ if not ok then
+ return ok, err
+ end
+
+ -- wait
+ local cmd = ssh_execute_wrap(self, self.db_ip,
+ "sudo docker logs -f kong-database")
+ if not perf.wait_output(cmd, "is ready to accept connections", 5) then
+ return false, "timeout waiting psql to start (5s)"
+ end
+
+ return true
+end
+
+function _M:teardown(full)
+ self.setup_kong_called = false
+
+ -- only run remote execute when terraform provisioned
+ if self.kong_ip then
+ local _, err = execute_batch(self, self.kong_ip, {
+ "sudo rm -rf /usr/local/kong_* /usr/local/kong || true",
+ "sudo pkill -kill nginx || true",
+ "sudo dpkg -r kong || true",
+ "sudo dpkg -r kong-enterprise-edition || true",
+ })
+ if err then
+ return false, err
+ end
+ end
+
+ if full then
+ -- terraform destroy
+ self.log.info("Running terraform to destroy instances...")
+
+ local ok, err = execute_batch(self, nil, {
+ "terraform version",
+ "cd " .. self.work_dir .. " && terraform init",
+ "cd " .. self.work_dir .. " && terraform destroy -auto-approve " .. self.tfvars,
+ })
+ if not ok then
+ return false, err
+ end
+ end
+
+ perf.git_restore()
+
+ -- otherwise do nothing
+ return true
+end
+
+function _M:start_worker(conf, port_count)
+ conf = conf or ""
+ local listeners = {}
+ for i=1,port_count do
+ listeners[i] = ("listen %d reuseport;"):format(UPSTREAM_PORT+i-1)
+ end
+ listeners = table.concat(listeners, "\n")
+
+ conf = ngx.encode_base64(([[
+ worker_processes auto;
+ worker_cpu_affinity auto;
+ error_log /var/log/nginx/error.log;
+ pid /run/nginx.pid;
+ worker_rlimit_nofile 20480;
+
+ events {
+ accept_mutex off;
+ worker_connections 10620;
+ }
+
+ http {
+ access_log off;
+ server_tokens off;
+ keepalive_requests 10000;
+ tcp_nodelay on;
+
+ server {
+ %s
+ location =/health {
+ return 200;
+ }
+ location / {
+ return 200 " performancetestperformancetestperformancetestperformancetestperformancetest";
+ }
+ %s
+ }
+ }]]):format(listeners, conf)):gsub("\n", "")
+
+ local ok, err = execute_batch(self, self.worker_ip, {
+ "sudo id",
+ "echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor || true",
+ "sudo apt-get purge unattended-upgrades -y",
+ "sudo apt-get update -qq", "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install -y --force-yes nginx gcc make unzip libssl-dev zlib1g-dev",
+ "which wrk || (rm -rf wrk && git clone https://github.com/wg/wrk -b 4.2.0 && cd wrk && make -j$(nproc) WITH_OPENSSL=/usr && sudo cp wrk /usr/local/bin/wrk)",
+ "which wrk2 || (rm -rf wrk2 && git clone https://github.com/giltene/wrk2 && cd wrk2 && make -j$(nproc) && sudo cp wrk /usr/local/bin/wrk2)",
+ "echo " .. conf .. " | base64 -d | sudo tee /etc/nginx/nginx.conf",
+ "sudo nginx -t",
+ "sudo systemctl restart nginx",
+ })
+ if not ok then
+ return nil, err
+ end
+
+ local uris = {}
+ for i=1,port_count do
+ uris[i] = "http://" .. self.worker_internal_ip .. ":" .. UPSTREAM_PORT+i-1
+ end
+ return uris
+end
+
+local function get_admin_port(self, kong_name)
+ kong_name = kong_name or "default"
+ local port, err = perf.execute(ssh_execute_wrap(self, self.kong_ip,
+ "sudo cat /etc/kong/" .. kong_name .. ".conf | grep admin_listen | cut -d ':' -f 2 | grep -oP '\\d+' || true"))
+ if port and tonumber(port) then
+ return tonumber(port)
+ else
+ self.log.warn("unable to read admin port for " .. kong_name .. ", fallback to default port " .. KONG_ADMIN_PORT .. ": " .. tostring(err))
+ return KONG_ADMIN_PORT
+ end
+end
+
+local function prepare_spec_helpers(self, use_git, version)
+ perf.setenv("KONG_PG_HOST", self.db_ip)
+ perf.setenv("KONG_PG_PASSWORD", PG_PASSWORD)
+ -- self.log.debug("(In a low voice) pg_password is " .. PG_PASSWORD)
+
+ if not use_git then
+ local current_spec_helpers_version = perf.get_kong_version(true)
+ if current_spec_helpers_version ~= version then
+ self.log.info("Current spec helpers version " .. current_spec_helpers_version ..
+ " doesn't match with version to be tested " .. version .. ", checking out remote version")
+
+ version = version:match("%d+%.%d+%.%d+%.%d+") or version:match("%d+%.%d+%.%d+")
+
+ perf.git_checkout(version) -- throws
+ end
+ end
+
+ self.log.info("Infra is up! However, preparing database remotely may take a while...")
+ for i=1, 3 do
+ perf.clear_loaded_package()
+
+ -- just to let spec.helpers happy, we are not going to start kong locally
+ require("kong.meta")._DEPENDENCIES.nginx = {"0.0.0.0", "9.9.9.9"}
+
+ local pok, pret = pcall(require, "spec.helpers")
+ package.loaded['kong.meta'] = nil
+ require("kong.meta")
+
+ if pok then
+ pret.admin_client = function(timeout)
+ return pret.http_client(self.kong_ip, get_admin_port(self), timeout or 60000)
+ end
+ perf.unsetenv("KONG_PG_HOST")
+ perf.unsetenv("KONG_PG_PASSWORD")
+
+ return pret
+ end
+ self.log.warn("unable to load spec.helpers: " .. (pret or "nil") .. ", try " .. i)
+ ngx.sleep(1)
+ end
+ error("Unable to load spec.helpers")
+end
+
+function _M:setup_kong(version)
+ local ok, err = _M.setup(self)
+ if not ok then
+ return ok, err
+ end
+
+ local git_repo_path, _
+
+ if version:startswith("git:") then
+ git_repo_path = perf.git_checkout(version:sub(#("git:")+1))
+
+ version = perf.get_kong_version()
+ self.log.debug("current git hash resolves to Kong version ", version)
+ end
+
+ local download_path
+ local download_user, download_pass = "x", "x"
+ local major_version = version:sub(1, 1)
+ if major_version == "2" or major_version == "3" then
+ local package_name = "kong-enterprise-edition_" .. version .. "_amd64.deb"
+ if major_version == "2" then
+ package_name = "kong-enterprise-edition_" .. version .. "_all.deb"
+ end
+ if version:match("%d+%.%d+%.%d+%.%d+") then -- EE
+ if version:match("internal%-preview") then
+ download_path = "https://download.konghq.com/internal/gateway-" .. major_version .. ".x-ubuntu-focal/pool/all/k/kong-enterprise-edition/"..package_name
+ download_user = os.getenv("PULP_USERNAME")
+ download_pass = os.getenv("PULP_PASSWORD")
+ if not download_user or not download_pass then
+ return nil, "PULP_USERNAME and PULP_PASSWORD are required to download internal builds"
+ end
+ else
+ download_path = "https://download.konghq.com/gateway-" .. major_version .. ".x-ubuntu-focal/pool/all/k/kong-enterprise-edition/"..package_name
+ end
+ else
+ download_path = "https://download.konghq.com/gateway-" .. major_version .. ".x-ubuntu-focal/pool/all/k/kong/kong_" ..
+ version .. "_amd64.deb"
+ end
+ else
+ error("Unknown download location for Kong version " .. version)
+ end
+
+ local docker_extract_cmds
+ self.daily_image_desc = nil
+ -- daily image are only used when testing with git
+ -- testing upon release artifact won't apply daily image files
+ local daily_image = "kong/kong-gateway-internal:master-ubuntu"
+ if self.opts.use_daily_image and git_repo_path then
+ -- install docker on kong instance
+ local _, err = execute_batch(self, self.kong_ip, {
+ "sudo apt-get update -qq",
+ "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install -y --force-yes docker.io",
+ "sudo docker version",
+ })
+ if err then
+ return false, err
+ end
+
+ docker_extract_cmds = {
+ "sudo docker login -u " .. (os.getenv("DOCKER_USERNAME") or "x") ..
+ " -p " .. (os.getenv("DOCKER_PASSWORD") or "x"),
+ "sudo docker rm -f daily || true",
+ "sudo docker rmi -f " .. daily_image,
+ "sudo docker pull " .. daily_image,
+ "sudo docker create --name daily " .. daily_image,
+ "sudo rm -rf /tmp/lua && sudo docker cp daily:/usr/local/share/lua/5.1/. /tmp/lua",
+ -- don't overwrite kong source code, use them from current git repo instead
+ "sudo rm -rf /tmp/lua/kong && sudo cp -r /tmp/lua/. /usr/local/share/lua/5.1/",
+ }
+
+ for _, dir in ipairs({"/usr/local/openresty",
+ "/usr/local/kong/include", "/usr/local/kong/lib"}) do
+ -- notice the /. it makes sure the content not the directory itself is copied
+ table.insert(docker_extract_cmds, "sudo docker cp daily:" .. dir .."/. " .. dir)
+ end
+
+ table.insert(docker_extract_cmds, "sudo rm -rf /tmp/lua && sudo docker cp daily:/usr/local/share/lua/5.1/. /tmp/lua")
+ table.insert(docker_extract_cmds, "sudo rm -rf /tmp/lua/kong && sudo cp -r /tmp/lua/. /usr/local/share/lua/5.1/")
+ end
+
+ local ok, err = execute_batch(self, self.kong_ip, {
+ "sudo apt-get purge unattended-upgrades -y",
+ "sudo apt-get update -qq",
+ "echo | sudo tee " .. KONG_ERROR_LOG_PATH, -- clear it
+ "sudo id",
+ -- set cpu scheduler to performance, it should lock cpufreq to static freq
+ "echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor || true",
+ -- increase outgoing port range to avoid 99: Cannot assign requested address
+ "sudo sysctl net.ipv4.ip_local_port_range='10240 65535'",
+ -- stop and remove kong if installed
+ "dpkg -l kong && (sudo pkill -kill nginx; sudo dpkg -r kong) || true",
+ -- stop and remove kong-ee if installed
+ "dpkg -l kong-enterprise-edition && (sudo pkill -kill nginx; sudo dpkg -r kong-enterprise-edition) || true",
+ -- have to do the pkill sometimes, because kong stop allow the process to linger for a while
+ "sudo pkill -F /usr/local/kong/pids/nginx.pid || true",
+ -- remove all lua files, not only those installed by package
+ "sudo rm -rf /usr/local/share/lua/5.1/kong",
+ "dpkg -I kong-" .. version .. ".deb || " .. -- check if already downloaded and valid because pulp flaky
+ "wget -nv " .. download_path ..
+ " --user " .. download_user .. " --password " .. download_pass .. " -O kong-" .. version .. ".deb",
+ "sudo dpkg -i kong-" .. version .. ".deb || sudo apt-get -f -y install",
+ -- generate hybrid cert
+ "kong hybrid gen_cert " .. KONG_DEFAULT_HYBRID_CERT .. " " .. KONG_DEFAULT_HYBRID_CERT_KEY .. " || true",
+ })
+ if not ok then
+ return false, err
+ end
+
+ if docker_extract_cmds then
+ _, err = execute_batch(self, self.kong_ip, docker_extract_cmds)
+ if err then
+ return false, "error extracting docker daily image:" .. err
+ end
+ local manifest
+ manifest, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "sudo docker inspect " .. daily_image))
+ if err then
+ return nil, "failed to inspect daily image: " .. err
+ end
+ local labels
+ labels, err = perf.parse_docker_image_labels(manifest)
+ if err then
+ return nil, "failed to use parse daily image manifest: " .. err
+ end
+
+ self.log.debug("daily image " .. labels.version .." was pushed at ", labels.created)
+ self.daily_image_desc = labels.version .. ", " .. labels.created
+ end
+
+ local kong_conf = {}
+ kong_conf["pg_host"] = self.db_internal_ip
+ kong_conf["pg_password"] = PG_PASSWORD
+ kong_conf["pg_database"] = "kong_tests"
+
+ local kong_conf_blob = ""
+ for k, v in pairs(kong_conf) do
+ kong_conf_blob = string.format("%s\n%s=%s\n", kong_conf_blob, k, v)
+ end
+ kong_conf_blob = ngx.encode_base64(kong_conf_blob):gsub("\n", "")
+
+ _, err = execute_batch(self, nil, {
+ -- upload
+ git_repo_path and ("(cd " .. git_repo_path .. " && tar zc kong) | " .. ssh_execute_wrap(self, self.kong_ip,
+ "sudo tar zx -C /usr/local/share/lua/5.1; sudo find /usr/local/share/lua/5.1/kong -name '*.ljbc' -delete; true"))
+ or "echo use stock files",
+ git_repo_path and (ssh_execute_wrap(self, self.kong_ip,
+ "sudo cp -r /usr/local/share/lua/5.1/kong/include/. /usr/local/kong/include/ && sudo chmod 777 -R /usr/local/kong/include/ || true"))
+ or "echo use stock files",
+ git_repo_path and ("(cd " .. git_repo_path .. " && tar zc plugins-ee/*/kong/plugins/* --transform='s,plugins-ee/[^/]*/kong,kong,') | " ..
+ ssh_execute_wrap(self, self.kong_ip, "sudo tar zx -C /usr/local/share/lua/5.1"))
+ or "echo use stock files",
+ -- run migrations with default configurations
+ ssh_execute_wrap(self, self.kong_ip,
+ "sudo mkdir -p /etc/kong"),
+ ssh_execute_wrap(self, self.kong_ip,
+ "echo " .. kong_conf_blob .. " | base64 -d | sudo tee /etc/kong/kong.conf"),
+ ssh_execute_wrap(self, self.kong_ip,
+ "sudo kong migrations bootstrap"),
+ ssh_execute_wrap(self, self.kong_ip,
+ "sudo kong migrations up -y || true"),
+ ssh_execute_wrap(self, self.kong_ip,
+ "sudo kong migrations finish -y || true"),
+ })
+ if err then
+ return false, err
+ end
+
+ self.setup_kong_called = true
+
+ return prepare_spec_helpers(self, git_repo_path, version)
+end
+
+function _M:start_kong(kong_conf, driver_conf)
+ if not self.setup_kong_called then
+ return false, "setup_kong() must be called before start_kong()"
+ end
+
+ local kong_name = driver_conf and driver_conf.name or "default"
+ local prefix = "/usr/local/kong_" .. kong_name
+ local conf_path = "/etc/kong/" .. kong_name .. ".conf"
+
+ kong_conf = kong_conf or {}
+ kong_conf["prefix"] = kong_conf["prefix"] or prefix
+ kong_conf["pg_host"] = kong_conf["pg_host"] or self.db_internal_ip
+ kong_conf["pg_password"] = kong_conf["pg_password"] or PG_PASSWORD
+ kong_conf["pg_database"] = kong_conf["pg_database"] or "kong_tests"
+
+ kong_conf['proxy_access_log'] = kong_conf['proxy_access_log'] or "/dev/null"
+ kong_conf['proxy_error_log'] = kong_conf['proxy_error_log'] or KONG_ERROR_LOG_PATH
+ kong_conf['admin_error_log'] = kong_conf['admin_error_log'] or KONG_ERROR_LOG_PATH
+
+ KONG_ADMIN_PORT = 39001
+ kong_conf['admin_listen'] = kong_conf['admin_listen'] or ("0.0.0.0:" .. KONG_ADMIN_PORT)
+ kong_conf['vitals'] = kong_conf['vitals'] or "off"
+ kong_conf['anonymous_reports'] = kong_conf['anonymous_reports'] or "off"
+ if not kong_conf['cluster_cert'] then
+ kong_conf['cluster_cert'] = KONG_DEFAULT_HYBRID_CERT
+ kong_conf['cluster_cert_key'] = KONG_DEFAULT_HYBRID_CERT_KEY
+ end
+
+ local kong_license_blob = ""
+ if kong_conf['license_data'] then
+ kong_license_blob = kong_conf['license_data']
+ kong_conf['license_data'] = nil
+ end
+
+ local kong_conf_blob = ""
+ for k, v in pairs(kong_conf) do
+ kong_conf_blob = string.format("%s\n%s=%s\n", kong_conf_blob, k, v)
+ end
+ kong_conf_blob = ngx.encode_base64(kong_conf_blob):gsub("\n", "")
+ kong_license_blob = ngx.encode_base64(kong_license_blob)
+
+ local _, err = execute_batch(self, self.kong_ip, {
+ "mkdir -p /etc/kong || true",
+ "echo " .. kong_conf_blob .. " | base64 -d | sudo tee " .. conf_path,
+ "echo " .. kong_license_blob .. " | base64 -d | sudo tee /etc/kong/license.json",
+ "sudo rm -rf " .. prefix .. " && sudo mkdir -p " .. prefix .. " && sudo chown kong:kong -R " .. prefix,
+ "sudo kong check " .. conf_path,
+ string.format("sudo kong migrations up -y -c %s || true", conf_path),
+ string.format("sudo kong migrations finish -y -c %s || true", conf_path),
+ string.format("ulimit -n 655360; sudo kong start -c %s || sudo kong restart -c %s", conf_path, conf_path),
+ -- set mapping of kong name to IP for use like Hybrid mode
+ "grep -q 'START PERF HOSTS' /etc/hosts || (echo '## START PERF HOSTS' | sudo tee -a /etc/hosts)",
+ "echo " .. self.kong_internal_ip .. " " .. kong_name .. " | sudo tee -a /etc/hosts",
+ })
+ if err then
+ return false, err
+ end
+
+ return true
+end
+
+function _M:stop_kong()
+ local load, err = perf.execute(ssh_execute_wrap(self, self.kong_ip,
+ "cat /proc/loadavg"))
+ if err then
+ self.log.err("failed to get loadavg: " .. err)
+ end
+
+ self.log.debug("Kong node end 1m loadavg is ", load:match("[%d%.]+"))
+
+ return execute_batch(self, self.kong_ip, {
+ "sudo pkill -kill nginx",
+ "sudo sed '/START PERF HOSTS/Q' -i /etc/hosts",
+ })
+end
+
+function _M:get_start_load_cmd(stub, script, uri)
+ if not uri then
+ uri = string.format("http://%s:8000", self.kong_internal_ip)
+ end
+
+ local script_path
+ if script then
+ script_path = string.format("/tmp/wrk-%s.lua", tools.random_string())
+ local out, err = perf.execute(
+ ssh_execute_wrap(self, self.worker_ip, "tee " .. script_path),
+ {
+ stdin = script,
+ })
+ if err then
+ return false, "failed to write script in remote machine: " .. (out or err)
+ end
+ end
+
+ script_path = script_path and ("-s " .. script_path) or ""
+
+ local nproc, err
+ -- find the physical cores count, instead of counting hyperthreading
+ nproc, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, [[grep '^cpu\scores' /proc/cpuinfo | uniq | awk '{print $4}']]))
+ if not nproc or err then
+ return false, "failed to get core count: " .. (err or "")
+ end
+
+ if not tonumber(nproc) then
+ return false, "failed to get core count: " .. (nproc or "")
+ end
+ nproc = tonumber(nproc)
+
+ local loadavg
+
+ while true do
+ loadavg, err = perf.execute(ssh_execute_wrap(self, self.kong_ip,
+ "cat /proc/loadavg"))
+ if not loadavg or err then
+ self.log.err("failed to get loadavg: ", (err or ""))
+ goto continue
+ end
+
+ loadavg = loadavg:match("[%d%.]+")
+ if not loadavg or not tonumber(loadavg) then
+ self.log.err("failed to get loadavg: ", loadavg or "nil")
+ goto continue
+ end
+ loadavg = tonumber(loadavg)
+
+ local load_normalized = loadavg / nproc
+ if load_normalized < LOAD_NORMALIZED_THRESHOLD then
+ break
+ end
+
+ self.log.info("waiting for Kong node 1m loadavg to drop under ",
+ nproc * LOAD_NORMALIZED_THRESHOLD, ", now: ", loadavg)
+ ngx.sleep(15)
+
+ ::continue::
+ end
+ self.log.debug("Kong node start 1m loadavg is ", loadavg)
+
+ return ssh_execute_wrap(self, self.worker_ip,
+ stub:format(script_path, uri))
+end
+
+function _M:get_admin_uri(kong_name)
+ return string.format("http://%s:%s", self.kong_internal_ip, get_admin_port(self, kong_name))
+end
+
+local function check_systemtap_sanity(self)
+ local _, err
+ _, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "which stap"))
+ if err then
+ _, err = execute_batch(self, self.kong_ip, {
+ "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install g++ libelf-dev libdw-dev libssl-dev libsqlite3-dev libnss3-dev pkg-config python3 make -y --force-yes",
+ "wget https://sourceware.org/ftp/systemtap/releases/systemtap-4.6.tar.gz -O systemtap.tar.gz",
+ "tar xf systemtap.tar.gz",
+ "cd systemtap-*/ && " ..
+ "./configure --enable-sqlite --enable-bpf --enable-nls --enable-nss --enable-avahi && " ..
+ "make PREFIX=/usr -j$(nproc) && "..
+ "sudo make install"
+ })
+ if err then
+ return false, "failed to build systemtap: " .. err
+ end
+ end
+
+ _, err = execute_batch(self, self.kong_ip, {
+ "sudo DEBIAN_FRONTEND=\"noninteractive\" apt-get install gcc linux-headers-$(uname -r) -y --force-yes",
+ "which stap",
+ "stat /tmp/stapxx || git clone https://github.com/Kong/stapxx /tmp/stapxx",
+ "stat /tmp/perf-ost || git clone https://github.com/openresty/openresty-systemtap-toolkit /tmp/perf-ost",
+ "stat /tmp/perf-fg || git clone https://github.com/brendangregg/FlameGraph /tmp/perf-fg"
+ })
+ if err then
+ return false, err
+ end
+
+ -- try compile the kernel module
+ local out
+ out, err = perf.execute(ssh_execute_wrap(self, self.kong_ip,
+ "sudo stap -ve 'probe begin { print(\"hello\\n\"); exit();}'"))
+ if err then
+ return nil, "systemtap failed to compile kernel module: " .. (out or "nil") ..
+ " err: " .. (err or "nil") .. "\n Did you install gcc and kernel headers?"
+ end
+
+ return true
+end
+
+function _M:get_start_stapxx_cmd(sample, args, driver_conf)
+ if not self.systemtap_sanity_checked then
+ local ok, err = check_systemtap_sanity(self)
+ if not ok then
+ return nil, err
+ end
+ self.systemtap_sanity_checked = true
+ end
+
+ -- find one of kong's child process hopefully it's a worker
+ -- (does kong have cache loader/manager?)
+ local kong_name = driver_conf and driver_conf.name or "default"
+ local prefix = "/usr/local/kong_" .. kong_name
+ local pid, err = perf.execute(ssh_execute_wrap(self, self.kong_ip,
+ "pid=$(cat " .. prefix .. "/pids/nginx.pid); " ..
+ "cat /proc/$pid/task/$pid/children | awk '{print $1}'"))
+ if err or not tonumber(pid) then
+ return nil, "failed to get Kong worker PID: " .. (err or "nil")
+ end
+
+ self.systemtap_dest_path = "/tmp/" .. tools.random_string()
+ return ssh_execute_wrap(self, self.kong_ip,
+ "sudo /tmp/stapxx/stap++ /tmp/stapxx/samples/" .. sample ..
+ " --skip-badvars -D MAXSKIPPED=1000000 -x " .. pid ..
+ " " .. args ..
+ " > " .. self.systemtap_dest_path .. ".bt"
+ )
+end
+
+function _M:get_wait_stapxx_cmd(timeout)
+ return ssh_execute_wrap(self, self.kong_ip, "lsmod | grep stap_")
+end
+
+function _M:generate_flamegraph(title, opts)
+ local path = self.systemtap_dest_path
+ self.systemtap_dest_path = nil
+
+ local out, err = perf.execute(ssh_execute_wrap(self, self.kong_ip, "cat " .. path .. ".bt"))
+ if err or #out == 0 then
+ return nil, "systemtap output is empty, possibly no sample are captured"
+ end
+
+ local ok, err = execute_batch(self, self.kong_ip, {
+ -- if there's any error like ee with compiled bytecode, skip fix-lua-bt
+ "/tmp/perf-ost/fix-lua-bt " .. path .. ".bt > " .. path .. ".fbt || true",
+ "stat " .. path .. ".fbt || cp " .. path .. ".bt " .. path .. ".fbt",
+ "/tmp/perf-fg/stackcollapse-stap.pl " .. path .. ".fbt > " .. path .. ".cbt",
+ "/tmp/perf-fg/flamegraph.pl --title='" .. title .. "' " .. (opts or "") .. " " .. path .. ".cbt > " .. path .. ".svg",
+ })
+ if not ok then
+ return false, err
+ end
+
+ local out, _ = perf.execute(ssh_execute_wrap(self, self.kong_ip, "cat " .. path .. ".svg"))
+
+ perf.execute(ssh_execute_wrap(self, self.kong_ip, "sudo rm -v " .. path .. ".*"),
+ { logger = self.ssh_log.log_exec })
+
+ return out
+end
+
+function _M:save_error_log(path)
+ return perf.execute(ssh_execute_wrap(self, self.kong_ip,
+ "cat " .. KONG_ERROR_LOG_PATH) .. " >'" .. path .. "'",
+ { logger = self.ssh_log.log_exec })
+end
+
+function _M:save_pgdump(path)
+ return perf.execute(ssh_execute_wrap(self, self.kong_ip,
+ "sudo docker exec -i kong-database psql -Ukong kong_tests --data-only") .. " >'" .. path .. "'",
+ { logger = self.ssh_log.log_exec })
+end
+
+function _M:load_pgdump(path, dont_patch_service)
+ local _, err = perf.execute("cat " .. path .. "| " .. ssh_execute_wrap(self, self.kong_ip,
+ "sudo docker exec -i kong-database psql -Ukong kong_tests"),
+ { logger = self.ssh_log.log_exec })
+ if err then
+ return false, err
+ end
+
+ if dont_patch_service then
+ return true
+ end
+
+ return perf.execute("echo \"UPDATE services set host='" .. self.worker_ip ..
+ "', port=" .. UPSTREAM_PORT ..
+ ", protocol='http';\" | " ..
+ ssh_execute_wrap(self, self.kong_ip,
+ "sudo docker exec -i kong-database psql -Ukong kong_tests"),
+ { logger = self.ssh_log.log_exec })
+end
+
+function _M:get_based_version()
+ return self.daily_image_desc or perf.get_kong_version()
+end
+
+return _M
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/git.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/git.lua
new file mode 100644
index 00000000..03553e48
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/git.lua
@@ -0,0 +1,106 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local perf
+local logger = require("spec.helpers.perf.logger")
+local utils = require("spec.helpers.perf.utils")
+
+local my_logger = logger.new_logger("[git]")
+
+local git_temp_repo = "/tmp/perf-temp-repo"
+
+local function is_git_repo()
+ -- reload the perf module, for circular dependency issue
+ perf = require("spec.helpers.perf")
+
+ local _, err = perf.execute("git rev-parse HEAD")
+ return err == nil
+end
+
+-- is this test based on git versions: e.g. have we git checkout versions?
+local function is_git_based()
+ return package.path:find(git_temp_repo)
+end
+
+local function git_checkout(version)
+ -- reload the perf module, for circular dependency issue
+ perf = require("spec.helpers.perf")
+
+ local _, err = perf.execute("which git")
+ if err then
+ error("git binary not found")
+ end
+
+ if not is_git_repo() then
+ error("not in a git repo")
+ end
+
+ for _, cmd in ipairs({
+ "rm -rf " .. git_temp_repo,
+ "git clone . " .. git_temp_repo,
+ "cp -r .git/refs/ " .. git_temp_repo .. "/.git/.", -- copy refs
+ -- version is sometimes a hash so we can't always use -b
+ "cd " .. git_temp_repo .. " && git checkout " ..version
+ }) do
+ local _, err = perf.execute(cmd, { logger = my_logger.log_exec })
+ if err then
+ error("error preparing temporary repo: " .. err)
+ end
+ end
+
+ utils.add_lua_package_paths(git_temp_repo)
+
+ return git_temp_repo
+end
+
+local function git_restore()
+ return utils.restore_lua_package_paths()
+end
+
+local ee_version_suffix_old = "-dev-enterprise-edition" -- pre 3.0 suffix
+local ee_version_suffix = "-enterprise-edition"
+
+local version_map_table = {
+ -- temporary hack, fallback to previous version of artifact
+ -- if current version is not released yet
+ ["3.1.0.0"] = "3.0.0.0",
+}
+
+local alpha_pattern = "(.+)-alpha" -- new version format starting 3.0.0
+
+local function get_kong_version(raw)
+ -- unload the module if it's previously loaded
+ package.loaded["kong.meta"] = nil
+ package.loaded["kong.enterprise_edition.meta"] = nil
+
+ local ok, meta, _ = pcall(require, "kong.meta")
+
+ if ok then
+ local v = meta._VERSION
+ v = string.match(v, alpha_pattern) or v
+
+ if v:endswith(ee_version_suffix_old) then
+ v = v:sub(1, #v-#ee_version_suffix_old)
+ elseif v:endswith(ee_version_suffix) then
+ v = v:sub(1, #v-#ee_version_suffix)
+ end
+ if not raw and version_map_table[v] then
+ return version_map_table[v]
+ end
+ return v
+ end
+ error("can't read Kong version from kong.meta: " .. (meta or "nil"))
+end
+
+
+return {
+ is_git_repo = is_git_repo,
+ is_git_based = is_git_based,
+ git_checkout = git_checkout,
+ git_restore = git_restore,
+ get_kong_version = get_kong_version,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/logger.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/logger.lua
new file mode 100644
index 00000000..46b9ac37
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/logger.lua
@@ -0,0 +1,69 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local tty = require("kong.cmd.utils.tty")
+
+local colors
+
+if not tty.isatty then
+ colors = setmetatable({}, {__index = function() return "" end})
+else
+ colors = { green = '\27[32m', yellow = '\27[33m', red = '\27[31m', reset = '\27[0m' }
+end
+
+local LOG_LEVEL = ngx.NOTICE
+
+-- Some logging helpers
+local level_cfg = {
+ [ngx.DEBUG] = { "debug", colors.green },
+ [ngx.INFO] = { "info", "" },
+ [ngx.NOTICE] = { "notice", "" },
+ [ngx.WARN] = { "warn", colors.yellow },
+ [ngx.ERR] = { "error", colors.red },
+ [ngx.CRIT] = { "crit", colors.red },
+}
+
+local function set_log_level(lvl)
+ if not level_cfg[lvl] then
+ error("Unknown log level ", lvl, 2)
+ end
+ LOG_LEVEL = lvl
+end
+
+local function log(lvl, namespace, ...)
+ lvl = lvl or ngx.INFO
+ local lvl_literal, lvl_color = unpack(level_cfg[lvl] or {"info", ""})
+ if lvl <= LOG_LEVEL then
+ ngx.update_time()
+ local msec = ngx.now()
+ print(lvl_color,
+ ("%s%s %8s %s "):format(
+ ngx.localtime():sub(12),
+ ("%.3f"):format(msec - math.floor(msec)):sub(2),
+ ("[%s]"):format(lvl_literal), namespace
+ ),
+ table.concat({...}, ""),
+ colors.reset)
+ end
+end
+local function new_logger(namespace)
+ return setmetatable({
+ debug = function(...) log(ngx.DEBUG, namespace, ...) end,
+ info = function(...) log(ngx.INFO, namespace, ...) end,
+ warn = function(...) log(ngx.WARN, namespace, ...) end,
+ err = function(...) log(ngx.ERR, namespace, ...) end,
+ crit = function(...) log(ngx.CRIT, namespace, ...) end,
+ log_exec = function(...) log(ngx.DEBUG, namespace, "=> ", ...) end,
+ }, {
+ __call = function(self, lvl, ...) log(lvl, namespace, ...) end,
+ })
+end
+
+return {
+ new_logger = new_logger,
+ set_log_level = set_log_level,
+}
\ No newline at end of file
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/perf/utils.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/utils.lua
new file mode 100644
index 00000000..44a54b2f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/perf/utils.lua
@@ -0,0 +1,258 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ngx_pipe = require("ngx.pipe")
+local ffi = require("ffi")
+local cjson_safe = require("cjson.safe")
+local logger = require("spec.helpers.perf.logger")
+
+local log = logger.new_logger("[controller]")
+local DISABLE_EXEC_OUTPUT = os.getenv("PERF_TEST_DISABLE_EXEC_OUTPUT") or false
+
+string.startswith = function(s, start) -- luacheck: ignore
+ return s and start and start ~= "" and s:sub(1, #start) == start
+end
+
+string.endswith = function(s, e) -- luacheck: ignore
+ return s and e and e ~= "" and s:sub(#s-#e+1, #s) == e
+end
+
+--- Spawns a child process and get its exit code and outputs
+-- @param opts.stdin string the stdin buffer
+-- @param opts.logger function(lvl, _, line) stdout+stderr writer; if not defined, whole
+-- stdout and stderr is returned
+-- @param opts.stop_signal function return true to abort execution
+-- @return stdout+stderr string, err|nil
+local function execute(cmd, opts)
+ local log_output = opts and opts.logger
+
+ -- skip if PERF_TEST_DISABLE_EXEC_OUTPUT is set
+ if not DISABLE_EXEC_OUTPUT then
+ -- fallback to default logger if not defined
+ log_output = log_output or log.debug
+ log_output("[exec]: ", cmd)
+ end
+
+ local proc, err = ngx_pipe.spawn(cmd, {
+ merge_stderr = true,
+ })
+ if not proc then
+ return "", "failed to start process: " .. err
+ end
+
+ -- set stdout/stderr read timeout to 1s for faster noticing process exit
+ -- proc:set_timeouts(write_timeout?, stdout_read_timeout?, stderr_read_timeout?, wait_timeout?)
+ proc:set_timeouts(nil, 1000, 1000, nil)
+ if opts and opts.stdin then
+ proc:write(opts.stdin)
+ end
+ proc:shutdown("stdin")
+
+ local ret = {}
+
+ while true do
+ -- is it alive?
+ local ok = proc:kill(0)
+ if not ok then
+ break
+ end
+
+ local l, err = proc:stdout_read_line()
+ if l then
+ if log_output then
+ log_output(l)
+ end
+
+ -- always store output
+ table.insert(ret, l)
+ end
+ if err == "closed" then
+ break
+ end
+ local sig = opts and opts.stop_signal and opts.stop_signal()
+ if sig then
+ proc:kill(sig)
+ break
+ end
+ end
+ local ok, msg, code = proc:wait()
+ ok = ok and code == 0
+ ret = table.concat(ret, "\n")
+ if ok then
+ return ret
+ end
+
+ return ret, ("process exited with code %s: %s"):format(code, msg)
+end
+
+--- Execute a command and return until pattern is found in its output
+-- @function wait_output
+-- @param cmd string the command the execute
+-- @param pattern string the pattern to find in stdout and stderr
+-- @param timeout number time in seconds to wait for the pattern
+-- @return bool whether the pattern is found
+local function wait_output(cmd, pattern, timeout)
+ timeout = timeout or 5
+ local found
+ local co = coroutine.create(function()
+ while not found do
+ local line = coroutine.yield("yield")
+ if line:match(pattern) then
+ found = true
+ end
+ end
+ end)
+
+ -- start
+ coroutine.resume(co)
+
+ -- don't kill it, it me finish by itself
+ ngx.thread.spawn(function()
+ execute(cmd, {
+ logger = function(line)
+ return coroutine.running(co) and coroutine.resume(co, line)
+ end,
+ stop_signal = function() if found then return 9 end end,
+ })
+ end)
+
+ ngx.update_time()
+ local s = ngx.now()
+ while not found and ngx.now() - s <= timeout do
+ ngx.update_time()
+ ngx.sleep(0.1)
+ end
+
+ return found
+end
+
+ffi.cdef [[
+ int setenv(const char *name, const char *value, int overwrite);
+ int unsetenv(const char *name);
+]]
+
+--- Set an environment variable
+-- @function setenv
+-- @param env (string) name of the environment variable
+-- @param value the value to set
+-- @return true on success, false otherwise
+local function setenv(env, value)
+ return ffi.C.setenv(env, value, 1) == 0
+end
+
+
+--- Unset an environment variable
+-- @function setenv
+-- @param env (string) name of the environment variable
+-- @return true on success, false otherwise
+local function unsetenv(env)
+ return ffi.C.unsetenv(env) == 0
+end
+
+local handler = require("busted.outputHandlers.base")()
+local current_test_element
+
+local function register_busted_hook()
+ local busted = require("busted")
+
+ handler.testStart = function(element, parent)
+ current_test_element = element
+ end
+
+ busted.subscribe({'test', 'start'}, handler.testStart)
+end
+
+local function get_test_descriptor(sanitized, element_override)
+ local elem = current_test_element or element_override
+ if elem then
+ local msg = handler.getFullName(elem)
+ local common_prefix = "perf test for Kong "
+ if msg:startswith(common_prefix) then
+ msg = msg:sub(#common_prefix+1)
+ end
+ if sanitized then
+ msg = msg:gsub("[:/]", "#"):gsub("[ ,]", "_"):gsub("__", "_")
+ end
+ return msg
+ end
+end
+
+local function get_test_output_filename()
+ return get_test_descriptor(true)
+end
+
+local function parse_docker_image_labels(docker_inspect_output)
+ local m, err = cjson_safe.decode(docker_inspect_output)
+ if err then
+ return nil, err
+ end
+
+ local labels = m[1].Config.Labels or {}
+ labels.version = labels["org.opencontainers.image.version"] or "unknown_version"
+ labels.revision = labels["org.opencontainers.image.revision"] or "unknown_revision"
+ labels.created = labels["org.opencontainers.image.created"] or "unknown_created"
+ return labels
+end
+
+local original_lua_package_paths = package.path
+local function add_lua_package_paths(d)
+ d = d or "."
+ local pp = d .. "/?.lua;" ..
+ d .. "/?/init.lua;"
+ local pl_dir = require("pl.dir")
+ local pl_path = require("pl.path")
+ if pl_path.isdir(d .. "/plugins-ee") then
+ for _, p in ipairs(pl_dir.getdirectories(d .. "/plugins-ee")) do
+ pp = pp.. p .. "/?.lua;"..
+ p .. "/?/init.lua;"
+ end
+ end
+ package.path = pp .. ";" .. original_lua_package_paths
+end
+
+local function restore_lua_package_paths()
+ package.path = original_lua_package_paths
+end
+
+-- clear certain packages to allow spec.helpers to be re-imported
+-- those modules are only needed to run migrations in the "controller"
+-- and won't affect kong instances performing tests
+local function clear_loaded_package()
+ for _, p in ipairs({
+ "spec.helpers", "kong.cluster_events",
+ "kong.global", "kong.constants",
+ "kong.meta", "kong.enterprise_edition.meta",
+ "kong.cache", "kong.db", "kong.plugins", "kong.pdk", "kong.enterprise_edition.pdk",
+ }) do
+ package.loaded[p] = nil
+ end
+end
+
+local function print_and_save(s, path)
+ local shell = require "resty.shell"
+ shell.run("mkdir -p output", nil, 0)
+ print(s)
+ local f = io.open(path or "output/result.txt", "a")
+ f:write(s)
+ f:write("\n")
+ f:close()
+end
+
+return {
+ execute = execute,
+ wait_output = wait_output,
+ setenv = setenv,
+ unsetenv = unsetenv,
+ register_busted_hook = register_busted_hook,
+ get_test_descriptor = get_test_descriptor,
+ get_test_output_filename = get_test_output_filename,
+ parse_docker_image_labels = parse_docker_image_labels,
+ add_lua_package_paths = add_lua_package_paths,
+ restore_lua_package_paths = restore_lua_package_paths,
+ clear_loaded_package = clear_loaded_package,
+ print_and_save = print_and_save,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/redis_helper.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/redis_helper.lua
new file mode 100644
index 00000000..3191aabd
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/redis_helper.lua
@@ -0,0 +1,53 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local redis = require "resty.redis"
+local version = require "version"
+
+local DEFAULT_TIMEOUT = 2000
+
+
+local function connect(host, port)
+ local redis_client = redis:new()
+ redis_client:set_timeout(DEFAULT_TIMEOUT)
+ assert(redis_client:connect(host, port))
+
+ local red_password = os.getenv("REDIS_PASSWORD") or nil -- This will allow for testing with a secured redis instance
+ if red_password then
+ assert(redis_client:auth(red_password))
+ end
+
+ local red_version = string.match(redis_client:info(), 'redis_version:([%g]+)\r\n')
+ return redis_client, assert(version(red_version))
+end
+
+local function reset_redis(host, port)
+ local redis_client = connect(host, port)
+ redis_client:flushall()
+ redis_client:close()
+end
+
+local function add_admin_user(redis_client, username, password)
+ assert(redis_client:acl("setuser", username, "on", "allkeys", "allcommands", ">" .. password))
+end
+
+local function add_basic_user(redis_client, username, password)
+ assert(redis_client:acl("setuser", username, "on", "allkeys", "+get", ">" .. password))
+end
+
+local function remove_user(redis_client, username)
+ assert(redis_client:acl("deluser", username))
+end
+
+
+return {
+ connect = connect,
+ add_admin_user = add_admin_user,
+ add_basic_user = add_basic_user,
+ remove_user = remove_user,
+ reset_redis = reset_redis,
+}
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/ssl.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/ssl.lua
new file mode 100644
index 00000000..77233962
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/ssl.lua
@@ -0,0 +1,275 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local ffi = require "ffi"
+local C = ffi.C
+local bit = require "bit"
+local format_error = require("resty.openssl.err").format_error
+require "resty.openssl.include.ssl"
+
+ffi.cdef [[
+ typedef struct ssl_method_st SSL_METHOD;
+ const SSL_METHOD *TLS_method(void);
+ const SSL_METHOD *TLS_server_method(void);
+
+ SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);
+ void SSL_CTX_free(SSL_CTX *ctx);
+
+ int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file);
+ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
+
+ SSL *SSL_new(SSL_CTX *ctx);
+ void SSL_free(SSL *s);
+
+ long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg);
+ long SSL_set_mode(SSL *ssl, long mode);
+
+ int SSL_set_fd(SSL *ssl, int fd);
+
+ void SSL_set_accept_state(SSL *ssl);
+
+ int SSL_do_handshake(SSL *ssl);
+ int SSL_get_error(const SSL *ssl, int ret);
+
+ int SSL_read(SSL *ssl, void *buf, int num);
+ int SSL_write(SSL *ssl, const void *buf, int num);
+ int SSL_shutdown(SSL *ssl);
+
+
+ typedef struct pollfd {
+ int fd; /* file descriptor */
+ short events; /* requested events */
+ short revents; /* returned events */
+ } pollfd;
+
+ int poll(struct pollfd *fds, unsigned long nfds, int timeout);
+]]
+
+
+local SSL = {}
+local ssl_mt = { __index = SSL }
+
+local modes = {
+ SSL_MODE_ENABLE_PARTIAL_WRITE = 0x001,
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = 0x002,
+ SSL_MODE_AUTO_RETRY = 0x004,
+ SSL_MODE_NO_AUTO_CHAIN = 0x008,
+ SSL_MODE_RELEASE_BUFFERS = 0x010,
+ SSL_MODE_SEND_CLIENTHELLO_TIME = 0x020,
+ SSL_MODE_SEND_SERVERHELLO_TIME = 0x040,
+ SSL_MODE_SEND_FALLBACK_SCSV = 0x080,
+ SSL_MODE_ASYNC = 0x100,
+ SSL_MODE_DTLS_SCTP_LABEL_LENGTH_BUG = 0x400,
+}
+
+local errors = {
+ SSL_ERROR_NONE = 0,
+ SSL_ERROR_SSL = 1,
+ SSL_ERROR_WANT_READ = 2,
+ SSL_ERROR_WANT_WRITE = 3,
+ SSL_ERROR_WANT_X509_LOOKUP = 4,
+ SSL_ERROR_SYSCALL = 5,
+ SSL_ERROR_ZERO_RETURN = 6,
+ SSL_ERROR_WANT_CONNECT = 7,
+ SSL_ERROR_WANT_ACCEPT = 8,
+ SSL_ERROR_WANT_ASYNC = 9,
+ SSL_ERROR_WANT_ASYNC_JOB = 10,
+ SSL_ERROR_WANT_CLIENT_HELLO_CB = 11,
+ SSL_ERROR_WANT_RETRY_VERIFY = 12,
+}
+
+local SOCKET_INVALID = -1
+local SSL_FILETYPE_PEM = 1
+
+local errors_literal = {}
+for k, v in pairs(errors) do
+ errors_literal[v] = k
+end
+
+local function ssl_set_mode(ctx, mode)
+ return C.SSL_ctrl(ctx, 33, mode, nil)
+end
+
+local function ssl_ctx_new(cfg)
+ if cfg.protocol and cfg.protocol ~= "any" then
+ return nil, "protocol other than 'any' is currently not supported"
+ elseif cfg.mode and cfg.mode ~= "server" then
+ return nil, "mode other than 'server' is currently not supported"
+ end
+ cfg.protocol = nil
+ cfg.mode = nil
+
+ local ctx = C.SSL_CTX_new(C.TLS_server_method())
+ if ctx == nil then
+ return nil, format_error("SSL_CTX_new")
+ end
+ ffi.gc(ctx, C.SSL_CTX_free)
+
+ for k, v in pairs(cfg) do
+ if k == "certificate" then
+ if C.SSL_CTX_use_certificate_chain_file(ctx, v) ~= 1 then
+ return nil, format_error("SSL_CTX_use_certificate_chain_file")
+ end
+ elseif k == "key" then -- password protected key is NYI
+ if C.SSL_CTX_use_PrivateKey_file(ctx, v, SSL_FILETYPE_PEM) ~= 1 then
+ return nil, format_error("SSL_CTX_use_PrivateKey_file")
+ end
+ else
+ return nil, "unknown option \"" .. k .. "\""
+ end
+ end
+
+ return ctx
+end
+
+local function ssl_new(ssl_ctx)
+ if not ssl_ctx or not ffi.istype("SSL_CTX*", ssl_ctx) then
+ return nil, "ssl_new: expect SSL_CTX* as first argument"
+ end
+
+ local ctx = C.SSL_new(ssl_ctx)
+ if ctx == nil then
+ return nil, format_error("SSL_new")
+ end
+ ffi.gc(ctx, C.SSL_free)
+
+ C.SSL_set_fd(ctx, SOCKET_INVALID)
+ ssl_set_mode(ctx, bit.bor(modes.SSL_MODE_ENABLE_PARTIAL_WRITE,
+ modes.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER))
+ ssl_set_mode(ctx, modes.SSL_MODE_RELEASE_BUFFERS)
+
+ C.SSL_set_accept_state(ctx) -- me is server
+
+ return ctx
+end
+
+function SSL.wrap(sock, cfg)
+ local ctx, err
+ if type(cfg) == "table" then
+ ctx, err = ssl_ctx_new(cfg)
+ if not ctx then return nil, err end
+ else
+ ctx = cfg
+ end
+ local s, err = ssl_new(ctx)
+ if s then
+ local fd = sock:getfd()
+ C.SSL_set_fd(s, fd)
+ sock:setfd(SOCKET_INVALID)
+
+ local self = setmetatable({
+ ssl_ctx = ctx,
+ ctx = s,
+ fd = fd,
+ }, ssl_mt)
+
+ return self, nil
+ end
+ return nil, err
+end
+
+local function socket_waitfd(fd, events, timeout)
+ local pfd = ffi.new("pollfd")
+ pfd.fd = fd
+ pfd.events = events
+ pfd.revents = 0
+ local ppfd = ffi.new("pollfd[1]", pfd)
+
+ local wait = timeout and 1 or -1
+
+ while true do
+ local ret = C.poll(ppfd, 1, wait)
+ timeout = timeout and timeout - 1
+ if ret ~= -1 then
+ break
+ end
+ end
+end
+
+local POLLIN = 1
+local POLLOUT = 2
+
+local function handle_ssl_io(self, cb, ...)
+ local err, code
+ while true do
+ err = cb(self.ctx, ...)
+ code = C.SSL_get_error(self.ctx, err)
+ if code == errors.SSL_ERROR_NONE then
+ break
+ elseif code == errors.SSL_ERROR_WANT_READ then
+ err = socket_waitfd(self.fd, POLLIN, 10)
+ if err then return nil, "want read: " .. err end
+ elseif code == errors.SSL_ERROR_WANT_WRITE then
+ err = socket_waitfd(self.fd, POLLOUT, 10)
+ if err then return nil, "want write: " .. err end
+ elseif code == errors.SSL_ERROR_SYSCALL then
+ if err == 0 then
+ return nil, "closed"
+ end
+ if C.ERR_peek_error() then
+ return nil, format_error("SSL_ERROR_SYSCALL")
+ end
+ else
+ return nil, errors_literal[code] or "unknown error"
+ end
+ end
+end
+
+function SSL:dohandshake()
+ return handle_ssl_io(self, C.SSL_do_handshake)
+end
+
+
+function SSL:receive(pattern)
+ if pattern and pattern ~= "*l" then
+ return nil, "receive pattern other than '*l' is currently not supported"
+ end
+
+ local buf = ffi.new("char[1024]")
+ local ret = ""
+
+ while true do
+ local ok, err = handle_ssl_io(self, C.SSL_read, ffi.cast("void *", buf), 1024)
+ if err then
+ if err == "SSL_ERROR_ZERO_RETURN" then
+ err = "closed"
+ end
+ return ok, err
+ end
+
+ local current = ffi.string(buf)
+ -- do we need to find \r?
+ local pos = current:find("\n")
+ if pos then -- found a newline
+ ret = ret .. current:sub(1, pos-1)
+ break
+ else
+ ret = ret .. current
+ end
+ end
+
+ return ret
+end
+
+function SSL:send(s)
+ local buf = ffi.new("char[?]", #s+1, s)
+ local ok, err = handle_ssl_io(self, C.SSL_write, ffi.cast("void *", buf), #s)
+ if err then
+ return ok, err
+ end
+
+ return true
+end
+
+function SSL:close()
+ if C.SSL_shutdown(self.ctx) ~= 1 then
+ return nil, format_error("SSL_shutdown")
+ end
+ return true
+end
+
+return SSL
diff --git a/kong-versions/test9.9.9.3/kong/spec/helpers/wait.lua b/kong-versions/test9.9.9.3/kong/spec/helpers/wait.lua
new file mode 100644
index 00000000..2a082185
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/helpers/wait.lua
@@ -0,0 +1,497 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+
+local say = require "say"
+local luassert = require "luassert.assert"
+local pretty = require "pl.pretty"
+
+local fmt = string.format
+local insert = table.insert
+
+---@param v any
+---@return string
+local function pretty_print(v)
+ local s, err = pretty.write(v)
+ if not s then
+ s = "ERROR: failed to pretty-print value: " .. tostring(err)
+ end
+ return s
+end
+
+
+local E_ARG_COUNT = "assertion.internal.argtolittle"
+local E_ARG_TYPE = "assertion.internal.badargtype"
+
+
+--- spec.helpers.wait.ctx.result
+-- "timeout", "error", "success", "max tries"
+local TIMEOUT = "timeout"
+local ERROR = "error"
+local SUCCESS = "success"
+local MAX_TRIES = "max tries"
+
+
+---
+-- @table spec.helpers.wait.ctx.condition
+-- helper functions that check the result of pcall() and report if the
+-- wait ctx condition has been met
+--
+-- @field "truthy"
+-- @field "falsy"
+-- @field "error"
+-- @field "no_error"
+local COND = {
+ truthy = function(pok, ok_or_err)
+ return (pok and ok_or_err and true) or false
+ end,
+
+ falsy = function(pok, ok_or_err)
+ return (pok and not ok_or_err) or false
+ end,
+
+ error = function(pok)
+ return not pok
+ end,
+
+ no_error = function(pok)
+ return (pok and true) or false
+ end,
+}
+
+
+---
+-- @param ... any
+-- @return any
+local function first_non_nil(...)
+ local n = select("#", ...)
+ for i = 1, n do
+ local v = select(i, ...)
+ if v ~= nil then
+ return v
+ end
+ end
+end
+
+
+---
+-- @param exp_type string
+-- @param field string|integer
+-- @param value any
+-- @param caller? string
+-- @param level? integer
+-- @return any
+local function check_type(exp_type, field, value, caller, level)
+ caller = caller or "wait_until"
+ level = (level or 1) + 1
+
+ local got_type = type(value)
+
+ -- accept callable tables
+ if exp_type == "function"
+ and got_type == "table"
+ and type(debug.getmetatable(value)) == "table"
+ and type(debug.getmetatable(value).__call) == "function"
+ then
+ got_type = "function"
+ end
+
+ if got_type ~= exp_type then
+ error(say(E_ARG_TYPE, { field, caller, exp_type, type(value) }),
+ level)
+ end
+
+ return value
+end
+
+
+local DEFAULTS = {
+ timeout = 5,
+ step = 0.05,
+ message = "UNSPECIFIED",
+ max_tries = 0,
+ ignore_exceptions = false,
+ condition = "truthy",
+}
+
+
+---
+-- @table spec.helpers.wait.ctx
+--
+-- @field condition "truthy"|"falsy"|"error"|"no_error"
+-- @field condition_met boolean
+-- @field debug? boolean
+-- @field elapsed number
+-- @field last_raised_error any
+-- @field error_raised boolean
+-- @field fn function
+-- @field ignore_exceptions boolean
+-- @field last_returned_error any
+-- @field last_returned_value any
+-- @field last_error any
+-- @field message? string
+-- @field result spec.helpers.wait.ctx.result
+-- @field step number
+-- @field timeout number
+-- @field tries number
+local wait_ctx = {
+ condition = nil,
+ condition_met = false,
+ debug = nil,
+ elapsed = 0,
+ error = nil,
+ error_raised = false,
+ ignore_exceptions = nil,
+ last_returned_error = nil,
+ last_returned_value = nil,
+ max_tries = nil,
+ message = nil,
+ result = "timeout",
+ step = nil,
+ timeout = nil,
+ traceback = nil,
+ tries = 0,
+}
+
+local wait_ctx_mt = { __index = wait_ctx }
+
+function wait_ctx:dd(msg)
+ if self.debug then
+ print(fmt("\n\n%s\n\n", pretty_print(msg)))
+ end
+end
+
+
+function wait_ctx:wait()
+ ngx.update_time()
+
+ local tstart = ngx.now()
+ local texp = tstart + self.timeout
+ local ok, res, err
+
+ local is_met = COND[self.condition]
+
+ if self.condition == "no_error" then
+ self.ignore_exceptions = true
+ end
+
+ local tries_remain = self.max_tries
+
+ local f = self.fn
+
+ local handle_error = function(e)
+ self.traceback = debug.traceback("", 2)
+ return e
+ end
+
+ while true do
+ ok, res, err = xpcall(f, handle_error)
+
+ if ok then
+ self.last_returned_value = first_non_nil(res, self.last_returned_value)
+ self.last_returned_error = first_non_nil(err, self.last_returned_error)
+ self.last_error = first_non_nil(err, self.last_error)
+ else
+ self.error_raised = true
+ self.last_raised_error = first_non_nil(res, self.last_raised_error)
+ self.last_error = first_non_nil(res, self.last_error)
+ end
+
+ self.tries = self.tries + 1
+ tries_remain = tries_remain - 1
+
+ self.condition_met = is_met(ok, res)
+
+ self:dd(self)
+
+ ngx.update_time()
+
+ -- yay!
+ if self.condition_met then
+ self.result = SUCCESS
+ break
+
+ elseif self.error_raised and not self.ignore_exceptions then
+ self.result = ERROR
+ break
+
+ elseif tries_remain == 0 then
+ self.result = MAX_TRIES
+ break
+
+ elseif ngx.now() >= texp then
+ self.result = TIMEOUT
+ break
+ end
+
+ ngx.sleep(self.step)
+ end
+
+ ngx.update_time()
+ self.elapsed = ngx.now() - tstart
+
+ self:dd(self)
+end
+
+
+local CTX_TYPES = {
+ condition = "string",
+ fn = "function",
+ max_tries = "number",
+ timeout = "number",
+ message = "string",
+ step = "number",
+ ignore_exceptions = "boolean",
+}
+
+
+function wait_ctx:validate(key, value, caller, level)
+ local typ = CTX_TYPES[key]
+
+ if not typ then
+ -- we don't care about validating this key
+ return value
+ end
+
+ if key == "condition" and type(value) == "string" then
+ assert(COND[value] ~= nil,
+ say(E_ARG_TYPE, { "condition", caller or "wait_until",
+ "one of: 'truthy', 'falsy', 'error', 'no_error'",
+ value }), level + 1)
+ end
+
+
+ return check_type(typ, key, value, caller, level)
+end
+
+
+---
+-- @param state table
+-- @return spec.helpers.wait.ctx
+local function get_or_create_ctx(state)
+ local ctx = rawget(state, "wait_ctx")
+
+ if not ctx then
+ ctx = setmetatable({}, wait_ctx_mt)
+ rawset(state, "wait_ctx", ctx)
+ end
+
+ return ctx
+end
+
+
+---
+-- @param ctx spec.helpers.wait.ctx
+-- @param key string
+-- @param ... any
+local function param(ctx, key, ...)
+ local value = first_non_nil(first_non_nil(...), DEFAULTS[key])
+ ctx[key] = ctx:validate(key, value, "wait_until", 3)
+end
+
+
+---
+-- @param state table
+-- @param arguments table
+-- @param level integer
+-- @return boolean ok
+-- @return table return_values
+local function wait_until(state, arguments, level)
+ assert(arguments.n > 0,
+ say(E_ARG_COUNT, { "wait_until", 1, arguments.n }),
+ level + 1)
+
+ local input = check_type("table", 1, arguments[1])
+ local ctx = get_or_create_ctx(state)
+
+ param(ctx, "fn", input.fn)
+ param(ctx, "timeout", input.timeout)
+ param(ctx, "step", input.step)
+ param(ctx, "message", input.message, arguments[2])
+ param(ctx, "max_tries", input.max_tries)
+ param(ctx, "debug", input.debug, ctx.debug, false)
+ param(ctx, "condition", input.condition)
+ param(ctx, "ignore_exceptions", input.ignore_exceptions)
+
+ -- reset the state
+ rawset(state, "wait_ctx", nil)
+
+ ctx:wait()
+
+ if ctx.condition_met then
+ return true, { ctx.last_returned_value, n = 1 }
+ end
+
+ local errors = {}
+ local result
+ if ctx.result == ERROR then
+ result = "error() raised"
+
+ elseif ctx.result == MAX_TRIES then
+ result = ("max tries (%s) reached"):format(ctx.max_tries)
+
+ elseif ctx.result == TIMEOUT then
+ result = ("timed out after %ss"):format(ctx.elapsed)
+ end
+
+ if ctx.last_returned_value ~= nil then
+ insert(errors, "Last returned value:")
+ insert(errors, "")
+ insert(errors, pretty_print(ctx.last_returned_value))
+ insert(errors, "")
+ end
+
+ if ctx.last_raised_error ~= nil then
+ insert(errors, "Last raised error:")
+ insert(errors, "")
+ insert(errors, pretty_print(ctx.last_raised_error))
+ insert(errors, "")
+
+ if ctx.traceback then
+ insert(errors, ctx.traceback)
+ insert(errors, "")
+ end
+ end
+
+ if ctx.last_returned_error ~= nil then
+ insert(errors, "Last returned error:")
+ insert(errors, "")
+ insert(errors, pretty_print(ctx.last_returned_error))
+ insert(errors, "")
+ end
+
+ arguments[1] = ctx.message
+ arguments[2] = result
+ arguments[3] = table.concat(errors, "\n")
+ arguments[4] = ctx.timeout
+ arguments[5] = ctx.step
+ arguments[6] = ctx.elapsed
+ arguments[7] = ctx.tries
+ arguments[8] = ctx.error_raised
+ arguments.n = 8
+
+ arguments.nofmt = {}
+ for i = 1, arguments.n do
+ arguments.nofmt[i] = true
+ end
+
+ return false, { ctx.last_error, n = 1 }
+end
+
+
+say:set("assertion.wait_until.failed", [[
+Failed to assert eventual condition:
+
+%q
+
+Result: %s
+
+%s
+---
+
+Timeout = %s
+Step = %s
+Elapsed = %s
+Tries = %s
+Raised = %s
+]])
+
+luassert:register("assertion", "wait_until", wait_until,
+ "assertion.wait_until.failed")
+
+
+local function wait_until_modifier(key)
+ return function(state, arguments)
+ local ctx = get_or_create_ctx(state)
+ ctx[key] = ctx:validate(key, arguments[1], key, 1)
+
+ return state
+ end
+end
+
+luassert:register("modifier", "with_timeout",
+ wait_until_modifier("timeout"))
+
+luassert:register("modifier", "with_step",
+ wait_until_modifier("step"))
+
+luassert:register("modifier", "with_max_tries",
+ wait_until_modifier("max_tries"))
+
+-- luassert blows up on us if we try to use 'error' or 'errors'
+luassert:register("modifier", "ignore_exceptions",
+ wait_until_modifier("ignore_exceptions"))
+
+luassert:register("modifier", "with_debug",
+ wait_until_modifier("debug"))
+
+
+---
+-- @param ctx spec.helpers.wait.ctx
+local function ctx_builder(ctx)
+ local self = setmetatable({}, {
+ __index = function(_, key)
+ error("unknown modifier/assertion: " .. tostring(key), 2)
+ end
+ })
+
+ local function with(field)
+ return function(value)
+ ctx[field] = ctx:validate(field, value, "with_" .. field, 2)
+ return self
+ end
+ end
+
+ self.with_timeout = with("timeout")
+ self.with_step = with("step")
+ self.with_max_tries = with("max_tries")
+ self.with_debug = with("debug")
+
+ self.ignore_exceptions = function(ignore)
+ ctx.ignore_exceptions = ctx:validate("ignore_exceptions", ignore,
+ "ignore_exceptions", 2)
+ return self
+ end
+
+ self.is_truthy = function(msg)
+ ctx.condition = "truthy"
+ return luassert.wait_until(ctx, msg)
+ end
+
+ self.is_falsy = function(msg)
+ ctx.condition = "falsy"
+ return luassert.wait_until(ctx, msg)
+ end
+
+ self.has_error = function(msg)
+ ctx.condition = "error"
+ return luassert.wait_until(ctx, msg)
+ end
+
+ self.has_no_error = function(msg)
+ ctx.condition = "no_error"
+ return luassert.wait_until(ctx, msg)
+ end
+
+ return self
+end
+
+
+local function eventually(state, arguments)
+ local ctx = get_or_create_ctx(state)
+
+ ctx.fn = first_non_nil(arguments[1], ctx.fn)
+
+ check_type("function", 1, ctx.fn, "eventually")
+
+ arguments[1] = ctx_builder(ctx)
+ arguments.n = 1
+
+ return true, arguments
+end
+
+luassert:register("assertion", "eventually", eventually)
diff --git a/kong-versions/test9.9.9.3/kong/spec/kong_tests.conf b/kong-versions/test9.9.9.3/kong/spec/kong_tests.conf
new file mode 100644
index 00000000..a3cbe025
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/kong_tests.conf
@@ -0,0 +1,73 @@
+# 1st digit is 9 for our test instances
+admin_gui_api_url = 127.0.0.1:9001
+admin_listen = 127.0.0.1:9001
+admin_gui_listen = off
+enforce_rbac = off
+proxy_listen = 0.0.0.0:9000, 0.0.0.0:9443 http2 ssl, 0.0.0.0:9002 http2, 0.0.0.0:9445 http2 ssl
+# avoid port conflicts when multiple Kong instances needed for tests
+status_listen = off
+stream_listen = off
+
+ssl_cert = spec/fixtures/kong_spec.crt
+ssl_cert_key = spec/fixtures/kong_spec.key
+
+admin_ssl_cert = spec/fixtures/kong_spec.crt
+admin_ssl_cert_key = spec/fixtures/kong_spec.key
+
+admin_gui_ssl_cert = spec/fixtures/kong_spec.crt
+admin_gui_ssl_cert_key = spec/fixtures/kong_spec.key
+
+portal = off
+portal_gui_listen = 0.0.0.0:9003, 0.0.0.0:9446 ssl
+portal_gui_protocol = http
+portal_gui_host = 127.0.0.1:9003
+portal_gui_ssl_cert = spec/fixtures/kong_spec.crt
+portal_gui_ssl_cert_key = spec/fixtures/kong_spec.key
+portal_api_listen = 0.0.0.0:9004, 0.0.0.0:9447 ssl
+portal_api_access_log = logs/portal_api_access.log
+portal_api_error_log = logs/portal_api_error.log
+portal_gui_access_log = logs/portal_gui_access.log
+portal_gui_error_log = logs/portal_gui_error.log
+proxy_url = http://127.0.0.1:9004
+portal_api_url = http://127.0.0.1:9004
+portal_api_ssl_cert = spec/fixtures/kong_spec.crt
+portal_api_ssl_cert_key = spec/fixtures/kong_spec.key
+
+smtp_mock = on
+
+database = postgres
+pg_host = 127.0.0.1
+pg_port = 5432
+pg_timeout = 15000
+pg_database = kong_tests
+# note: this does not trigger readonly mode to be enabled on its own
+# for that pg_ro_host is also needed
+pg_ro_user = kong_ro
+anonymous_reports = on
+
+worker_consistency = strict
+
+dedicated_config_processing = on
+
+dns_hostsfile = spec/fixtures/hosts
+
+nginx_main_worker_processes = 1
+nginx_main_worker_rlimit_nofile = 4096
+nginx_events_worker_connections = 4096
+nginx_events_multi_accept = off
+
+plugins = bundled,dummy,cache,rewriter,error-handler-log,error-generator,error-generator-last,short-circuit
+
+prefix = servroot
+log_level = debug
+lua_package_path = ./spec/fixtures/custom_plugins/?.lua;./spec/fixtures/custom_vaults/?.lua;./spec/fixtures/custom_vaults/?/init.lua
+
+
+untrusted_lua = sandbox
+
+# temporarily disable hcv and aws due to loading dns issues
+vaults = env
+
+pg_password = foo\#bar# this is a comment that should be stripped
+
+wasm_filters_path = ./spec/fixtures/proxy_wasm_filters/build
diff --git a/kong-versions/test9.9.9.3/kong/spec/ldoc.css b/kong-versions/test9.9.9.3/kong/spec/ldoc.css
new file mode 100644
index 00000000..f6e3d99f
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/ldoc.css
@@ -0,0 +1,291 @@
+body {
+ color: #47555c;
+ font-size: 16px;
+ font-family: "Open Sans", sans-serif;
+ margin: 0;
+ background: #eff4ff;
+}
+
+a:link { color: #008fee; }
+a:visited { color: #008fee; }
+a:hover { color: #22a7ff; }
+
+h1 { font-size:26px; font-weight: normal; }
+h2 { font-size:22px; font-weight: normal; }
+h3 { font-size:18px; font-weight: normal; }
+h4 { font-size:16px; font-weight: bold; }
+
+hr {
+ height: 1px;
+ background: #c1cce4;
+ border: 0px;
+ margin: 15px 0;
+}
+
+code, tt {
+ font-family: monospace;
+}
+span.parameter {
+ font-family: monospace;
+ font-weight: bold;
+ color: rgb(99, 115, 131);
+}
+span.parameter:after {
+ content:":";
+}
+span.types:before {
+ content:"(";
+}
+span.types:after {
+ content:")";
+}
+.type {
+ font-weight: bold; font-style:italic
+}
+
+p.name {
+ font-family: "Andale Mono", monospace;
+}
+
+#navigation {
+ float: left;
+ background-color: white;
+ border-right: 1px solid #d3dbec;
+ border-bottom: 1px solid #d3dbec;
+
+ width: 14em;
+ vertical-align: top;
+ overflow: visible;
+}
+
+#navigation br {
+ display: none;
+}
+
+#navigation h1 {
+ background-color: white;
+ border-bottom: 1px solid #d3dbec;
+ padding: 15px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+#navigation h2 {
+ font-size: 18px;
+ background-color: white;
+ border-bottom: 1px solid #d3dbec;
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ margin-top: 30px;
+ margin-bottom: 0px;
+}
+
+#content h1 {
+ background-color: #2c3e67;
+ color: white;
+ padding: 15px;
+ margin: 0px;
+}
+
+#content h2 {
+ background-color: #6c7ea7;
+ color: white;
+ padding: 15px;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ margin-top: 0px;
+}
+
+#content h2 a {
+ background-color: #6c7ea7;
+ color: white;
+ text-decoration: none;
+}
+
+#content h2 a:hover {
+ text-decoration: underline;
+}
+
+#content h3 {
+ font-style: italic;
+ padding-top: 15px;
+ padding-bottom: 4px;
+ margin-right: 15px;
+ margin-left: 15px;
+ margin-bottom: 5px;
+ border-bottom: solid 1px #bcd;
+}
+
+#content h4 {
+ margin-right: 15px;
+ margin-left: 15px;
+ border-bottom: solid 1px #bcd;
+}
+
+#content pre {
+ margin: 15px;
+}
+
+pre {
+ background-color: rgb(50, 55, 68);
+ color: white;
+ border-radius: 3px;
+ /* border: 1px solid #C0C0C0; /* silver */
+ padding: 15px;
+ overflow: auto;
+ font-family: "Andale Mono", monospace;
+}
+
+#content ul pre.example {
+ margin-left: 0px;
+}
+
+table.index {
+/* border: 1px #00007f; */
+}
+table.index td { text-align: left; vertical-align: top; }
+
+#navigation ul
+{
+ font-size:1em;
+ list-style-type: none;
+ margin: 1px 1px 10px 1px;
+ padding-left: 20px;
+}
+
+#navigation li {
+ text-indent: -1em;
+ display: block;
+ margin: 3px 0px 0px 22px;
+}
+
+#navigation li li a {
+ margin: 0px 3px 0px -1em;
+}
+
+#content {
+ margin-left: 14em;
+}
+
+#content p {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+#content table {
+ padding-left: 15px;
+ padding-right: 15px;
+ background-color: white;
+}
+
+#content p, #content table, #content ol, #content ul, #content dl {
+ max-width: 900px;
+}
+
+#about {
+ padding: 15px;
+ padding-left: 16em;
+ background-color: white;
+ border-top: 1px solid #d3dbec;
+ border-bottom: 1px solid #d3dbec;
+}
+
+table.module_list, table.function_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cccccc;
+ border-collapse: collapse;
+ margin: 15px;
+}
+table.module_list td, table.function_list td {
+ border-width: 1px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ border: solid 1px rgb(193, 204, 228);
+}
+table.module_list td.name, table.function_list td.name {
+ background-color: white; min-width: 200px; border-right-width: 0px;
+}
+table.module_list td.summary, table.function_list td.summary {
+ background-color: white; width: 100%; border-left-width: 0px;
+}
+
+dl.function {
+ margin-right: 15px;
+ margin-left: 15px;
+ border-bottom: solid 1px rgb(193, 204, 228);
+ border-left: solid 1px rgb(193, 204, 228);
+ border-right: solid 1px rgb(193, 204, 228);
+ background-color: white;
+}
+
+dl.function dt {
+ color: rgb(99, 123, 188);
+ font-family: monospace;
+ border-top: solid 1px rgb(193, 204, 228);
+ padding: 15px;
+}
+
+dl.function dd {
+ margin-left: 15px;
+ margin-right: 15px;
+ margin-top: 5px;
+ margin-bottom: 15px;
+}
+
+#content dl.function dd h3 {
+ margin-top: 0px;
+ margin-left: 0px;
+ padding-left: 0px;
+ font-size: 16px;
+ color: rgb(128, 128, 128);
+ border-bottom: solid 1px #def;
+}
+
+#content dl.function dd ul, #content dl.function dd ol {
+ padding: 0px;
+ padding-left: 15px;
+ list-style-type: none;
+}
+
+ul.nowrap {
+ overflow:auto;
+ white-space:nowrap;
+}
+
+.section-description {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+/* stop sublists from having initial vertical space */
+ul ul { margin-top: 0px; }
+ol ul { margin-top: 0px; }
+ol ol { margin-top: 0px; }
+ul ol { margin-top: 0px; }
+
+/* make the target distinct; helps when we're navigating to a function */
+a:target + * {
+ background-color: #FF9;
+}
+
+
+/* styles for prettification of source */
+pre .comment { color: #bbccaa; }
+pre .constant { color: #a8660d; }
+pre .escape { color: #844631; }
+pre .keyword { color: #ffc090; font-weight: bold; }
+pre .library { color: #0e7c6b; }
+pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
+pre .string { color: #8080ff; }
+pre .number { color: #f8660d; }
+pre .operator { color: #2239a8; font-weight: bold; }
+pre .preprocessor, pre .prepro { color: #a33243; }
+pre .global { color: #c040c0; }
+pre .user-keyword { color: #800080; }
+pre .prompt { color: #558817; }
+pre .url { color: #272fc2; text-decoration: underline; }
diff --git a/kong-versions/test9.9.9.3/kong/spec/on_demand_specs b/kong-versions/test9.9.9.3/kong/spec/on_demand_specs
new file mode 100644
index 00000000..dc02f005
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/on_demand_specs
@@ -0,0 +1,4 @@
+# Whitelist regexes representing file paths that will not be tested during running busted CI
+spec/02-integration/05-proxy/10-balancer/05-stress.lua
+spec/03-plugins/16-jwt/fixtures.lua
+spec/04-perf/.*
diff --git a/kong-versions/test9.9.9.3/kong/spec/renderdocs.sh b/kong-versions/test9.9.9.3/kong/spec/renderdocs.sh
new file mode 100755
index 00000000..86cfe1f2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/renderdocs.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# auto-doc renderer
+#
+# will watch the spec directory and upon changes automatically
+# render the helper documentation using `ldoc .`
+# resulting docs are in ./spec/docs/index.html
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+pushd $SCRIPT_DIR
+
+watched_files=$(find . -name '*.lua')
+
+if [ -z "$watched_files" ]; then
+ echo "Nothing to watch, abort"
+ exit 1
+else
+ echo "watching: $watched_files"
+fi
+
+previous_checksum="dummy"
+while true ; do
+ checksum=$(md5 $watched_files | md5)
+ if [ "$checksum" != "$previous_checksum" ]; then
+ ldoc .
+ result=$?
+ if [ $result -ne 0 ]; then
+ echo -e "\033[0;31mldoc failed, exitcode: $result\033[0m"
+ echo
+ else
+ echo
+ echo "docs updated at: $(pwd)/docs/index.html"
+ echo -e "\033[1;33mwatching for changes...\033[0m"
+ echo
+ fi
+ fi
+ previous_checksum="$checksum"
+ sleep 1
+done
+
diff --git a/kong-versions/test9.9.9.3/kong/spec/require.lua b/kong-versions/test9.9.9.3/kong/spec/require.lua
new file mode 100644
index 00000000..c7d2ce83
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/require.lua
@@ -0,0 +1,100 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+-- Bundled from https://github.com/pygy/require.lua
+-- License MIT
+--- usage:
+-- require = require"require".require
+-- :o)
+
+local error, ipairs, newproxy, tostring, type
+ = error, ipairs, newproxy, tostring, type
+
+local t_concat = table.concat
+
+--- Helpers
+
+
+local function checkstring(s)
+ local t = type(s)
+ if t == "string" then
+ return s
+ elseif t == "number" then
+ return tostring(s)
+ else
+ error("bad argument #1 to 'require' (string expected, got "..t..")", 3)
+ end
+end
+
+--- for Lua 5.1
+
+local package, p_loaded, setmetatable = package, package.loaded, setmetatable
+
+local sentinel do
+ local function errhandler() error("the require() sentinel can't be indexed or updated", 2) end
+ sentinel = newproxy and newproxy() or setmetatable({}, {__index = errhandler, __newindex = errhandler, __metatable = false})
+end
+
+local function require51 (name)
+ name = checkstring(name)
+ if p_loaded[name] == sentinel then
+ error("loop or previous error loading module '"..name.."'", 2)
+ end
+
+ local module = p_loaded[name]
+ if module then return module end
+
+ local msg = {}
+ local loader
+ for _, searcher in ipairs(package.loaders) do
+ loader = searcher(name)
+ if type(loader) == "function" then break end
+ if type(loader) == "string" then
+ -- `loader` is actually an error message
+ msg[#msg + 1] = loader
+ end
+ loader = nil
+ end
+ if loader == nil then
+ error("module '" .. name .. "' not found: "..t_concat(msg), 2)
+ end
+ p_loaded[name] = sentinel
+ local res = loader(name)
+ if res ~= nil then
+ module = res
+ elseif p_loaded[name] == sentinel or not p_loaded[name] then
+ module = true
+ else
+ module = p_loaded[name]
+ end
+
+ p_loaded[name] = module
+ return module
+end
+
+local module = {
+ VERSION = "0.1.8",
+ require51 = require51,
+}
+
+if _VERSION == "Lua 5.1" then module.require = require51 end
+
+--- rerequire :o)
+
+for _, o in ipairs{
+ {"rerequiredefault", require},
+ {"rerequire", module.require},
+ {"rerequire51", require51},
+} do
+ local rereq, req = o[1], o[2]
+ module[rereq] = function(name)
+ p_loaded[name] = nil
+ return req(name)
+ end
+end
+
+return module
diff --git a/kong-versions/test9.9.9.3/kong/spec/upgrade_helpers.lua b/kong-versions/test9.9.9.3/kong/spec/upgrade_helpers.lua
new file mode 100644
index 00000000..88f3aef2
--- /dev/null
+++ b/kong-versions/test9.9.9.3/kong/spec/upgrade_helpers.lua
@@ -0,0 +1,267 @@
+-- This software is copyright Kong Inc. and its licensors.
+-- Use of the software is subject to the agreement between your organization
+-- and Kong Inc. If there is no such agreement, use is governed by and
+-- subject to the terms of the Kong Master Software License Agreement found
+-- at https://konghq.com/enterprisesoftwarelicense/.
+-- [ END OF LICENSE 0867164ffc95e54f04670b5169c09574bdbd9bba ]
+
+local say = require "say"
+local assert = require "luassert"
+
+local busted = require "busted"
+
+local conf_loader = require "kong.conf_loader"
+local DB = require "kong.db"
+local helpers = require "spec.helpers"
+local utils = require "kong.tools.utils"
+
+local conf = conf_loader()
+
+local function database_type()
+ return conf['database']
+end
+
+local function get_database()
+ local db = assert(DB.new(conf))
+ assert(db:init_connector())
+ return db
+end
+
+
+local function database_has_relation(state, arguments)
+ local table_name = arguments[1]
+ local schema = arguments[2] or "public"
+ local db = get_database()
+ local res, err
+ if database_type() == 'postgres' then
+ res, err = db.connector:query(string.format(
+ "select true"
+ .. " from pg_tables"
+ .. " where tablename = '%s'"
+ .. " and schemaname = '%s'",
+ table_name, schema))
+ else
+ return false
+ end
+ if err then
+ return false
+ end
+ return not(not(res[1]))
+end
+
+say:set("assertion.database_has_relation.positive", "Expected schema to have table %s")
+say:set("assertion.database_has_relation.negative", "Expected schema not to have table %s")
+assert:register("assertion", "database_has_relation", database_has_relation, "assertion.database_has_relation.positive", "assertion.database_has_relation.negative")
+
+
+local function database_has_trigger(state, arguments)
+ local trigger_name = arguments[1]
+ local db = get_database()
+ local res, err
+ if database_type() == 'postgres' then
+ res, err = db.connector:query(string.format(
+ "select true"
+ .. " from pg_trigger"
+ .. " where tgname = '%s'",
+ trigger_name))
+ else
+ return false
+ end
+ if err then
+ return false
+ end
+ return not(not(res[1]))
+end
+
+say:set("assertion.database_has_trigger.positive", "Expected database to have trigger %s")
+say:set("assertion.database_has_trigger.negative", "Expected database not to have trigger %s")
+assert:register("assertion", "database_has_trigger", database_has_trigger, "assertion.database_has_trigger.positive", "assertion.database_has_trigger.negative")
+
+
+local function table_has_column(state, arguments)
+ local table = arguments[1]
+ local column_name = arguments[2]
+ local postgres_type = arguments[3]
+ local db = get_database()
+ local res, err
+ if database_type() == 'postgres' then
+ res, err = db.connector:query(string.format(
+ "select true"
+ .. " from information_schema.columns"
+ .. " where table_schema = 'public'"
+ .. " and table_name = '%s'"
+ .. " and column_name = '%s'"
+ .. " and data_type = '%s'",
+ table, column_name, postgres_type))
+ else
+ return false
+ end
+ if err then
+ return false
+ end
+ return not(not(res[1]))
+end
+
+say:set("assertion.table_has_column.positive", "Expected table %s to have column %s with type %s")
+say:set("assertion.table_has_column.negative", "Expected table %s not to have column %s with type %s")
+assert:register("assertion", "table_has_column", table_has_column, "assertion.table_has_column.positive", "assertion.table_has_column.negative")
+
+local function table_has_index(state, arguments)
+ local table = arguments[1]
+ local index_name = arguments[2]
+ local db = get_database()
+ local res, err
+ if database_type() == 'postgres' then
+ res, err = db.connector:query(string.format(
+ "select true"
+ .. " from pg_indexes"
+ .. " where schemaname = 'public'"
+ .. " and tablename = '%s'"
+ .. " and indexname = '%s'",
+ table, index_name))
+ else
+ return false
+ end
+ if err then
+ return false
+ end
+ return not(not(res[1]))
+end
+
+say:set("assertion.table_has_index.positive", "Expected table %s to have index %s")
+say:set("assertion.table_has_index.negative", "Expected table %s not to index %s")
+assert:register("assertion", "table_has_index", table_has_index, "assertion.table_has_index.positive", "assertion.table_has_index.negative")
+
+local upstream_server_url = "http://" .. helpers.mock_upstream_host .. ":" .. helpers.mock_upstream_port .. "/"
+
+local function create_example_service()
+ local admin_client = assert(helpers.admin_client())
+ local res = assert(admin_client:send {
+ method = "POST",
+ path = "/services/",
+ body = {
+ name = "example-service",
+ url = upstream_server_url
+ },
+ headers = {
+ ["Content-Type"] = "application/json"
+ }
+ })
+ assert.res_status(201, res)
+ res = assert(admin_client:send {
+ method = "POST",
+ path = "/services/example-service/routes",
+ body = {
+ hosts = { "example.com" },
+ },
+ headers = {
+ ["Content-Type"] = "application/json"
+ }
+ })
+ assert.res_status(201, res)
+ admin_client:close()
+end
+
+local function send_proxy_get_request()
+ local proxy_client = assert(helpers.proxy_client())
+ local res = assert(proxy_client:send {
+ method = "GET",
+ headers = {
+ ["Host"] = "example.com",
+ },
+ path = "/",
+ })
+ local body = assert.res_status(200, res)
+ proxy_client:close()
+
+ return res, body
+end
+
+local function start_kong(options)
+ return helpers.start_kong(utils.table_merge({
+ database = database_type(),
+ dns_resolver = "",
+ proxy_listen = "0.0.0.0:9000",
+ admin_listen = "0.0.0.0:9001",
+ admin_ssl = false,
+ admin_gui_ssl = false,
+ nginx_conf = "spec/fixtures/custom_nginx.template",
+ }, options))
+end
+
+local function it_when(phase, phrase, f)
+ return busted.it(phrase .. " #" .. phase, f)
+end
+
+local function setup(f)
+ return busted.it("setting up kong #setup", f)
+end
+
+local function old_after_up(phrase, f)
+ return it_when("old_after_up", phrase, f)
+end
+
+local function new_after_up(phrase, f)
+ return it_when("new_after_up", phrase, f)
+end
+
+local function new_after_finish(phrase, f)
+ return it_when("new_after_finish", phrase, f)
+end
+
+local function all_phases(phrase, f)
+ return it_when("all_phases", phrase, f)
+end
+
+
+--- Get a Busted test handler for migration tests.
+--
+-- This convenience function determines the appropriate Busted handler
+-- (`busted.describe` or `busted.pending`) based on the "old Kong version"
+-- that migrations are running on and the specified version range.
+--
+-- @function get_busted_handler
+-- @param min_version The minimum Kong version (inclusive)
+-- @param max_version The maximum Kong version (inclusive)
+-- @return `busted.describe` if Kong's version is within the specified range,
+-- `busted.pending` otherwise.
+-- @usage
+-- local handler = get_busted_handler("3.3.0", "3.6.0")
+-- handler("some migration test", function() ... end)
+local get_busted_handler
+do
+ local function get_version_num(v1, v2)
+ if v2 then
+ assert(#v2 == #v1, string.format("different version format: %s and %s", v1, v2))
+ end
+ return assert(tonumber((v1:gsub("[%.x]", ""))), "invalid version: " .. v1)
+ end
+
+ function get_busted_handler(min_version, max_version)
+ local old_version_var = assert(os.getenv("OLD_KONG_VERSION"), "old version not set")
+ local old_version = string.match(old_version_var, "[^/]+$")
+
+ local old_version_num = get_version_num(old_version)
+ local min_v_num = min_version and get_version_num(min_version, old_version) or 0
+ local max_v_num = max_version and get_version_num(max_version, old_version) or math.huge
+
+ return old_version_num >= min_v_num and old_version_num <= max_v_num and busted.describe or busted.pending
+ end
+end
+
+return {
+ database_type = database_type,
+ get_database = get_database,
+ create_example_service = create_example_service,
+ send_proxy_get_request = send_proxy_get_request,
+ start_kong = start_kong,
+ stop_kong = helpers.stop_kong,
+ admin_client = helpers.admin_client,
+ proxy_client = helpers.proxy_client,
+ setup = setup,
+ old_after_up = old_after_up,
+ new_after_up = new_after_up,
+ new_after_finish = new_after_finish,
+ all_phases = all_phases,
+ get_busted_handler = get_busted_handler,
+}