feat(apisix): add Cloudron package
- Implements Apache APISIX packaging for Cloudron platform. - Includes Dockerfile, CloudronManifest.json, and start.sh. - Configured to use Cloudron's etcd addon. 🤖 Generated with Gemini CLI Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
384
CloudronPackages/APISIX/apisix-source/apisix/patch.lua
Normal file
384
CloudronPackages/APISIX/apisix-source/apisix/patch.lua
Normal file
@@ -0,0 +1,384 @@
|
||||
--
|
||||
-- Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
-- contributor license agreements. See the NOTICE file distributed with
|
||||
-- this work for additional information regarding copyright ownership.
|
||||
-- The ASF licenses this file to You 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.
|
||||
--
|
||||
local require = require
|
||||
require("resty.dns.resolver") -- preload dns resolver to prevent recursive patch
|
||||
local ipmatcher = require("resty.ipmatcher")
|
||||
local socket = require("socket")
|
||||
local unix_socket = require("socket.unix")
|
||||
local ssl = require("ssl")
|
||||
local ngx = ngx
|
||||
local get_phase = ngx.get_phase
|
||||
local ngx_socket = ngx.socket
|
||||
local original_tcp = ngx.socket.tcp
|
||||
local original_udp = ngx.socket.udp
|
||||
local concat_tab = table.concat
|
||||
local debug = debug
|
||||
local new_tab = require("table.new")
|
||||
local log = ngx.log
|
||||
local WARN = ngx.WARN
|
||||
local ipairs = ipairs
|
||||
local select = select
|
||||
local setmetatable = setmetatable
|
||||
local string = string
|
||||
local table = table
|
||||
local type = type
|
||||
local tonumber = tonumber
|
||||
|
||||
|
||||
local config_local
|
||||
local _M = {}
|
||||
|
||||
|
||||
local function get_local_conf()
|
||||
if not config_local then
|
||||
config_local = require("apisix.core.config_local")
|
||||
end
|
||||
|
||||
return config_local.local_conf()
|
||||
end
|
||||
|
||||
|
||||
local patch_tcp_socket
|
||||
do
|
||||
local old_tcp_sock_connect
|
||||
|
||||
local function new_tcp_sock_connect(sock, host, port, opts)
|
||||
local core_str = require("apisix.core.string")
|
||||
local resolver = require("apisix.core.resolver")
|
||||
|
||||
if host then
|
||||
if core_str.has_prefix(host, "unix:") then
|
||||
if not opts then
|
||||
-- workaround for https://github.com/openresty/lua-nginx-module/issues/860
|
||||
return old_tcp_sock_connect(sock, host)
|
||||
end
|
||||
|
||||
elseif not ipmatcher.parse_ipv4(host) and not ipmatcher.parse_ipv6(host) then
|
||||
local err
|
||||
host, err = resolver.parse_domain(host)
|
||||
if not host then
|
||||
return nil, "failed to parse domain: " .. err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return old_tcp_sock_connect(sock, host, port, opts)
|
||||
end
|
||||
|
||||
|
||||
function patch_tcp_socket(sock)
|
||||
if not old_tcp_sock_connect then
|
||||
old_tcp_sock_connect = sock.connect
|
||||
end
|
||||
|
||||
sock.connect = new_tcp_sock_connect
|
||||
return sock
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do -- `math.randomseed` patch
|
||||
-- `math.random` generates PRND(pseudo-random numbers) from the seed set by `math.randomseed`
|
||||
-- Many module libraries use `ngx.time` and `ngx.worker.pid` to generate seeds which may
|
||||
-- loss randomness in container env (where pids are identical, e.g. root pid is 1)
|
||||
-- Kubernetes may launch multi instance with deployment RS at the same time, `ngx.time` may
|
||||
-- get same return in the pods.
|
||||
-- Therefore, this global patch enforce entire framework to use
|
||||
-- the best-practice PRND generates.
|
||||
|
||||
local resty_random = require("resty.random")
|
||||
local math_randomseed = math.randomseed
|
||||
local seeded = {}
|
||||
|
||||
-- make linter happy
|
||||
-- luacheck: ignore
|
||||
math.randomseed = function()
|
||||
local worker_pid = ngx.worker.pid()
|
||||
|
||||
-- check seed mark
|
||||
if seeded[worker_pid] then
|
||||
log(ngx.DEBUG, debug.traceback("Random seed has been inited", 2))
|
||||
return
|
||||
end
|
||||
|
||||
-- generate randomseed
|
||||
-- chose 6 from APISIX's SIX, 256 ^ 6 should do the trick
|
||||
-- it shouldn't be large than 16 to prevent overflow.
|
||||
local random_bytes = resty_random.bytes(6)
|
||||
local t = {}
|
||||
|
||||
for i = 1, #random_bytes do
|
||||
t[i] = string.byte(random_bytes, i)
|
||||
end
|
||||
|
||||
local s = table.concat(t)
|
||||
|
||||
math_randomseed(tonumber(s))
|
||||
seeded[worker_pid] = true
|
||||
end
|
||||
end -- do
|
||||
|
||||
|
||||
local patch_udp_socket
|
||||
do
|
||||
local old_udp_sock_setpeername
|
||||
|
||||
local function new_udp_sock_setpeername(sock, host, port)
|
||||
local core_str = require("apisix.core.string")
|
||||
local resolver = require("apisix.core.resolver")
|
||||
|
||||
if host then
|
||||
if core_str.has_prefix(host, "unix:") then
|
||||
return old_udp_sock_setpeername(sock, host)
|
||||
end
|
||||
|
||||
if not ipmatcher.parse_ipv4(host) and not ipmatcher.parse_ipv6(host) then
|
||||
local err
|
||||
host, err = resolver.parse_domain(host)
|
||||
if not host then
|
||||
return nil, "failed to parse domain: " .. err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return old_udp_sock_setpeername(sock, host, port)
|
||||
end
|
||||
|
||||
|
||||
function patch_udp_socket(sock)
|
||||
if not old_udp_sock_setpeername then
|
||||
old_udp_sock_setpeername = sock.setpeername
|
||||
end
|
||||
|
||||
sock.setpeername = new_udp_sock_setpeername
|
||||
return sock
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function flatten(args)
|
||||
local buf = new_tab(#args, 0)
|
||||
for i, v in ipairs(args) do
|
||||
local ty = type(v)
|
||||
if ty == "table" then
|
||||
buf[i] = flatten(v)
|
||||
elseif ty == "boolean" then
|
||||
buf[i] = v and "true" or "false"
|
||||
elseif ty == "nil" then
|
||||
buf[i] = "nil"
|
||||
else
|
||||
buf[i] = v
|
||||
end
|
||||
end
|
||||
return concat_tab(buf)
|
||||
end
|
||||
|
||||
|
||||
local luasocket_wrapper = {
|
||||
connect = function (self, host, port)
|
||||
if not port then
|
||||
-- unix socket
|
||||
self.sock = unix_socket()
|
||||
if self.timeout then
|
||||
self.sock:settimeout(self.timeout)
|
||||
end
|
||||
|
||||
local path = host:sub(#("unix:") + 1)
|
||||
return self.sock:connect(path)
|
||||
end
|
||||
|
||||
if host:byte(1) == string.byte('[') then
|
||||
-- ipv6, form as '[::1]', remove '[' and ']'
|
||||
host = host:sub(2, -2)
|
||||
self.sock = self.tcp6
|
||||
else
|
||||
self.sock = self.tcp4
|
||||
end
|
||||
|
||||
return self.sock:connect(host, port)
|
||||
end,
|
||||
|
||||
send = function(self, ...)
|
||||
if select('#', ...) == 1 and type(select(1, ...)) == "string" then
|
||||
-- fast path
|
||||
return self.sock:send(...)
|
||||
end
|
||||
|
||||
-- luasocket's send only accepts a single string
|
||||
return self.sock:send(flatten({...}))
|
||||
end,
|
||||
|
||||
getreusedtimes = function ()
|
||||
return 0
|
||||
end,
|
||||
setkeepalive = function (self)
|
||||
self.sock:close()
|
||||
return 1
|
||||
end,
|
||||
|
||||
settimeout = function (self, time)
|
||||
if time then
|
||||
time = time / 1000
|
||||
end
|
||||
|
||||
self.timeout = time
|
||||
|
||||
return self.sock:settimeout(time)
|
||||
end,
|
||||
settimeouts = function (self, connect_time, read_time, write_time)
|
||||
connect_time = connect_time or 0
|
||||
read_time = read_time or 0
|
||||
write_time = write_time or 0
|
||||
|
||||
-- set the max one as the timeout
|
||||
local time = connect_time
|
||||
if time < read_time then
|
||||
time = read_time
|
||||
end
|
||||
if time < write_time then
|
||||
time = write_time
|
||||
end
|
||||
|
||||
if time > 0 then
|
||||
time = time / 1000
|
||||
else
|
||||
time = nil
|
||||
end
|
||||
|
||||
self.timeout = time
|
||||
|
||||
return self.sock:settimeout(time)
|
||||
end,
|
||||
|
||||
tlshandshake = function (self, options)
|
||||
local reused_session = options.reused_session
|
||||
local server_name = options.server_name
|
||||
local verify = options.verify
|
||||
local send_status_req = options.ocsp_status_req
|
||||
|
||||
if reused_session then
|
||||
log(WARN, "reused_session is not supported yet")
|
||||
end
|
||||
|
||||
if send_status_req then
|
||||
log(WARN, "send_status_req is not supported yet")
|
||||
end
|
||||
|
||||
local params = {
|
||||
mode = "client",
|
||||
protocol = "any",
|
||||
verify = verify and "peer" or "none",
|
||||
certificate = options.client_cert_path,
|
||||
key = options.client_priv_key_path,
|
||||
options = {
|
||||
"all",
|
||||
"no_sslv2",
|
||||
"no_sslv3",
|
||||
"no_tlsv1"
|
||||
}
|
||||
}
|
||||
|
||||
local local_conf, err = get_local_conf()
|
||||
if not local_conf then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local apisix_ssl = local_conf.apisix.ssl
|
||||
if apisix_ssl and apisix_ssl.ssl_trusted_certificate then
|
||||
params.cafile = apisix_ssl.ssl_trusted_certificate
|
||||
end
|
||||
|
||||
local sec_sock, err = ssl.wrap(self.sock, params)
|
||||
if not sec_sock then
|
||||
return false, err
|
||||
end
|
||||
|
||||
if server_name then
|
||||
sec_sock:sni(server_name)
|
||||
end
|
||||
|
||||
local success
|
||||
success, err = sec_sock:dohandshake()
|
||||
if not success then
|
||||
return false, err
|
||||
end
|
||||
|
||||
self.sock = sec_sock
|
||||
return true
|
||||
end,
|
||||
|
||||
sslhandshake = function (self, reused_session, server_name, verify, send_status_req)
|
||||
return self:tlshandshake({
|
||||
reused_session = reused_session,
|
||||
server_name = server_name,
|
||||
verify = verify,
|
||||
ocsp_status_req = send_status_req,
|
||||
})
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
local mt = {
|
||||
__index = function(self, key)
|
||||
local sock = self.sock
|
||||
local fn = luasocket_wrapper[key]
|
||||
if fn then
|
||||
self[key] = fn
|
||||
return fn
|
||||
end
|
||||
|
||||
local origin = sock[key]
|
||||
if type(origin) ~= "function" then
|
||||
return origin
|
||||
end
|
||||
|
||||
fn = function(_, ...)
|
||||
return origin(sock, ...)
|
||||
end
|
||||
|
||||
self[key] = fn
|
||||
return fn
|
||||
end
|
||||
}
|
||||
|
||||
local function luasocket_tcp()
|
||||
local sock = socket.tcp()
|
||||
local tcp4 = socket.tcp4()
|
||||
local tcp6 = socket.tcp6()
|
||||
return setmetatable({sock = sock, tcp4 = tcp4, tcp6 = tcp6}, mt)
|
||||
end
|
||||
|
||||
|
||||
function _M.patch()
|
||||
-- make linter happy
|
||||
-- luacheck: ignore
|
||||
ngx_socket.tcp = function ()
|
||||
local phase = get_phase()
|
||||
if phase ~= "init" and phase ~= "init_worker" then
|
||||
return patch_tcp_socket(original_tcp())
|
||||
end
|
||||
|
||||
return luasocket_tcp()
|
||||
end
|
||||
|
||||
ngx_socket.udp = function ()
|
||||
return patch_udp_socket(original_udp())
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return _M
|
Reference in New Issue
Block a user