mirror of
https://github.com/bunkerity/bunkerized-nginx
synced 2023-12-13 21:30:18 +01:00
refactoring and road to nginx 1.24.0
This commit is contained in:
parent
666b7a1bac
commit
928ed2d6ce
700 changed files with 39377 additions and 8510 deletions
|
@ -1,4 +1,4 @@
|
|||
FROM nginx:1.22.1-alpine AS builder
|
||||
FROM nginx:1.24.0-alpine AS builder
|
||||
|
||||
# Copy dependencies sources folder
|
||||
COPY src/deps /tmp/bunkerweb/deps
|
||||
|
@ -21,7 +21,7 @@ RUN apk add --no-cache --virtual .build-deps py3-pip && \
|
|||
pip install --no-cache-dir --require-hashes --target /usr/share/bunkerweb/deps/python -r /usr/share/bunkerweb/deps/requirements.txt && \
|
||||
apk del .build-deps
|
||||
|
||||
FROM nginx:1.22.1-alpine
|
||||
FROM nginx:1.24.0-alpine
|
||||
|
||||
# Copy dependencies
|
||||
COPY --from=builder /usr/share/bunkerweb /usr/share/bunkerweb
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
|
||||
local badbehavior = class("badbehavior", plugin)
|
||||
|
|
|
@ -71,7 +71,7 @@ function blacklist:init()
|
|||
if not ok then
|
||||
return self:ret(false, "can't store blacklist list into datastore : " .. err)
|
||||
end
|
||||
return self:ret(true, "successfully loaded " .. tostring(i) .. " bad IP/network/rDNS/ASN/User-Agent/URI")
|
||||
return self:ret(true, "successfully loaded " .. tostring(i) .. " IP/network/rDNS/ASN/User-Agent/URI")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -96,9 +96,9 @@ function blacklist:access()
|
|||
["UA"] = false
|
||||
}
|
||||
for k, v in pairs(checks) do
|
||||
local cached, err = self:is_in_cache(v)
|
||||
if not cached and err ~= "success" then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. err)
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not cached then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ok" then
|
||||
return self:ret(true, k + " is in cached blacklist (info : " .. cached .. ")", utils.get_deny_status())
|
||||
end
|
||||
|
@ -113,16 +113,16 @@ function blacklist:access()
|
|||
-- Perform checks
|
||||
for k, v in pairs(checks) do
|
||||
if not already_cached[k] then
|
||||
local blacklisted, err = self:is_blacklisted(k)
|
||||
if blacklisted == nil then
|
||||
local ok, blacklisted = self:is_blacklisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is blacklisted : " .. err)
|
||||
else
|
||||
local ok, err = self:add_to_cache(v, blacklisted or "ok")
|
||||
local ok, err = self:add_to_cache(v, blacklisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if blacklisted ~= "ok" then
|
||||
return self:ret(true, k + " is in cached blacklist (info : " .. blacklisted .. ")", utils.get_deny_status())
|
||||
return self:ret(true, k + " is blacklisted (info : " .. blacklisted .. ")", utils.get_deny_status())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -97,7 +97,7 @@ function country:preread()
|
|||
end
|
||||
|
||||
function country:is_in_cache(ip)
|
||||
local ok, data = cachestore:get("plugin_country_" .. ip)
|
||||
local ok, data = cachestore:get("plugin_country_cache_" .. ip)
|
||||
if not ok then then
|
||||
return false, data
|
||||
end
|
||||
|
@ -105,7 +105,7 @@ function country:is_in_cache(ip)
|
|||
end
|
||||
|
||||
function country:add_to_cache(ip, country, result)
|
||||
local ok, err = cachestore:set("plugin_country_" .. ip, cjson.encode({country = country, result = result}))
|
||||
local ok, err = cachestore:set("plugin_country_cache_" .. ip, cjson.encode({country = country, result = result}))
|
||||
if not ok then then
|
||||
return false, err
|
||||
end
|
||||
|
|
|
@ -25,21 +25,15 @@ location = {{ page }} {
|
|||
internal;
|
||||
modsecurity off;
|
||||
default_type 'text/html';
|
||||
root /usr/share/bunkerweb/core/files;
|
||||
content_by_lua_block {
|
||||
local logger = require "bunkerweb.logger"
|
||||
local errors = require "errors.errors"
|
||||
local html, err
|
||||
logger:new("errors")
|
||||
errors:new()
|
||||
if ngx.status == 200 then
|
||||
html, err = errors:error_html(tostring(405))
|
||||
errors:render_template(tostring(405))
|
||||
else
|
||||
html, err = errors:error_html(tostring(ngx.status))
|
||||
end
|
||||
if not html then
|
||||
logger:log(ngx.ERR, "error while computing HTML error template for {{ intercepted_error_code }} : " .. err)
|
||||
else
|
||||
ngx.say(html)
|
||||
errors:render_template(tostring(ngx.status))
|
||||
end
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ local plugin = require "bunkerweb.plugin"
|
|||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local cjson = require "cjson"
|
||||
local template = require "resty.template"
|
||||
|
||||
local errors = class("errors", plugin)
|
||||
|
||||
|
@ -12,94 +13,68 @@ function errors:new()
|
|||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
return true, "success"
|
||||
end
|
||||
|
||||
function errors:init()
|
||||
-- Save default errors into datastore
|
||||
local default_errors = {
|
||||
-- Default error texts
|
||||
self.default_errors = {
|
||||
["400"] = {
|
||||
body1 = "Bad Request",
|
||||
body2 = "The server did not understand the request."
|
||||
title = "Bad Request",
|
||||
text = "The server did not understand the request."
|
||||
},
|
||||
["401"] = {
|
||||
body1 = "Not Authorized",
|
||||
body2 = "Valid authentication credentials needed for the target resource."
|
||||
title = "Not Authorized",
|
||||
text = "Valid authentication credentials needed for the target resource."
|
||||
},
|
||||
["403"] = {
|
||||
body1 = "Forbidden",
|
||||
body2 = "Access is forbidden to the requested page."
|
||||
title = "Forbidden",
|
||||
text = "Access is forbidden to the requested page."
|
||||
},
|
||||
["404"] = {
|
||||
body1 = "Not Found",
|
||||
body2 = "The server cannot find the requested page."
|
||||
title = "Not Found",
|
||||
text = "The server cannot find the requested page."
|
||||
},
|
||||
["405"] = {
|
||||
body1 = "Method Not Allowed",
|
||||
body2 = "The method specified in the request is not allowed."
|
||||
title = "Method Not Allowed",
|
||||
text = "The method specified in the request is not allowed."
|
||||
},
|
||||
["413"] = {
|
||||
body1 = "Request Entity Too Large",
|
||||
body2 = "The server will not accept the request, because the request entity is too large."
|
||||
title = "Request Entity Too Large",
|
||||
text = "The server will not accept the request, because the request entity is too large."
|
||||
},
|
||||
["429"] = {
|
||||
body1 = "Too Many Requests",
|
||||
body2 = "Too many requests sent in a given amount of time, try again later."
|
||||
title = "Too Many Requests",
|
||||
text = "Too many requests sent in a given amount of time, try again later."
|
||||
},
|
||||
["500"] = {
|
||||
body1 = "Internal Server Error",
|
||||
body2 = "The request was not completed. The server met an unexpected condition."
|
||||
title = "Internal Server Error",
|
||||
text = "The request was not completed. The server met an unexpected condition."
|
||||
},
|
||||
["501"] = {
|
||||
body1 = "Not Implemented",
|
||||
body2 = "The request was not completed. The server did not support the functionality required."
|
||||
title = "Not Implemented",
|
||||
text = "The request was not completed. The server did not support the functionality required."
|
||||
},
|
||||
["502"] = {
|
||||
body1 = "Bad Gateway",
|
||||
body2 = "The request was not completed. The server received an invalid response from the upstream server."
|
||||
title = "Bad Gateway",
|
||||
text = "The request was not completed. The server received an invalid response from the upstream server."
|
||||
},
|
||||
["503"] = {
|
||||
body1 = "Service Unavailable",
|
||||
body2 = "The request was not completed. The server is temporarily overloading or down."
|
||||
title = "Service Unavailable",
|
||||
text = "The request was not completed. The server is temporarily overloading or down."
|
||||
},
|
||||
["504"] = {
|
||||
body1 = "Gateway Timeout",
|
||||
body2 = "The gateway has timed out."
|
||||
title = "Gateway Timeout",
|
||||
text = "The gateway has timed out."
|
||||
}
|
||||
}
|
||||
local ok, err = datastore:set("plugin_errors_default_errors", cjson.encode(default_errors))
|
||||
if not ok then
|
||||
return self:ret(false, "can't save default errors to datastore : " .. err)
|
||||
end
|
||||
-- Save generic template into datastore
|
||||
local f, err = io.open("/usr/share/bunkerweb/core/errors/files/error.html", "r")
|
||||
if not f then
|
||||
return self:ret(false, "can't open error.html : " .. err)
|
||||
end
|
||||
local template = f:read("*all")
|
||||
f:close()
|
||||
local ok, err = datastore:set("plugin_errors_template", template)
|
||||
if not ok then
|
||||
return false, "can't save error.html to datastore : " .. err
|
||||
end
|
||||
return true, "success"
|
||||
end
|
||||
|
||||
function errors:error_html(code)
|
||||
-- Load default errors texts
|
||||
local default_errors, err = datastore:get("plugin_errors_default_errors")
|
||||
if not default_errors then
|
||||
return false, "can't get default errors from datastore : " .. err
|
||||
end
|
||||
default_errors = cjson.decode(default_errors)
|
||||
-- Load template
|
||||
local template, err = datastore:get("plugin_errors_template")
|
||||
if not template then
|
||||
return false, "can't get template from datastore : " .. err
|
||||
end
|
||||
-- Compute template
|
||||
return template:format(code .. " - " .. default_errors[code].body1, code, default_errors[code].body1,
|
||||
default_errors[code].body2), "success"
|
||||
function errors:render_template(code)
|
||||
-- Render template
|
||||
template.render("error.html", {
|
||||
title = code .. " - " .. self.default_errors[code].title,
|
||||
error_title = self.default_errors[code].title,
|
||||
error_code = code,
|
||||
error_text = self.default_errors[code].text
|
||||
})
|
||||
end
|
||||
|
||||
return errors
|
|
@ -3,29 +3,29 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>%s</title>
|
||||
<title>{{title}}</title>
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml, %%3Csvg version='1.0' xmlns='http://www.w3.org/2000/svg' width='96.000000pt' height='96.000000pt' viewBox='0 0 96.000000 96.000000' preserveAspectRatio='xMidYMid meet'%%3E%%3Cg transform='translate(0.000000,96.000000) scale(0.100000,-0.100000)'%%0Afill='%%23085577' stroke='none'%%3E%%3Cpath d='M535 863 c-22 -2 -139 -17 -260 -34 -228 -31 -267 -43 -272 -85 -2%%0A-10 23 -181 55 -379 l57 -360 400 0 400 0 20 40 c16 31 20 59 19 125 -1 100%%0A-24 165 -73 199 -41 29 -46 57 -22 111 30 67 29 188 -3 256 -13 28 -37 60 -53%%0A72 -55 39 -169 62 -268 55z m-15 -348 c30 -16 60 -61 60 -90 0 -10 -8 -33 -17%%0A-52 -16 -34 -16 -41 0 -116 9 -44 15 -82 12 -85 -6 -7 -92 -21 -131 -21 l-31%%0A-1 -6 85 c-4 75 -8 89 -31 112 -20 20 -26 36 -26 70 0 38 5 50 34 79 39 39 86%%0A45 136 19z'/%%3E%%3C/g%%3E%%3C/svg%%3E"
|
||||
href="data:image/svg+xml, %3Csvg version='1.0' xmlns='http://www.w3.org/2000/svg' width='96.000000pt' height='96.000000pt' viewBox='0 0 96.000000 96.000000' preserveAspectRatio='xMidYMid meet'%3E%3Cg transform='translate(0.000000,96.000000) scale(0.100000,-0.100000)'%0Afill='%23085577' stroke='none'%3E%3Cpath d='M535 863 c-22 -2 -139 -17 -260 -34 -228 -31 -267 -43 -272 -85 -2%0A-10 23 -181 55 -379 l57 -360 400 0 400 0 20 40 c16 31 20 59 19 125 -1 100%0A-24 165 -73 199 -41 29 -46 57 -22 111 30 67 29 188 -3 256 -13 28 -37 60 -53%0A72 -55 39 -169 62 -268 55z m-15 -348 c30 -16 60 -61 60 -90 0 -10 -8 -33 -17%0A-52 -16 -34 -16 -41 0 -116 9 -44 15 -82 12 -85 -6 -7 -92 -21 -131 -21 l-31%0A-1 -6 85 c-4 75 -8 89 -31 112 -20 20 -26 36 -26 70 0 38 5 50 34 79 39 39 86%0A45 136 19z'/%3E%3C/g%3E%3C/svg%3E"
|
||||
type="image/svg+xml"
|
||||
/>
|
||||
<style type="text/css">
|
||||
body,
|
||||
html {
|
||||
width: 100%%;
|
||||
height: 100%%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #125678;
|
||||
}
|
||||
body {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
min-height: 100%%;
|
||||
min-height: 100%;
|
||||
display: table;
|
||||
font-family: "Open Sans", Arial, sans-serif;
|
||||
margin: 0;
|
||||
-ms-text-size-adjust: 100%%;
|
||||
-webkit-text-size-adjust: 100%%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
h1 {
|
||||
display: flex;
|
||||
|
@ -63,7 +63,7 @@
|
|||
}
|
||||
footer {
|
||||
position: fixed;
|
||||
width: 100%%;
|
||||
width: 100%;
|
||||
letter-spacing: 1px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
|
@ -89,8 +89,8 @@
|
|||
<body>
|
||||
<div class="cover">
|
||||
<div class="message">
|
||||
<h1>%s<small>%s</small></h1>
|
||||
<p class="lead">%s</p>
|
||||
<h1>{{error_title}}<small>{{error_code}}</small></h1>
|
||||
<p class="lead">{{error_text}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
|
|
|
@ -1,29 +1,65 @@
|
|||
local _M = {}
|
||||
_M.__index = _M
|
||||
|
||||
local utils = require "utils"
|
||||
local datastore = require "datastore"
|
||||
local logger = require "logger"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
function _M.new()
|
||||
local self = setmetatable({}, _M)
|
||||
return self, nil
|
||||
local limit = class("limit", plugin)
|
||||
|
||||
function limit:new()
|
||||
-- Call parent new
|
||||
local ok, err = plugin.new(self, "limit")
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
-- Check if redis is enabled
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
return false, err
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
-- Load rules if needed
|
||||
if ngx.get_phase() == "access" then
|
||||
if self.variables["USE_LIMIT_REQ"] == "yes" then
|
||||
-- Get all rules from datastore
|
||||
local limited = false
|
||||
local all_rules, err = datastore:get("plugin_limit_rules")
|
||||
if not all_rules then
|
||||
return false, err
|
||||
end
|
||||
all_rules = cjson.decode(all_rules)
|
||||
self.rules = {}
|
||||
-- Extract global rules
|
||||
if all_rules.global then
|
||||
for k, v in pairs(all_rules.global) do
|
||||
self.rules[k] = v
|
||||
end
|
||||
end
|
||||
-- Extract and overwrite if needed server rules
|
||||
if all_rules[ngx.var.server_name] then
|
||||
for k, v in pairs(all_rules[ngx.var.server_name]) do
|
||||
self.rules[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true, "success"
|
||||
end
|
||||
|
||||
function _M:init()
|
||||
function limit:init()
|
||||
-- Check if init is needed
|
||||
local init_needed, err = utils.has_variable("USE_LIMIT_REQ", "yes")
|
||||
if init_needed == nil then
|
||||
return false, err
|
||||
return self:ret(false, err)
|
||||
end
|
||||
if not init_needed then
|
||||
return true, "no service uses Limit for requests, skipping init"
|
||||
return self:ret(true, "no service uses Limit for requests, skipping init")
|
||||
end
|
||||
-- Get variables
|
||||
local variables, err = utils.get_multiple_variables({"LIMIT_REQ_URL", "LIMIT_REQ_RATE"})
|
||||
if variables == nil then
|
||||
return false, err
|
||||
return self:ret(false, err)
|
||||
end
|
||||
-- Store URLs and rates
|
||||
local data = {}
|
||||
|
@ -43,73 +79,142 @@ function _M:init()
|
|||
end
|
||||
local ok, err = datastore:set("plugin_limit_rules", cjson.encode(data))
|
||||
if not ok then
|
||||
return false, err
|
||||
return self:ret(false, err)
|
||||
end
|
||||
return true, "successfully loaded " .. tostring(i) .. " limit rules for requests"
|
||||
return self:ret(true, "successfully loaded " .. tostring(i) .. " limit rules for requests")
|
||||
end
|
||||
|
||||
function _M:access()
|
||||
function limit:access()
|
||||
-- Check if we are whitelisted
|
||||
if ngx.var.is_whitelisted == "yes" then
|
||||
return self:ret(true, "client is whitelisted")
|
||||
end
|
||||
-- Check if access is needed
|
||||
local access_needed, err = utils.get_variable("USE_LIMIT_REQ")
|
||||
if access_needed == nil then
|
||||
return false, err, nil, nil
|
||||
if self.variables["USE_LIMIT_REQ"] ~= "yes" then
|
||||
return self:ret(true, "limit req is disabled")
|
||||
end
|
||||
if access_needed ~= "yes" then
|
||||
return true, "Limit for request not activated", nil, nil
|
||||
end
|
||||
|
||||
-- Don't go further if URL is not limited
|
||||
local limited = false
|
||||
local all_rules, err = datastore:get("plugin_limit_rules")
|
||||
if not all_rules then
|
||||
return false, err, nil, nil
|
||||
end
|
||||
all_rules = cjson.decode(all_rules)
|
||||
local limited = false
|
||||
local rate = ""
|
||||
if not limited and all_rules[ngx.var.server_name] then
|
||||
for k, v in pairs(all_rules[ngx.var.server_name]) do
|
||||
if ngx.var.uri:match(k) and k ~= "/" then
|
||||
limited = true
|
||||
rate = all_rules[ngx.var.server_name][k]
|
||||
-- Check if URI is limited
|
||||
local rate = nil
|
||||
local uri = nil
|
||||
for k, v in pairs(self.rules) do
|
||||
if k ~= "/" and ngx.var.uri:match(k) then
|
||||
rate = v
|
||||
uri = k
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if all_rules.global and not limited then
|
||||
for k, v in pairs(all_rules.global) do
|
||||
if ngx.var.uri:match(k) and k ~= "/" then
|
||||
limited = true
|
||||
rate = all_rules.global[k]
|
||||
break
|
||||
if not rate then
|
||||
if self.rules["/"] then
|
||||
rate = self.rules["/"]
|
||||
uri = "/"
|
||||
else
|
||||
return self:ret(true, "no rule for " .. ngx.var.uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not limited then
|
||||
if all_rules[ngx.var.server_name] and all_rules[ngx.var.server_name]["/"] then
|
||||
limited = true
|
||||
rate = all_rules[ngx.var.server_name]["/"]
|
||||
elseif all_rules.global and all_rules.global["/"] then
|
||||
limited = true
|
||||
rate = all_rules.global["/"]
|
||||
end
|
||||
if not limited then
|
||||
return true, "URL " .. ngx.var.uri .. " is not limited by a rule, skipping check", nil, nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Get the rate
|
||||
-- Check if limit is reached
|
||||
local _, _, rate_max, rate_time = rate:find("(%d+)r/(.)")
|
||||
|
||||
-- Get current requests timestamps
|
||||
local requests, err = datastore:get("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri)
|
||||
if not requests and err ~= "not found" then
|
||||
return false, err, nil, nil
|
||||
elseif err == "not found" then
|
||||
requests = "{}"
|
||||
local limited, err, current_rate = self:limit_req(tonumber(rate_max), rate_time)
|
||||
if limited == nil then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
-- Limit reached
|
||||
if limited then
|
||||
return return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is limited for URL " .. ngx.var.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")", ngx.HTTP_TOO_MANY_REQUESTS)
|
||||
end
|
||||
-- Limit not reached
|
||||
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is not limited for URL " .. ngx.var.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")")
|
||||
end
|
||||
end
|
||||
|
||||
function limit:limit_req(rate_max, rate_time)
|
||||
local timestamps = nil
|
||||
-- Redis case
|
||||
if self.use_redis then
|
||||
local redis_timestamps, err = self:limit_req_redis(rate_max, rate_time)
|
||||
if redis_timestamps == nil then
|
||||
self.logger:log(ngx.ERR, "limit_req_redis failed, falling back to local : " .. err)
|
||||
else
|
||||
timestamps = redis_timestamps
|
||||
-- Save the new timestamps
|
||||
local ok, err = datastore:set("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(timestamps), delay)
|
||||
if not ok then
|
||||
return nil, "can't update timestamps : " .. err
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Local case (or fallback)
|
||||
if timestamps == nil then
|
||||
local local_timestamps, err = self:limit_req_local(rate_max, rate_time)
|
||||
if local_timestamps == nil then
|
||||
return nil, "limit_req_local failed : " .. err
|
||||
end
|
||||
timestamps = local_timestamps
|
||||
end
|
||||
if #timestamps > rate_max then
|
||||
return true, "success - limited", #timestamps
|
||||
end
|
||||
return false, "success - not limited", #timestamps
|
||||
end
|
||||
|
||||
function limit:limit_req_local(rate_max, rate_time)
|
||||
-- Get timestamps
|
||||
local timestamps, err = datastore:get("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri)
|
||||
if not timestamps and err ~= "not found" then
|
||||
return nil, err
|
||||
elseif err == "not found" then
|
||||
timestamps = "{}"
|
||||
end
|
||||
timestamps = cjson.decode(timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated, new_timestamps, delay = self:limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
-- Save new timestamps if needed
|
||||
if updated then
|
||||
local ok, err = datastore:set("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(timestamps), delay)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
return new_timestamps, "success"
|
||||
end
|
||||
|
||||
function limit:limit_req_redis(rate_max, rate_time)
|
||||
-- Connect to server
|
||||
local cstore, err = clusterstore:new()
|
||||
if not cstore then
|
||||
return nil, err
|
||||
end
|
||||
local ok, err = clusterstore:connect()
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
-- Get timestamps
|
||||
local timestamps, err = clusterstore:call("get", "limit_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri)
|
||||
if err then
|
||||
clusterstore:close()
|
||||
return nil, err
|
||||
end
|
||||
if timestamps then
|
||||
timestamps = cjson.decode(timestamps)
|
||||
else
|
||||
timestamps = {}
|
||||
end
|
||||
-- Compute new timestamps
|
||||
local updated, new_timestamps, delay = self:limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
-- Save new timestamps if needed
|
||||
if updated then
|
||||
local ok, err = clusterstore:call("set", "limit_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(new_timestamps), "EX", delay)
|
||||
if not ok then
|
||||
clusterstore:close()
|
||||
return nil, err
|
||||
end
|
||||
end
|
||||
lusterstore:close()
|
||||
return new_timestamps, "success"
|
||||
end
|
||||
|
||||
function limit:limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated = false
|
||||
local new_timestamps = {}
|
||||
local current_timestamp = os.time(os.date("!*t"))
|
||||
local delay = 0
|
||||
|
@ -122,29 +227,20 @@ function _M:access()
|
|||
elseif rate_time == "d" then
|
||||
delay = 86400
|
||||
end
|
||||
for i, timestamp in ipairs(cjson.decode(requests)) do
|
||||
-- Keep only timestamp within the delay
|
||||
for i, timestamp in ipairs(timestamps) do
|
||||
if current_timestamp - timestamp <= delay then
|
||||
table.insert(new_timestamps, timestamp)
|
||||
else
|
||||
updated = true
|
||||
end
|
||||
end
|
||||
-- Only insert the new timestamp if client is not limited already to avoid infinite insert
|
||||
if #new_timestamps <= tonumber(rate_max) then
|
||||
if #new_timestamps <= rate_max then
|
||||
table.insert(new_timestamps, current_timestamp)
|
||||
updated = true
|
||||
end
|
||||
|
||||
-- Save the new timestamps
|
||||
local ok, err = datastore:set("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(new_timestamps), delay)
|
||||
if not ok then
|
||||
return false, "can't update timestamps : " .. err, nil, nil
|
||||
end
|
||||
|
||||
-- Deny if the rate is higher than the one defined in rule
|
||||
if #new_timestamps > tonumber(rate_max) then
|
||||
return true, "client IP " .. ngx.var.remote_addr .. " is limited for URL " .. ngx.var.uri .. " (current rate = " .. tostring(#new_timestamps) .. "r/" .. rate_time .. " and max rate = " .. rate .. ")", true, ngx.HTTP_TOO_MANY_REQUESTS
|
||||
end
|
||||
|
||||
-- Limit not reached
|
||||
return true, "client IP " .. ngx.var.remote_addr .. " is not limited for URL " .. ngx.var.uri .. " (current rate = " .. tostring(#new_timestamps) .. "r/" .. rate_time .. " and max rate = " .. rate .. ")", nil, nil
|
||||
return updated, new_timestamps, delay
|
||||
end
|
||||
|
||||
return _M
|
||||
return limit
|
|
@ -1,65 +1,58 @@
|
|||
local _M = {}
|
||||
_M.__index = _M
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
|
||||
local utils = require "utils"
|
||||
local datastore = require "datastore"
|
||||
local logger = require "logger"
|
||||
local cjson = require "cjson"
|
||||
local reversescan = class("reversescan", plugin)
|
||||
|
||||
function _M.new()
|
||||
local self = setmetatable({}, _M)
|
||||
return self, nil
|
||||
function reversescan:new()
|
||||
-- Call parent new
|
||||
local ok, err = plugin.new(self, "reversescan")
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
return false, err
|
||||
end
|
||||
cachestore:new(use_redis)
|
||||
return true, "success"
|
||||
end
|
||||
|
||||
function _M:access()
|
||||
function reversescan:access()
|
||||
-- Check if access is needed
|
||||
local access_needed, err = utils.get_variable("USE_REVERSE_SCAN")
|
||||
if access_needed == nil then
|
||||
return false, "can't get USE_REVERSE_SCAN setting from datastore : " .. err, nil, nil
|
||||
end
|
||||
if access_needed ~= "yes" then
|
||||
return true, "reverse scan not activated", nil, nil
|
||||
end
|
||||
-- Get ports
|
||||
local ports, err = utils.get_variable("REVERSE_SCAN_PORTS")
|
||||
if ports == nil then
|
||||
return false, "can't get REVERSE_SCAN_PORTS setting from datastore : " .. err, nil, nil
|
||||
end
|
||||
if ports == "" then
|
||||
return true, "no port defined", nil, nil
|
||||
end
|
||||
-- Get timeout
|
||||
local timeout, err = utils.get_variable("REVERSE_SCAN_TIMEOUT")
|
||||
if timeout == nil then
|
||||
return false, "can't get REVERSE_SCAN_TIMEOUT setting from datastore : " .. err, nil, nil
|
||||
if self.variables["USE_REVERSE_SCAN"] ~= "yes" then
|
||||
return self:ret(true, "reverse scan not activated")
|
||||
end
|
||||
-- Loop on ports
|
||||
for port in ports:gmatch("%S+") do
|
||||
for port in self.variables["REVERSE_SCAN_PORTS"]:gmatch("%S+") do
|
||||
-- Check if the scan is already cached
|
||||
local cached, err = self:is_in_cache(ngx.var.remote_addr .. ":" .. port)
|
||||
if cached == nil then
|
||||
return false, "error getting cache from datastore : " .. err, nil, nil
|
||||
return self:ret(false, "error getting cache from datastore : " .. err)
|
||||
end
|
||||
if cached == "open" then
|
||||
return true, "port " .. port .. " is opened for IP " .. ngx.var.remote_addr, true, utils.get_deny_status()
|
||||
return self:ret(true, "port " .. port .. " is opened for IP " .. ngx.var.remote_addr, utils.get_deny_status())
|
||||
elseif not cached then
|
||||
-- Do the scan
|
||||
local res, err = self:scan(ngx.var.remote_addr, tonumber(port), tonumber(timeout))
|
||||
local res, err = self:scan(ngx.var.remote_addr, tonumber(port), tonumber(self.variables["REVERSE_SCAN_TIMEOUT"]))
|
||||
-- Cache the result
|
||||
local ok, err = self:add_to_cache(ngx.var.remote_addr .. ":" .. port, res)
|
||||
if not ok then
|
||||
return false, "error updating cache from datastore : " .. err, nil, nil
|
||||
return self:ret(false, "error updating cache from datastore : " .. err)
|
||||
end
|
||||
-- Deny request if port is open
|
||||
if res == "open" then
|
||||
return true, "port " .. port .. " is opened for IP " .. ngx.var.remote_addr, true, utils.get_deny_status()
|
||||
return self:ret(true, "port " .. port .. " is opened for IP " .. ngx.var.remote_addr, utils.get_deny_status())
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, "no port open for IP " .. ngx.var.remote_addr, nil, nil
|
||||
-- No port opened
|
||||
return self:ret(true, "no port open for IP " .. ngx.var.remote_addr)
|
||||
end
|
||||
|
||||
function _M:scan(ip, port, timeout)
|
||||
function reversescan:scan(ip, port, timeout)
|
||||
local tcpsock = ngx.socket.tcp()
|
||||
tcpsock:settimeout(timeout)
|
||||
local ok, err = tcpsock:connect(ip, port)
|
||||
|
@ -70,24 +63,20 @@ function _M:scan(ip, port, timeout)
|
|||
return "open", nil
|
||||
end
|
||||
|
||||
function _M:is_in_cache(ele)
|
||||
local res, err = datastore:get("plugin_reversescan_" .. ele)
|
||||
if not res then
|
||||
if err == "not found" then
|
||||
return false, nil
|
||||
function reversescan:is_in_cache(ip_port)
|
||||
local ok, data = cachestore:get("plugin_reversescan_cache_" .. ip_port)
|
||||
if not ok then then
|
||||
return false, data
|
||||
end
|
||||
return nil, err
|
||||
end
|
||||
return true, res
|
||||
return true, data
|
||||
end
|
||||
|
||||
function _M:add_to_cache(ele, value)
|
||||
local ok, err = datastore:set("plugin_reversescan_" .. ele, value, 86400)
|
||||
if not ok then
|
||||
function reversescan:add_to_cache(ip_port, value)
|
||||
local ok, err = cachestore:set("plugin_reversescan_cache_" .. ip_port, value)
|
||||
if not ok then then
|
||||
return false, err
|
||||
end
|
||||
return true, nil
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
return _M
|
||||
return reversescan
|
|
@ -50,7 +50,7 @@ function _M:init()
|
|||
else
|
||||
config.storage = "redis"
|
||||
config.redis = {
|
||||
prefix = "session_",
|
||||
prefix = "sessions_",
|
||||
connect_timeout = tonumber(vars["REDIS_TIMEOUT"]),
|
||||
send_timeout = tonumber(vars["REDIS_TIMEOUT"]),
|
||||
read_timeout = tonumber(vars["REDIS_TIMEOUT"]),
|
||||
|
|
|
@ -1,27 +1,48 @@
|
|||
local _M = {}
|
||||
_M.__index = _M
|
||||
|
||||
local utils = require "utils"
|
||||
local datastore = require "datastore"
|
||||
local logger = require "logger"
|
||||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local cjson = require "cjson"
|
||||
local ipmatcher = require "resty.ipmatcher"
|
||||
local env = require "resty.env"
|
||||
|
||||
function _M.new()
|
||||
local self = setmetatable({}, _M)
|
||||
return self, nil
|
||||
end
|
||||
local whitelist = class("whitelist", plugin)
|
||||
|
||||
function _M:init()
|
||||
function whitelist:new()
|
||||
-- Call parent new
|
||||
local ok, err = plugin.new(self, "whitelist")
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
-- Check if redis is enabled
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
return false, err
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
-- Check if init is needed
|
||||
if ngx.get_phase() == "init" then
|
||||
local init_needed, err = utils.has_variable("USE_WHITELIST", "yes")
|
||||
if init_needed == nil then
|
||||
return false, err
|
||||
end
|
||||
if not init_needed then
|
||||
return true, "no service uses Whitelist, skipping init"
|
||||
self.init_needed = init_needed
|
||||
-- Decode lists
|
||||
else
|
||||
local lists, err = datastore:get("plugin_whitelist_lists")
|
||||
if not lists then
|
||||
return false, err
|
||||
end
|
||||
self.lists = cjson.decode(lists)
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
cachestore:new(use_redis)
|
||||
return true, "success"
|
||||
end
|
||||
|
||||
function whitelist:init()
|
||||
if self.init_needed then
|
||||
-- Read whitelists
|
||||
local whitelists = {
|
||||
["IP"] = {},
|
||||
|
@ -42,379 +63,208 @@ function _M:init()
|
|||
end
|
||||
end
|
||||
-- Load them into datastore
|
||||
local ok, err = datastore:set("plugin_whitelist_list", cjson.encode(whitelists))
|
||||
local ok, err = datastore:set("plugin_whitelist_lists", cjson.encode(whitelists))
|
||||
if not ok then
|
||||
return false, "can't store Whitelist list into datastore : " .. err
|
||||
return self:ret(false, "can't store whitelist list into datastore : " .. err)
|
||||
end
|
||||
return self:ret(true, "successfully loaded " .. tostring(i) .. " IP/network/rDNS/ASN/User-Agent/URI")
|
||||
end
|
||||
return true, "successfully loaded " .. tostring(i) .. " whitelisted IP/network/rDNS/ASN/User-Agent/URI"
|
||||
end
|
||||
|
||||
function _M:set()
|
||||
|
||||
function whitelist:set()
|
||||
-- Set default value
|
||||
ngx.var.is_whitelisted = "no"
|
||||
env.set("is_whitelisted", "no")
|
||||
-- Check if set is needed
|
||||
if self.variables["USE_WHITELIST"] ~= "yes" then
|
||||
return self:ret(true, "whitelist not activated")
|
||||
end
|
||||
-- Check cache
|
||||
local whitelisted, err = self:check_cache()
|
||||
if whitelisted == nil then
|
||||
return self:ret(false, err)
|
||||
elseif whitelisted then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return self:ret(true, err)
|
||||
end
|
||||
return self:ret(true, "not in whitelist cache")
|
||||
end
|
||||
|
||||
function whitelist:access()
|
||||
-- Check if access is needed
|
||||
local set_needed, err = utils.get_variable("USE_WHITELIST")
|
||||
if set_needed == nil then
|
||||
if self.variables["USE_WHITELIST"] ~= "yes" then
|
||||
return self:ret(true, "whitelist not activated")
|
||||
end
|
||||
-- Check cache
|
||||
local whitelisted, err, already_cached = self:check_cache()
|
||||
if whitelisted == nil then
|
||||
return self:ret(false, err)
|
||||
elseif whitelisted then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return self:ret(true, err, ngx.OK)
|
||||
end
|
||||
-- Perform checks
|
||||
for k, v in pairs(already_cached) do
|
||||
if not already_cached[k] then
|
||||
local ok, whitelisted = self:is_whitelisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is whitelisted : " .. err)
|
||||
else
|
||||
local ok, err = self:add_to_cache(v, whitelisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if whitelisted ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return self:ret(true, k + " is whitelisted (info : " .. whitelisted .. ")", ngx.OK)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Not whitelisted
|
||||
return self:ret(true, "not whitelisted")
|
||||
end
|
||||
|
||||
function whitelist:preread()
|
||||
return self:access()
|
||||
end
|
||||
|
||||
function whitelist:check_cache()
|
||||
-- Check the caches
|
||||
local checks = {
|
||||
["IP"] = "ip" .. ngx.var.remote_addr
|
||||
}
|
||||
if ngx.var.http_user_agent then
|
||||
checks["UA"] = "ua" .. ngx.var.http_user_agent
|
||||
end
|
||||
if ngx.var.uri then
|
||||
checks["URI"] = "uri" .. ngx.var.uri
|
||||
end
|
||||
local already_cached = {
|
||||
["IP"] = false,
|
||||
["URI"] = false,
|
||||
["UA"] = false
|
||||
}
|
||||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ok" then
|
||||
return true, k + " is in cached whitelist (info : " .. cached .. ")"
|
||||
end
|
||||
if cached then
|
||||
already_cached[k] = true
|
||||
end
|
||||
end
|
||||
-- Check lists
|
||||
if not self.lists then
|
||||
return nil, "lists is nil"
|
||||
end
|
||||
-- Not cached/whitelisted
|
||||
return false, "not cached/whitelisted", already_cached
|
||||
end
|
||||
|
||||
function whitelist:is_in_cache(ele)
|
||||
local ok, data = cachestore:get("plugin_whitelist_" .. ele)
|
||||
if not ok then then
|
||||
return false, data
|
||||
end
|
||||
return true, data
|
||||
end
|
||||
|
||||
function whitelist:add_to_cache(ele, value)
|
||||
local ok, err = cachestore:set("plugin_whitelist_" .. ele, value)
|
||||
if not ok then then
|
||||
return false, err
|
||||
end
|
||||
if set_needed ~= "yes" then
|
||||
return true, "whitelist not enabled"
|
||||
return true
|
||||
end
|
||||
|
||||
function whitelist:is_whitelisted(kind)
|
||||
if kind == "IP" then
|
||||
return self:is_whitelisted_ip()
|
||||
elseif kind == "URI"
|
||||
return self:is_whitelisted_uri()
|
||||
elseif kind == "UA"
|
||||
return self:is_whitelisted_ua()
|
||||
return false, "unknown kind " .. kind
|
||||
end
|
||||
|
||||
function whitelist:is_whitelisted_ip()
|
||||
-- Check if IP is in whitelist
|
||||
local ipm, err = ipmatcher.new(self.lists["IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
local match, err = ipm:match(ngx.var.remote_addr)
|
||||
if err then
|
||||
return nil, err
|
||||
end
|
||||
if match then
|
||||
return true, "ip"
|
||||
end
|
||||
|
||||
-- Check the cache
|
||||
local cached_ip, err = self:is_in_cache("ip" .. ngx.var.remote_addr)
|
||||
if cached_ip and cached_ip ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return true, "ip whitelisted"
|
||||
-- Check if rDNS is needed
|
||||
local check_rdns = true
|
||||
if self.variables["WHITELIST_RDNS_GLOBAL"] == "yes" then
|
||||
local is_global, err = utils.ip_is_global(ngx.var.remote_addr)
|
||||
if is_global == nil then
|
||||
return nil, err
|
||||
end
|
||||
local cached_uri, err = self:is_in_cache("uri" .. ngx.var.uri)
|
||||
if cached_uri and cached_uri ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return true, "uri whitelisted"
|
||||
if not is_global then
|
||||
check_rdns = false
|
||||
end
|
||||
local cached_ua = true
|
||||
if ngx.var.http_user_agent then
|
||||
cached_ua, err = self:is_in_cache("ua" .. ngx.var.http_user_agent)
|
||||
if cached_ua and cached_ua ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return true, "ua whitelisted"
|
||||
end
|
||||
if check_rdns then
|
||||
-- Get rDNS
|
||||
local rdns_list, err = utils.get_rdns(ngx.var.remote_addr)
|
||||
if not rdns_list then
|
||||
return nil, err
|
||||
end
|
||||
-- Check if rDNS is in whitelist
|
||||
for i, suffix in ipairs(self.lists["RDNS"]) do
|
||||
for j, rdns in ipairs(rdns_list) do
|
||||
if rdns:sub(-#suffix) == suffix then
|
||||
return true, "rDNS " .. suffix
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in whitelist
|
||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
return true, "ASN " .. bl_asn
|
||||
end
|
||||
end
|
||||
|
||||
-- Not whitelisted
|
||||
return true, "not whitelisted"
|
||||
return false, "ok"
|
||||
end
|
||||
|
||||
function _M:access()
|
||||
-- Check if access is needed
|
||||
local access_needed, err = utils.get_variable("USE_WHITELIST")
|
||||
if access_needed == nil then
|
||||
return false, err, nil, nil
|
||||
end
|
||||
if access_needed ~= "yes" then
|
||||
return true, "Whitelist not activated", nil, nil
|
||||
end
|
||||
|
||||
-- Check the cache
|
||||
local cached_ip, err = self:is_in_cache("ip" .. ngx.var.remote_addr)
|
||||
if cached_ip and cached_ip ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return true, "IP is in whitelist cache (info = " .. cached_ip .. ")", true, ngx.OK
|
||||
end
|
||||
local cached_uri, err = self:is_in_cache("uri" .. ngx.var.uri)
|
||||
if cached_uri and cached_uri ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return true, "URI is in whitelist cache (info = " .. cached_uri .. ")", true, ngx.OK
|
||||
end
|
||||
local cached_ua = true
|
||||
if ngx.var.http_user_agent then
|
||||
cached_ua, err = self:is_in_cache("ua" .. ngx.var.http_user_agent)
|
||||
if cached_ua and cached_ua ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return true, "User-Agent is in whitelist cache (info = " .. cached_ua .. ")", true, ngx.OK
|
||||
end
|
||||
end
|
||||
if cached_ip and cached_uri and cached_ua then
|
||||
return true, "full request is in whitelist cache (not whitelisted)", nil, nil
|
||||
end
|
||||
|
||||
-- Get list
|
||||
local data, err = datastore:get("plugin_whitelist_list")
|
||||
if not data then
|
||||
return false, "can't get Whitelist list : " .. err, false, nil
|
||||
end
|
||||
local ok, whitelists = pcall(cjson.decode, data)
|
||||
if not ok then
|
||||
return false, "error while decoding whitelists : " .. whitelists, false, nil
|
||||
end
|
||||
|
||||
-- Return value
|
||||
local ret, ret_err = true, "success"
|
||||
|
||||
-- Check if IP is in IP/net whitelist
|
||||
local ip_net, err = utils.get_variable("WHITELIST_IP")
|
||||
if ip_net and ip_net ~= "" then
|
||||
for element in ip_net:gmatch("%S+") do
|
||||
table.insert(whitelists["IP"], element)
|
||||
end
|
||||
end
|
||||
if not cached_ip then
|
||||
local ipm, err = ipmatcher.new(whitelists["IP"])
|
||||
if not ipm then
|
||||
ret = false
|
||||
ret_err = "can't instantiate ipmatcher " .. err
|
||||
else
|
||||
if ipm:match(ngx.var.remote_addr) then
|
||||
self:add_to_cache("ip" .. ngx.var.remote_addr, "ip/net")
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client IP " .. ngx.var.remote_addr .. " is in whitelist", true, ngx.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if rDNS is in whitelist
|
||||
local rdns_global, err = utils.get_variable("WHITELIST_RDNS_GLOBAL")
|
||||
local check = true
|
||||
if not rdns_global then
|
||||
logger.log(ngx.ERR, "WHITELIST", "Error while getting WHITELIST_RDNS_GLOBAL variable : " .. err)
|
||||
elseif rdns_global == "yes" then
|
||||
check, err = utils.ip_is_global(ngx.var.remote_addr)
|
||||
if check == nil then
|
||||
logger.log(ngx.ERR, "WHITELIST", "Error while getting checking if IP is global : " .. err)
|
||||
end
|
||||
end
|
||||
if not cached_ip and check then
|
||||
local rdns, err = utils.get_rdns(ngx.var.remote_addr)
|
||||
if not rdns then
|
||||
ret = false
|
||||
ret_err = "error while trying to get reverse dns : " .. err
|
||||
else
|
||||
local rdns_list, err = utils.get_variable("WHITELIST_RDNS")
|
||||
if rdns_list and rdns_list ~= "" then
|
||||
for element in rdns_list:gmatch("%S+") do
|
||||
table.insert(whitelists["RDNS"], element)
|
||||
end
|
||||
end
|
||||
for i, suffix in ipairs(whitelists["RDNS"]) do
|
||||
if rdns:sub(- #suffix) == suffix then
|
||||
self:add_to_cache("ip" .. ngx.var.remote_addr, "rDNS " .. suffix)
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client IP " .. ngx.var.remote_addr .. " is in whitelist (info = rDNS " .. suffix .. ")", true, ngx.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in whitelist
|
||||
if not cached_ip then
|
||||
if utils.ip_is_global(ngx.var.remote_addr) then
|
||||
local asn, err = utils.get_asn(ngx.var.remote_addr)
|
||||
if not asn then
|
||||
ret = false
|
||||
ret_err = "error while trying to get asn number : " .. err
|
||||
else
|
||||
local asn_list, err = utils.get_variable("WHITELIST_ASN")
|
||||
if asn_list and asn_list ~= "" then
|
||||
for element in asn_list:gmatch("%S+") do
|
||||
table.insert(whitelists["ASN"], element)
|
||||
end
|
||||
end
|
||||
for i, asn_bl in ipairs(whitelists["ASN"]) do
|
||||
if tostring(asn) == asn_bl then
|
||||
self:add_to_cache("ip" .. ngx.var.remote_addr, "ASN " .. tostring(asn))
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client IP " .. ngx.var.remote_addr .. " is in whitelist (kind = ASN " .. tostring(asn) .. ")", true,
|
||||
ngx.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- IP is not whitelisted
|
||||
local ok, err = self:add_to_cache("ip" .. ngx.var.remote_addr, "ok")
|
||||
if not ok then
|
||||
ret = false
|
||||
ret_err = err
|
||||
end
|
||||
|
||||
-- Check if User-Agent is in whitelist
|
||||
if not cached_ua and ngx.var.http_user_agent then
|
||||
local ua_list, err = utils.get_variable("WHITELIST_USER_AGENT")
|
||||
if ua_list and ua_list ~= "" then
|
||||
for element in ua_list:gmatch("%S+") do
|
||||
table.insert(whitelists["USER_AGENT"], element)
|
||||
end
|
||||
end
|
||||
for i, ua_bl in ipairs(whitelists["USER_AGENT"]) do
|
||||
if ngx.var.http_user_agent:match(ua_bl) then
|
||||
self:add_to_cache("ua" .. ngx.var.http_user_agent, "UA " .. ua_bl)
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client User-Agent " .. ngx.var.http_user_agent .. " is in whitelist (matched " .. ua_bl .. ")", true,
|
||||
ngx.OK
|
||||
end
|
||||
end
|
||||
-- UA is not whitelisted
|
||||
local ok, err = self:add_to_cache("ua" .. ngx.var.http_user_agent, "ok")
|
||||
if not ok then
|
||||
ret = false
|
||||
ret_err = err
|
||||
end
|
||||
end
|
||||
|
||||
function whitelist:is_whitelisted_uri()
|
||||
-- Check if URI is in whitelist
|
||||
if not cached_uri then
|
||||
local uri_list, err = utils.get_variable("WHITELIST_URI")
|
||||
if uri_list and uri_list ~= "" then
|
||||
for element in uri_list:gmatch("%S+") do
|
||||
table.insert(whitelists["URI"], element)
|
||||
for i, uri in ipairs(self.lists["URI"]) do
|
||||
if ngx.var.uri:match(uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
end
|
||||
for i, uri_bl in ipairs(whitelists["URI"]) do
|
||||
if ngx.var.uri:match(uri_bl) then
|
||||
self:add_to_cache("uri" .. ngx.var.uri, "URI " .. uri_bl)
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client URI " .. ngx.var.uri .. " is in whitelist (matched " .. uri_bl .. ")", true, ngx.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- URI is not whitelisted
|
||||
local ok, err = self:add_to_cache("uri" .. ngx.var.uri, "ok")
|
||||
if not ok then
|
||||
ret = false
|
||||
ret_err = err
|
||||
end
|
||||
|
||||
return ret, "IP is not in list (error = " .. ret_err .. ")", false, nil
|
||||
return false, "ok"
|
||||
end
|
||||
|
||||
function _M:preread()
|
||||
-- Check if preread is needed
|
||||
local preread_needed, err = utils.get_variable("USE_WHITELIST")
|
||||
if preread_needed == nil then
|
||||
return false, err, nil, nil
|
||||
end
|
||||
if preread_needed ~= "yes" then
|
||||
return true, "Whitelist not activated", nil, nil
|
||||
end
|
||||
|
||||
-- Check the cache
|
||||
local cached_ip, err = self:is_in_cache("ip" .. ngx.var.remote_addr)
|
||||
if cached_ip and cached_ip ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return true, "IP is in whitelist cache (info = " .. cached_ip .. ")", true, ngx.OK
|
||||
end
|
||||
if cached_ip then
|
||||
return true, "full request is in whitelist cache (not whitelisted)", nil, nil
|
||||
end
|
||||
|
||||
-- Get list
|
||||
local data, err = datastore:get("plugin_whitelist_list")
|
||||
if not data then
|
||||
return false, "can't get Whitelist list : " .. err, false, nil
|
||||
end
|
||||
local ok, whitelists = pcall(cjson.decode, data)
|
||||
if not ok then
|
||||
return false, "error while decoding whitelists : " .. whitelists, false, nil
|
||||
end
|
||||
|
||||
-- Return value
|
||||
local ret, ret_err = true, "success"
|
||||
|
||||
-- Check if IP is in IP/net whitelist
|
||||
local ip_net, err = utils.get_variable("WHITELIST_IP")
|
||||
if ip_net and ip_net ~= "" then
|
||||
for element in ip_net:gmatch("%S+") do
|
||||
table.insert(whitelists["IP"], element)
|
||||
function whitelist:is_whitelisted_ua()
|
||||
-- Check if UA is in whitelist
|
||||
for i, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if ngx.var.http_user_agent:match(ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
end
|
||||
if not cached_ip then
|
||||
local ipm, err = ipmatcher.new(whitelists["IP"])
|
||||
if not ipm then
|
||||
ret = false
|
||||
ret_err = "can't instantiate ipmatcher " .. err
|
||||
else
|
||||
if ipm:match(ngx.var.remote_addr) then
|
||||
self:add_to_cache("ip" .. ngx.var.remote_addr, "ip/net")
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client IP " .. ngx.var.remote_addr .. " is in whitelist", true, ngx.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if rDNS is in whitelist
|
||||
local rdns_global, err = utils.get_variable("WHITELIST_RDNS_GLOBAL")
|
||||
local check = true
|
||||
if not rdns_global then
|
||||
logger.log(ngx.ERR, "WHITELIST", "Error while getting WHITELIST_RDNS_GLOBAL variable : " .. err)
|
||||
elseif rdns_global == "yes" then
|
||||
check, err = utils.ip_is_global(ngx.var.remote_addr)
|
||||
if check == nil then
|
||||
logger.log(ngx.ERR, "WHITELIST", "Error while getting checking if IP is global : " .. err)
|
||||
end
|
||||
end
|
||||
if not cached_ip and check then
|
||||
local rdns, err = utils.get_rdns(ngx.var.remote_addr)
|
||||
if not rdns then
|
||||
ret = false
|
||||
ret_err = "error while trying to get reverse dns : " .. err
|
||||
else
|
||||
local rdns_list, err = utils.get_variable("WHITELIST_RDNS")
|
||||
if rdns_list and rdns_list ~= "" then
|
||||
for element in rdns_list:gmatch("%S+") do
|
||||
table.insert(whitelists["RDNS"], element)
|
||||
end
|
||||
end
|
||||
for i, suffix in ipairs(whitelists["RDNS"]) do
|
||||
if rdns:sub(- #suffix) == suffix then
|
||||
self:add_to_cache("ip" .. ngx.var.remote_addr, "rDNS " .. suffix)
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client IP " .. ngx.var.remote_addr .. " is in whitelist (info = rDNS " .. suffix .. ")", true, ngx.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in whitelist
|
||||
if not cached_ip then
|
||||
if utils.ip_is_global(ngx.var.remote_addr) then
|
||||
local asn, err = utils.get_asn(ngx.var.remote_addr)
|
||||
if not asn then
|
||||
ret = false
|
||||
ret_err = "error while trying to get asn number : " .. err
|
||||
else
|
||||
local asn_list, err = utils.get_variable("WHITELIST_ASN")
|
||||
if asn_list and asn_list ~= "" then
|
||||
for element in asn_list:gmatch("%S+") do
|
||||
table.insert(whitelists["ASN"], element)
|
||||
end
|
||||
end
|
||||
for i, asn_bl in ipairs(whitelists["ASN"]) do
|
||||
if tostring(asn) == asn_bl then
|
||||
self:add_to_cache("ip" .. ngx.var.remote_addr, "ASN " .. tostring(asn))
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
return ret, "client IP " .. ngx.var.remote_addr .. " is in whitelist (kind = ASN " .. tostring(asn) .. ")", true,
|
||||
ngx.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- IP is not whitelisted
|
||||
local ok, err = self:add_to_cache("ip" .. ngx.var.remote_addr, "ok")
|
||||
if not ok then
|
||||
ret = false
|
||||
ret_err = err
|
||||
end
|
||||
return ret, "IP is not in list (error = " .. ret_err .. ")", false, nil
|
||||
-- UA is not whiteklisted
|
||||
return false, "ok"
|
||||
end
|
||||
|
||||
function _M:is_in_cache(ele)
|
||||
local kind, err = datastore:get("plugin_whitelist_cache_" .. ngx.var.server_name .. ele)
|
||||
if not kind then
|
||||
if err ~= "not found" then
|
||||
logger.log(ngx.ERR, "WHITELIST", "Error while accessing cache : " .. err)
|
||||
end
|
||||
return false, err
|
||||
end
|
||||
return kind, "success"
|
||||
end
|
||||
|
||||
function _M:add_to_cache(ele, kind)
|
||||
local ok, err = datastore:set("plugin_whitelist_cache_" .. ngx.var.server_name .. ele, kind, 3600)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "WHITELIST", "Error while adding element to cache : " .. err)
|
||||
return false, err
|
||||
end
|
||||
return true, "success"
|
||||
end
|
||||
|
||||
return _M
|
||||
return whitelist
|
|
@ -128,10 +128,10 @@ function do_and_check_cmd() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# nginx 1.22.1
|
||||
# nginx 1.24.0
|
||||
echo "ℹ️ Downloading nginx"
|
||||
NGINX_VERSION="1.22.1"
|
||||
secure_download "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" "nginx-${NGINX_VERSION}.tar.gz" "1d468dcfa9bbd348b8a5dc514ac1428a789e73a92384c039b73a51ce376785f74bf942872c5594a9fcda6bbf44758bd727ce15ac2395f1aa989c507014647dcc"
|
||||
NGINX_VERSION="1.24.0"
|
||||
secure_download "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" "nginx-${NGINX_VERSION}.tar.gz" "1114e37de5664a8109c99cfb2faa1f42ff8ac63c932bcf3780d645e5ed32c0b2ac446f80305b4465994c8f9430604968e176ae464fd80f632d1cb2c8f6007ff3"
|
||||
if [ -f "deps/src/nginx-${NGINX_VERSION}.tar.gz" ] ; then
|
||||
do_and_check_cmd tar -xvzf deps/src/nginx-${NGINX_VERSION}.tar.gz -C deps/src
|
||||
do_and_check_cmd rm -f deps/src/nginx-${NGINX_VERSION}.tar.gz
|
||||
|
@ -148,21 +148,21 @@ if [ -f "deps/src/lua-${LUA_VERSION}.tar.gz" ] ; then
|
|||
do_and_check_cmd patch deps/src/lua-${LUA_VERSION}/src/Makefile deps/misc/lua.patch2
|
||||
fi
|
||||
|
||||
# LuaJIT v2.1-20220915
|
||||
# LuaJIT v2.1-20230410
|
||||
echo "ℹ️ Downloading LuaJIT"
|
||||
git_secure_clone "https://github.com/openresty/luajit2.git" "8384278b14988390cf030b787537aa916a9709bb"
|
||||
git_secure_clone "https://github.com/openresty/luajit2.git" "04f33ff01da97905a1641985fb5c840d234f97f1"
|
||||
|
||||
# lua-nginx-module v0.10.23
|
||||
# lua-nginx-module v0.10.24
|
||||
echo "ℹ️ Downloading lua-nginx-module"
|
||||
git_secure_clone "https://github.com/openresty/lua-nginx-module.git" "5e05fa3adb0d2492ecaaf2cb76498e23765aa6ab"
|
||||
git_secure_clone "https://github.com/openresty/lua-nginx-module.git" "68acad14e4a8f42e31d4a4bb5ed44d6f5b55fc1c"
|
||||
|
||||
# lua-resty-core v0.1.25
|
||||
# lua-resty-core v0.1.26
|
||||
echo "ℹ️ Downloading lua-resty-core"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-core.git" "0173d96c9eb77b513b989b765716fd2498f09dd9"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-core.git" "407000a9856d3a5aab34e8c73f6ab0f049f8b8d7"
|
||||
|
||||
# lua-resty-lrucache v0.13
|
||||
echo "ℹ️ Downloading lua-resty-lrucache"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-lrucache.git" "2ab2624c841cbf04785cc6384c5e213933d3b5f2"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-lrucache.git" "a79615ec9dc547fdb4aaee59ef8f5a50648ce9fd"
|
||||
|
||||
# lua-resty-dns v0.22
|
||||
echo "ℹ️ Downloading lua-resty-dns"
|
||||
|
@ -180,29 +180,29 @@ git_secure_clone "https://github.com/bungle/lua-resty-random.git" "17b604f7f7dd2
|
|||
echo "ℹ️ Downloading lua-resty-string"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-string.git" "b192878f6ed31b0af237935bbc5a8110a3c2256c"
|
||||
|
||||
# lua-cjson v2.1.0.9
|
||||
# lua-cjson v2.1.0.12
|
||||
echo "ℹ️ Downloading lua-cjson"
|
||||
git_secure_clone "https://github.com/openresty/lua-cjson.git" "891962b11d6d3b1b7275550b5c109e16c73ac94f"
|
||||
git_secure_clone "https://github.com/openresty/lua-cjson.git" "881accc8fadca5ec02aa34d364df2a1aa25cd2f9"
|
||||
|
||||
# lua-gd v2.0.33r3+
|
||||
echo "ℹ️ Downloading lua-gd"
|
||||
git_secure_clone "https://github.com/ittner/lua-gd.git" "2ce8e478a8591afd71e607506bc8c64b161bbd30"
|
||||
|
||||
# lua-resty-http v0.16.1
|
||||
# lua-resty-http v0.17.1
|
||||
echo "ℹ️ Downloading lua-resty-http"
|
||||
git_secure_clone "https://github.com/ledgetech/lua-resty-http.git" "9bf951dfe162dd9710a0e1f4525738d4902e9d20"
|
||||
git_secure_clone "https://github.com/ledgetech/lua-resty-http.git" "4ab4269cf442ba52507aa2c718f606054452fcad"
|
||||
|
||||
# lualogging v1.8.0
|
||||
# lualogging v1.8.2
|
||||
echo "ℹ️ Downloading lualogging"
|
||||
git_secure_clone "https://github.com/lunarmodules/lualogging.git" "1c6fcf5f68e4d0324c5977f1a27083c06f4d1b8f"
|
||||
git_secure_clone "https://github.com/lunarmodules/lualogging.git" "465c994788f1bc18fca950934fa5ec9a909f496c"
|
||||
|
||||
# luasocket v3.1.0
|
||||
echo "ℹ️ Downloading luasocket"
|
||||
git_secure_clone "https://github.com/diegonehab/luasocket.git" "95b7efa9da506ef968c1347edf3fc56370f0deed"
|
||||
|
||||
# luasec v1.2.0
|
||||
# luasec v1.3.1
|
||||
echo "ℹ️ Downloading luasec"
|
||||
git_secure_clone "https://github.com/brunoos/luasec.git" "d9215ee00f6694a228daad50ee85827a4cd13583"
|
||||
git_secure_clone "https://github.com/brunoos/luasec.git" "fddde111f7fe9ad5417d75ebbd70429d13eaad97"
|
||||
|
||||
# lua-resty-ipmatcher v0.6.1 (3 commits after just in case)
|
||||
echo "ℹ️ Downloading lua-resty-ipmatcher"
|
||||
|
@ -215,13 +215,13 @@ if [ "$dopatch" = "yes" ] ; then
|
|||
do_and_check_cmd patch deps/src/lua-resty-ipmatcher/resty/ipmatcher.lua deps/misc/ipmatcher.patch
|
||||
fi
|
||||
|
||||
# lua-resty-redis v0.29
|
||||
# lua-resty-redis v0.30
|
||||
echo "ℹ️ Downloading lua-resty-redis"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-redis.git" "053f989c7f43d8edc79d5151e73b79249c6b5d94"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-redis.git" "d7c25f1b339d79196ff67f061c547a73a920b580"
|
||||
|
||||
# lua-resty-upload v0.10 (8 commits after just in case)
|
||||
# lua-resty-upload v0.11
|
||||
echo "ℹ️ Downloading lua-resty-upload"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-upload.git" "73c89846e866bf5d0660ffa881df37fd63f04391"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-upload.git" "03704aee42f7135e7782688d8a9af63a16015edc"
|
||||
|
||||
# luajit-geoip v2.1.0
|
||||
echo "ℹ️ Downloading luajit-geoip"
|
||||
|
@ -242,12 +242,24 @@ git_secure_clone "https://github.com/iskolbin/lbase64.git" "c261320edbdf82c16409
|
|||
echo "ℹ️ Downloading lua-resty-env"
|
||||
git_secure_clone "https://github.com/3scale/lua-resty-env.git" "adb294def823dd910ffa11972d2c61eab7cfce3e"
|
||||
|
||||
# ModSecurity v3.0.8 (19 commits after just in case)
|
||||
# lua-resty-mlcache v2.6.0
|
||||
echo "ℹ️ Downloading lua-resty-mlcache"
|
||||
git_secure_clone "https://github.com/thibaultcha/lua-resty-mlcache.git" "f140f56663cbdb9cdd247d29f75c299c702ff6b4"
|
||||
|
||||
# lua-resty-template v2.0
|
||||
echo "ℹ️ Downloading lua-resty-template"
|
||||
git_secure_clone "https://github.com/bungle/lua-resty-template.git" "c08c6bc9e27710806990f2dec0f03b19406976ac"
|
||||
|
||||
# lua-resty-lock v0.09
|
||||
echo "ℹ️ Downloading lua-resty-lock"
|
||||
git_secure_clone "https://github.com/openresty/lua-resty-lock.git" "9dc550e56b6f3b1a2f1a31bb270a91813b5b6861"
|
||||
|
||||
# ModSecurity v3.0.9
|
||||
echo "ℹ️ Downloading ModSecurity"
|
||||
if [ ! -d "deps/src/ModSecurity" ] ; then
|
||||
dopatch="yes"
|
||||
fi
|
||||
git_secure_clone "https://github.com/SpiderLabs/ModSecurity.git" "40f7a5067c695b1770920b881f30abc09a4e02b3"
|
||||
git_secure_clone "https://github.com/SpiderLabs/ModSecurity.git" "205dac0e8c675182f96b5c2fb06be7d1cf7af2b2"
|
||||
if [ "$dopatch" = "yes" ] ; then
|
||||
do_and_check_cmd patch deps/src/ModSecurity/configure.ac deps/misc/modsecurity.patch
|
||||
fi
|
||||
|
@ -285,10 +297,10 @@ git_secure_clone "https://github.com/AirisX/nginx_cookie_flag_module.git" "4e48a
|
|||
echo "ℹ️ Downloading ngx_brotli"
|
||||
git_secure_clone "https://github.com/google/ngx_brotli.git" "6e975bcb015f62e1f303054897783355e2a877dc"
|
||||
|
||||
# ngx_devel_kit
|
||||
# ngx_devel_kit v0.3.2
|
||||
echo "ℹ️ Downloading ngx_devel_kit"
|
||||
git_secure_clone "https://github.com/vision5/ngx_devel_kit.git" "b4642d6ca01011bd8cd30b253f5c3872b384fd21"
|
||||
|
||||
# stream-lua-nginx-module
|
||||
# stream-lua-nginx-module v0.0.13
|
||||
echo "ℹ️ Downloading stream-lua-nginx-module"
|
||||
git_secure_clone "https://github.com/openresty/stream-lua-nginx-module.git" "2ef14f373b991b911c4eb5d09aa333352be9a756"
|
||||
git_secure_clone "https://github.com/openresty/stream-lua-nginx-module.git" "309198abf26266f1a3e53c71388ed7bb9d1e5ea2"
|
|
@ -123,6 +123,18 @@ do_and_check_cmd cp -r /tmp/bunkerweb/deps/src/lbase64/base64.lua /usr/share/bun
|
|||
echo "ℹ️ Installing lua-resty-env"
|
||||
do_and_check_cmd cp -r /tmp/bunkerweb/deps/src/lua-resty-env/src/resty/env.lua /usr/share/bunkerweb/deps/lib/lua/resty
|
||||
|
||||
# Installing lua-resty-mlcache
|
||||
echo "ℹ️ Installing lua-resty-mlcache"
|
||||
do_and_check_cmd cp -r /tmp/bunkerweb/deps/src/lua-resty-mlcache/lib/resty/* /usr/share/bunkerweb/deps/lib/lua/resty
|
||||
|
||||
# Installing lua-resty-template
|
||||
echo "ℹ️ Installing lua-resty-template"
|
||||
do_and_check_cmd cp -r /tmp/bunkerweb/deps/src/lua-resty-template/lib/resty/* /usr/share/bunkerweb/deps/lib/lua/resty
|
||||
|
||||
# Installing lua-resty-lock
|
||||
echo "ℹ️ Installing lua-resty-lock"
|
||||
CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-lock" do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
|
||||
|
||||
# Compile dynamic modules
|
||||
echo "ℹ️ Compiling and installing dynamic modules"
|
||||
CONFARGS="$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p')"
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
---
|
||||
name: Bug report for version 2.x
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
name: Bug report for version 3.x
|
||||
about: Create a report to help us improve. If you don't know a specific detail or
|
||||
piece of information leave it blank, if necessary we will help you to figure out.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
@ -17,7 +20,7 @@ Output of:
|
|||
3. Error logs
|
||||
4. If there is a crash, the core dump file.
|
||||
|
||||
_Notice:_ Be carefully to not leak any confidential information.
|
||||
_Notice:_ Be careful to not leak any confidential information.
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
|
@ -33,8 +36,8 @@ A **curl** command line that mimics the original request and reproduces the prob
|
|||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Server (please complete the following information):**
|
||||
- ModSecurity version (and connector): [e.g. ModSecurity v3.0.1 with nginx-connector v1.0.0]
|
||||
- WebServer: [e.g. nginx-1.15.5]
|
||||
- ModSecurity version (and connector): [e.g. ModSecurity v3.0.8 with nginx-connector v1.0.3]
|
||||
- WebServer: [e.g. nginx-1.18.0]
|
||||
- OS (and distro): [e.g. Linux, archlinux]
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
v3.x.y - YYYY-MMM-DD (to be released)
|
||||
-------------------------------------
|
||||
v3.0.9 - 2023-Apr-12
|
||||
--------------------
|
||||
|
||||
- Fix: possible segfault on reload if duplicate ip+CIDR in ip match list
|
||||
[Issue #2877, #2890 - @tomsommer, @martinhsv]
|
||||
- Add some member variable inits in Transaction class (possible segfault)
|
||||
[Issue #2886 - @GNU-Plus-Windows-User, @airween, @mdounin, @martinhsv]
|
||||
- Resolve memory leak on reload (bison-generated variable)
|
||||
[Issue #2876 - @martinhsv]
|
||||
- Support equals sign in XPath expressions
|
||||
[Issue #2328 - @dennus, @martinhsv]
|
||||
- Encode two special chars in error.log output
|
||||
[Issue #2854 - @airween, @martinhsv]
|
||||
- Add JIT support for PCRE2
|
||||
[Issue #2791 - @wfjsw, @airween, @FireBurn, @martinhsv]
|
||||
- Support comments in ipMatchFromFile file via '#' token
|
||||
[Issue #2554 - @tomsommer, @martinhsv]
|
||||
- Use name package name libmaxminddb with pkg-config
|
||||
[Issue #2595, #2596 - @frankvanbever, @ffontaine, @arnout]
|
||||
- Fix: FILES_TMP_CONTENT collection key should use part name
|
||||
[Issue #2831 - @airween]
|
||||
- Use AS_HELP_STRING instead of obsolete AC_HELP_STRING macro
|
||||
[Issue #2806 - @hughmcmaster]
|
||||
- During configure, do not check for pcre if pcre2 specified
|
||||
|
|
|
@ -279,6 +279,7 @@ TESTS+=test/test-cases/regression/variable-variation-count.json
|
|||
TESTS+=test/test-cases/regression/variable-variation-exclusion.json
|
||||
TESTS+=test/test-cases/regression/variable-WEBAPPID.json
|
||||
TESTS+=test/test-cases/regression/variable-WEBSERVER_ERROR_LOG.json
|
||||
TESTS+=test/test-cases/regression/variable-XML.json
|
||||
TESTS+=test/test-cases/secrules-language-tests/operators/beginsWith.json
|
||||
TESTS+=test/test-cases/secrules-language-tests/operators/contains.json
|
||||
TESTS+=test/test-cases/secrules-language-tests/operators/containsWord.json
|
||||
|
|
|
@ -60,12 +60,10 @@ else
|
|||
# Nothing about MaxMind was informed, using the pkg-config to figure things out.
|
||||
if test -n "${PKG_CONFIG}"; then
|
||||
MAXMIND_PKG_NAME=""
|
||||
for x in ${MAXMIND_POSSIBLE_LIB_NAMES}; do
|
||||
if ${PKG_CONFIG} --exists ${x}; then
|
||||
MAXMIND_PKG_NAME="$x"
|
||||
if ${PKG_CONFIG} --exists libmaxminddb; then
|
||||
MAXMIND_PKG_NAME="libmaxminddb"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
AC_MSG_NOTICE([Nothing about MaxMind was informed during the configure phase. Trying to detect it on the platform...])
|
||||
if test -n "${MAXMIND_PKG_NAME}"; then
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ModSecurity, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
* Copyright (c) 2015 - 2023 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
@ -190,7 +190,7 @@ namespace modsecurity {
|
|||
|
||||
#define MODSECURITY_MAJOR "3"
|
||||
#define MODSECURITY_MINOR "0"
|
||||
#define MODSECURITY_PATCHLEVEL "8"
|
||||
#define MODSECURITY_PATCHLEVEL "9"
|
||||
#define MODSECURITY_TAG ""
|
||||
#define MODSECURITY_TAG_NUM "100"
|
||||
|
||||
|
@ -198,7 +198,7 @@ namespace modsecurity {
|
|||
MODSECURITY_MINOR "." MODSECURITY_PATCHLEVEL \
|
||||
MODSECURITY_TAG
|
||||
|
||||
#define MODSECURITY_VERSION_NUM 3080100
|
||||
#define MODSECURITY_VERSION_NUM 3090100
|
||||
|
||||
#define MODSECURITY_CHECK_VERSION(a) (MODSECURITY_VERSION_NUM <= a)
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ bool VerifyCC::init(const std::string ¶m2, std::string *error) {
|
|||
if (m_pc == NULL) {
|
||||
return false;
|
||||
}
|
||||
m_pcje = pcre2_jit_compile(m_pc, PCRE2_JIT_COMPLETE);
|
||||
#else
|
||||
const char *errptr = NULL;
|
||||
int erroffset = 0;
|
||||
|
@ -142,8 +143,16 @@ bool VerifyCC::evaluate(Transaction *t, RuleWithActions *rule,
|
|||
PCRE2_SPTR pcre2_i = reinterpret_cast<PCRE2_SPTR>(i.c_str());
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(m_pc, NULL);
|
||||
|
||||
int ret;
|
||||
for (offset = 0; offset < target_length; offset++) {
|
||||
int ret = pcre2_match(m_pc, pcre2_i, target_length, offset, 0, match_data, NULL);
|
||||
|
||||
if (m_pcje == 0) {
|
||||
ret = pcre2_jit_match(m_pc, pcre2_i, target_length, offset, 0, match_data, NULL);
|
||||
}
|
||||
|
||||
if (m_pcje != 0 || ret == PCRE2_ERROR_JIT_STACKLIMIT) {
|
||||
ret = pcre2_match(m_pc, pcre2_i, target_length, offset, PCRE2_NO_JIT, match_data, NULL);
|
||||
}
|
||||
|
||||
/* If there was no match, then we are done. */
|
||||
if (ret < 0) {
|
||||
|
|
|
@ -39,7 +39,8 @@ class VerifyCC : public Operator {
|
|||
explicit VerifyCC(std::unique_ptr<RunTimeString> param)
|
||||
: Operator("VerifyCC", std::move(param)),
|
||||
#if WITH_PCRE2
|
||||
m_pc(NULL) { }
|
||||
m_pc(NULL),
|
||||
m_pcje(PCRE2_ERROR_JIT_BADOPTION) { }
|
||||
#else
|
||||
m_pc(NULL),
|
||||
m_pce(NULL) { }
|
||||
|
@ -53,6 +54,7 @@ class VerifyCC : public Operator {
|
|||
private:
|
||||
#if WITH_PCRE2
|
||||
pcre2_code *m_pc;
|
||||
int m_pcje;
|
||||
#else
|
||||
pcre *m_pc;
|
||||
pcre_extra *m_pce;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ModSecurity, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
* Copyright (c) 2015 - 2023 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
@ -34,6 +34,7 @@ Driver::Driver()
|
|||
|
||||
|
||||
Driver::~Driver() {
|
||||
|
||||
while (loc.empty() == false) {
|
||||
yy::location *a = loc.back();
|
||||
loc.pop_back();
|
||||
|
@ -129,9 +130,11 @@ int Driver::parse(const std::string &f, const std::string &ref) {
|
|||
m_lastRule = nullptr;
|
||||
loc.push_back(new yy::location());
|
||||
if (ref.empty()) {
|
||||
loc.back()->begin.filename = loc.back()->end.filename = new std::string("<<reference missing or not informed>>");
|
||||
m_filenames.push_back("<<reference missing or not informed>>");
|
||||
loc.back()->begin.filename = loc.back()->end.filename = &(m_filenames.back());
|
||||
} else {
|
||||
loc.back()->begin.filename = loc.back()->end.filename = new std::string(ref);
|
||||
m_filenames.push_back(ref);
|
||||
loc.back()->begin.filename = loc.back()->end.filename = &(m_filenames.back());
|
||||
}
|
||||
|
||||
if (f.empty()) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ModSecurity, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
* Copyright (c) 2015 - 2023 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
@ -53,14 +53,6 @@ typedef struct Driver_t Driver;
|
|||
#endif
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* FIXME: There is a memory leak in the filename at yy::location.
|
||||
* The filename should be converted into a shared string to
|
||||
* save memory or be associated with the life cycle of the
|
||||
* driver class.
|
||||
*
|
||||
**/
|
||||
class Driver : public RulesSetProperties {
|
||||
public:
|
||||
Driver();
|
||||
|
@ -92,6 +84,13 @@ class Driver : public RulesSetProperties {
|
|||
RuleWithActions *m_lastRule;
|
||||
|
||||
RulesSetPhases m_rulesSetPhases;
|
||||
|
||||
// Retain a list of new'd filenames so that they are available during the lifetime
|
||||
// of the Driver object, but so that they will get cleaned up by the Driver
|
||||
// destructor. This is to resolve a memory leak of yy.position.filename in location.hh.
|
||||
// Ordinarily other solutions would have been preferable, but location.hh is a
|
||||
// bison-generated file, which makes some alternative solutions impractical.
|
||||
std::list<std::string> m_filenames;
|
||||
};
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -897,6 +897,7 @@ namespace yy {
|
|||
// "RUN_TIME_VAR_TIME_YEAR"
|
||||
// "VARIABLE"
|
||||
// "Dictionary element"
|
||||
// "Dictionary element, with equals"
|
||||
// "Dictionary element, selected by regexp"
|
||||
char dummy1[sizeof (std::string)];
|
||||
|
||||
|
@ -1314,7 +1315,8 @@ namespace yy {
|
|||
TOK_RUN_TIME_VAR_TIME_YEAR = 596, // "RUN_TIME_VAR_TIME_YEAR"
|
||||
TOK_VARIABLE = 597, // "VARIABLE"
|
||||
TOK_DICT_ELEMENT = 598, // "Dictionary element"
|
||||
TOK_DICT_ELEMENT_REGEXP = 599 // "Dictionary element, selected by regexp"
|
||||
TOK_DICT_ELEMENT_WITH_EQUALS = 599, // "Dictionary element, with equals"
|
||||
TOK_DICT_ELEMENT_REGEXP = 600 // "Dictionary element, selected by regexp"
|
||||
};
|
||||
/// Backward compatibility alias (Bison 3.6).
|
||||
typedef token_kind_type yytokentype;
|
||||
|
@ -1331,7 +1333,7 @@ namespace yy {
|
|||
{
|
||||
enum symbol_kind_type
|
||||
{
|
||||
YYNTOKENS = 345, ///< Number of tokens.
|
||||
YYNTOKENS = 346, ///< Number of tokens.
|
||||
S_YYEMPTY = -2,
|
||||
S_YYEOF = 0, // "end of file"
|
||||
S_YYerror = 1, // error
|
||||
|
@ -1677,23 +1679,24 @@ namespace yy {
|
|||
S_RUN_TIME_VAR_TIME_YEAR = 341, // "RUN_TIME_VAR_TIME_YEAR"
|
||||
S_VARIABLE = 342, // "VARIABLE"
|
||||
S_DICT_ELEMENT = 343, // "Dictionary element"
|
||||
S_DICT_ELEMENT_REGEXP = 344, // "Dictionary element, selected by regexp"
|
||||
S_YYACCEPT = 345, // $accept
|
||||
S_input = 346, // input
|
||||
S_line = 347, // line
|
||||
S_audit_log = 348, // audit_log
|
||||
S_actions = 349, // actions
|
||||
S_actions_may_quoted = 350, // actions_may_quoted
|
||||
S_op = 351, // op
|
||||
S_op_before_init = 352, // op_before_init
|
||||
S_expression = 353, // expression
|
||||
S_variables = 354, // variables
|
||||
S_variables_pre_process = 355, // variables_pre_process
|
||||
S_variables_may_be_quoted = 356, // variables_may_be_quoted
|
||||
S_var = 357, // var
|
||||
S_act = 358, // act
|
||||
S_setvar_action = 359, // setvar_action
|
||||
S_run_time_string = 360 // run_time_string
|
||||
S_DICT_ELEMENT_WITH_EQUALS = 344, // "Dictionary element, with equals"
|
||||
S_DICT_ELEMENT_REGEXP = 345, // "Dictionary element, selected by regexp"
|
||||
S_YYACCEPT = 346, // $accept
|
||||
S_input = 347, // input
|
||||
S_line = 348, // line
|
||||
S_audit_log = 349, // audit_log
|
||||
S_actions = 350, // actions
|
||||
S_actions_may_quoted = 351, // actions_may_quoted
|
||||
S_op = 352, // op
|
||||
S_op_before_init = 353, // op_before_init
|
||||
S_expression = 354, // expression
|
||||
S_variables = 355, // variables
|
||||
S_variables_pre_process = 356, // variables_pre_process
|
||||
S_variables_may_be_quoted = 357, // variables_may_be_quoted
|
||||
S_var = 358, // var
|
||||
S_act = 359, // act
|
||||
S_setvar_action = 360, // setvar_action
|
||||
S_run_time_string = 361 // run_time_string
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1927,6 +1930,7 @@ namespace yy {
|
|||
case symbol_kind::S_RUN_TIME_VAR_TIME_YEAR: // "RUN_TIME_VAR_TIME_YEAR"
|
||||
case symbol_kind::S_VARIABLE: // "VARIABLE"
|
||||
case symbol_kind::S_DICT_ELEMENT: // "Dictionary element"
|
||||
case symbol_kind::S_DICT_ELEMENT_WITH_EQUALS: // "Dictionary element, with equals"
|
||||
case symbol_kind::S_DICT_ELEMENT_REGEXP: // "Dictionary element, selected by regexp"
|
||||
value.move< std::string > (std::move (that.value));
|
||||
break;
|
||||
|
@ -2300,6 +2304,7 @@ switch (yykind)
|
|||
case symbol_kind::S_RUN_TIME_VAR_TIME_YEAR: // "RUN_TIME_VAR_TIME_YEAR"
|
||||
case symbol_kind::S_VARIABLE: // "VARIABLE"
|
||||
case symbol_kind::S_DICT_ELEMENT: // "Dictionary element"
|
||||
case symbol_kind::S_DICT_ELEMENT_WITH_EQUALS: // "Dictionary element, with equals"
|
||||
case symbol_kind::S_DICT_ELEMENT_REGEXP: // "Dictionary element, selected by regexp"
|
||||
value.template destroy< std::string > ();
|
||||
break;
|
||||
|
@ -7648,6 +7653,21 @@ switch (yykind)
|
|||
return symbol_type (token::TOK_DICT_ELEMENT, v, l);
|
||||
}
|
||||
#endif
|
||||
#if 201103L <= YY_CPLUSPLUS
|
||||
static
|
||||
symbol_type
|
||||
make_DICT_ELEMENT_WITH_EQUALS (std::string v, location_type l)
|
||||
{
|
||||
return symbol_type (token::TOK_DICT_ELEMENT_WITH_EQUALS, std::move (v), std::move (l));
|
||||
}
|
||||
#else
|
||||
static
|
||||
symbol_type
|
||||
make_DICT_ELEMENT_WITH_EQUALS (const std::string& v, const location_type& l)
|
||||
{
|
||||
return symbol_type (token::TOK_DICT_ELEMENT_WITH_EQUALS, v, l);
|
||||
}
|
||||
#endif
|
||||
#if 201103L <= YY_CPLUSPLUS
|
||||
static
|
||||
symbol_type
|
||||
|
@ -7993,7 +8013,7 @@ switch (yykind)
|
|||
/// Constants.
|
||||
enum
|
||||
{
|
||||
yylast_ = 3344, ///< Last index in yytable_.
|
||||
yylast_ = 3346, ///< Last index in yytable_.
|
||||
yynnts_ = 16, ///< Number of nonterminal symbols.
|
||||
yyfinal_ = 339 ///< Termination state number.
|
||||
};
|
||||
|
@ -8073,10 +8093,11 @@ switch (yykind)
|
|||
305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
|
||||
315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
|
||||
325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
|
||||
335, 336, 337, 338, 339, 340, 341, 342, 343, 344
|
||||
335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
|
||||
345
|
||||
};
|
||||
// Last valid token kind.
|
||||
const int code_max = 599;
|
||||
const int code_max = 600;
|
||||
|
||||
if (t <= 0)
|
||||
return symbol_kind::S_YYEOF;
|
||||
|
@ -8292,6 +8313,7 @@ switch (yykind)
|
|||
case symbol_kind::S_RUN_TIME_VAR_TIME_YEAR: // "RUN_TIME_VAR_TIME_YEAR"
|
||||
case symbol_kind::S_VARIABLE: // "VARIABLE"
|
||||
case symbol_kind::S_DICT_ELEMENT: // "Dictionary element"
|
||||
case symbol_kind::S_DICT_ELEMENT_WITH_EQUALS: // "Dictionary element, with equals"
|
||||
case symbol_kind::S_DICT_ELEMENT_REGEXP: // "Dictionary element, selected by regexp"
|
||||
value.copy< std::string > (YY_MOVE (that.value));
|
||||
break;
|
||||
|
@ -8551,6 +8573,7 @@ switch (yykind)
|
|||
case symbol_kind::S_RUN_TIME_VAR_TIME_YEAR: // "RUN_TIME_VAR_TIME_YEAR"
|
||||
case symbol_kind::S_VARIABLE: // "VARIABLE"
|
||||
case symbol_kind::S_DICT_ELEMENT: // "Dictionary element"
|
||||
case symbol_kind::S_DICT_ELEMENT_WITH_EQUALS: // "Dictionary element, with equals"
|
||||
case symbol_kind::S_DICT_ELEMENT_REGEXP: // "Dictionary element, selected by regexp"
|
||||
value.move< std::string > (YY_MOVE (s.value));
|
||||
break;
|
||||
|
@ -8646,7 +8669,7 @@ switch (yykind)
|
|||
}
|
||||
|
||||
} // yy
|
||||
#line 8650 "seclang-parser.hh"
|
||||
#line 8673 "seclang-parser.hh"
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -319,7 +319,8 @@ using namespace modsecurity::operators;
|
|||
%initial-action
|
||||
{
|
||||
// Initialize the initial location.
|
||||
@$.begin.filename = @$.end.filename = new std::string(driver.file);
|
||||
driver.m_filenames.push_back(driver.file);
|
||||
@$.begin.filename = @$.end.filename = &(driver.m_filenames.back());
|
||||
};
|
||||
%define parse.trace
|
||||
%define parse.error verbose
|
||||
|
@ -680,6 +681,7 @@ using namespace modsecurity::operators;
|
|||
RUN_TIME_VAR_TIME_YEAR "RUN_TIME_VAR_TIME_YEAR"
|
||||
VARIABLE "VARIABLE"
|
||||
DICT_ELEMENT "Dictionary element"
|
||||
DICT_ELEMENT_WITH_EQUALS "Dictionary element, with equals"
|
||||
DICT_ELEMENT_REGEXP "Dictionary element, selected by regexp"
|
||||
;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -420,10 +420,8 @@ DICT_ELEMENT ([^\"|,\n \t}=]|([^\\]\\\"))+
|
|||
DICT_ELEMENT_WITH_PIPE [^ =\t"]+
|
||||
DICT_ELEMENT_NO_PIPE [^ =\|\t"]+
|
||||
DICT_ELEMENT_NO_MACRO ([^\"|,%{\n \t}=]|([^\\]\\\"))+
|
||||
DICT_ELEMENT_WITH_EQUALS ([^\"|,\n \t}]|([^\\]\\\"))+
|
||||
|
||||
DICT_ELEMENT_TWO [^\"\=, \t\r\n\\]*
|
||||
DICT_ELEMENT_TWO_QUOTED [^\"\'\=\r\n\\]*
|
||||
DICT_ELEMENT_TWO2 [A-Za-z_ -\%\{\.\}\-\/]+
|
||||
DIRECTIVE (?i:SecRule)
|
||||
DIRECTIVE_SECRULESCRIPT (?i:SecRuleScript)
|
||||
FREE_TEXT_NEW_LINE [^\"|\n]+
|
||||
|
@ -1068,7 +1066,7 @@ EQUALS_MINUS (?i:=\-)
|
|||
[\/]{DICT_ELEMENT_NO_PIPE}[\/][|] { BEGIN_PREVIOUS(); yyless(yyleng - 1); return p::make_DICT_ELEMENT_REGEXP(std::string(yytext, 1, yyleng-2), *driver.loc.back()); }
|
||||
['][\/]{DICT_ELEMENT_WITH_PIPE}[\/]['] { BEGIN_PREVIOUS(); yyless(yyleng - 0); return p::make_DICT_ELEMENT_REGEXP(std::string(yytext, 2, yyleng-4), *driver.loc.back()); }
|
||||
['][\/]{DICT_ELEMENT_WITH_PIPE}[\/]['][|] { BEGIN_PREVIOUS(); yyless(yyleng - 1); return p::make_DICT_ELEMENT_REGEXP(std::string(yytext, 2, yyleng-4), *driver.loc.back()); }
|
||||
{DICT_ELEMENT} { BEGIN_PREVIOUS(); return p::make_DICT_ELEMENT(yytext, *driver.loc.back()); }
|
||||
{DICT_ELEMENT_WITH_EQUALS} { BEGIN_PREVIOUS(); return p::make_DICT_ELEMENT(yytext, *driver.loc.back()); }
|
||||
|
||||
[\/]{DICT_ELEMENT_NO_PIPE}[\/][,] { BEGIN_PREVIOUS(); yyless(yyleng - 1); return p::make_DICT_ELEMENT_REGEXP(std::string(yytext, 1, yyleng-2), *driver.loc.back()); }
|
||||
['][\/]{DICT_ELEMENT_NO_PIPE}[\/]['][,] { BEGIN_PREVIOUS(); yyless(yyleng - 1); return p::make_DICT_ELEMENT_REGEXP(std::string(yytext, 2, yyleng-4), *driver.loc.back()); }
|
||||
|
@ -1257,7 +1255,8 @@ EQUALS_MINUS (?i:=\-)
|
|||
std::string err;
|
||||
std::string f = modsecurity::utils::find_resource(s, *driver.loc.back()->end.filename, &err);
|
||||
driver.loc.push_back(new yy::location());
|
||||
driver.loc.back()->begin.filename = driver.loc.back()->end.filename = new std::string(f);
|
||||
driver.m_filenames.push_back(f);
|
||||
driver.loc.back()->begin.filename = driver.loc.back()->end.filename = &(driver.m_filenames.back());
|
||||
yyin = fopen(f.c_str(), "r" );
|
||||
if (!yyin) {
|
||||
BEGIN(INITIAL);
|
||||
|
@ -1285,7 +1284,8 @@ EQUALS_MINUS (?i:=\-)
|
|||
for (auto& s: files) {
|
||||
std::string f = modsecurity::utils::find_resource(s, *driver.loc.back()->end.filename, &err);
|
||||
driver.loc.push_back(new yy::location());
|
||||
driver.loc.back()->begin.filename = driver.loc.back()->end.filename = new std::string(f);
|
||||
driver.m_filenames.push_back(f);
|
||||
driver.loc.back()->begin.filename = driver.loc.back()->end.filename = &(driver.m_filenames.back());
|
||||
|
||||
yyin = fopen(f.c_str(), "r" );
|
||||
if (!yyin) {
|
||||
|
@ -1314,7 +1314,8 @@ EQUALS_MINUS (?i:=\-)
|
|||
c.setKey(key);
|
||||
|
||||
driver.loc.push_back(new yy::location());
|
||||
driver.loc.back()->begin.filename = driver.loc.back()->end.filename = new std::string(url);
|
||||
driver.m_filenames.push_back(url);
|
||||
driver.loc.back()->begin.filename = driver.loc.back()->end.filename = &(driver.m_filenames.back());
|
||||
YY_BUFFER_STATE temp = YY_CURRENT_BUFFER;
|
||||
yypush_buffer_state(temp);
|
||||
|
||||
|
|
|
@ -29,17 +29,17 @@ std::string RuleMessage::_details(const RuleMessage *rm) {
|
|||
msg.append(" [file \"" + std::string(*rm->m_ruleFile.get()) + "\"]");
|
||||
msg.append(" [line \"" + std::to_string(rm->m_ruleLine) + "\"]");
|
||||
msg.append(" [id \"" + std::to_string(rm->m_ruleId) + "\"]");
|
||||
msg.append(" [rev \"" + rm->m_rev + "\"]");
|
||||
msg.append(" [rev \"" + utils::string::toHexIfNeeded(rm->m_rev, true) + "\"]");
|
||||
msg.append(" [msg \"" + rm->m_message + "\"]");
|
||||
msg.append(" [data \"" + utils::string::limitTo(200, rm->m_data) + "\"]");
|
||||
msg.append(" [data \"" + utils::string::toHexIfNeeded(utils::string::limitTo(200, rm->m_data), true) + "\"]");
|
||||
msg.append(" [severity \"" +
|
||||
std::to_string(rm->m_severity) + "\"]");
|
||||
msg.append(" [ver \"" + rm->m_ver + "\"]");
|
||||
msg.append(" [ver \"" + utils::string::toHexIfNeeded(rm->m_ver, true) + "\"]");
|
||||
msg.append(" [maturity \"" + std::to_string(rm->m_maturity) + "\"]");
|
||||
msg.append(" [accuracy \"" + std::to_string(rm->m_accuracy) + "\"]");
|
||||
|
||||
for (auto &a : rm->m_tags) {
|
||||
msg.append(" [tag \"" + a + "\"]");
|
||||
msg.append(" [tag \"" + utils::string::toHexIfNeeded(a, true) + "\"]");
|
||||
}
|
||||
|
||||
msg.append(" [hostname \"" + *rm->m_serverIpAddress.get() \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ModSecurity, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
* Copyright (c) 2015 - 2023 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
@ -101,11 +101,11 @@ namespace modsecurity {
|
|||
*/
|
||||
Transaction::Transaction(ModSecurity *ms, RulesSet *rules, void *logCbData)
|
||||
: m_creationTimeStamp(utils::cpu_seconds()),
|
||||
/* m_clientIpAddress(nullptr), */
|
||||
m_clientIpAddress(std::make_shared<std::string>("")),
|
||||
m_httpVersion(""),
|
||||
/* m_serverIpAddress(""), */
|
||||
m_serverIpAddress(std::make_shared<std::string>("")),
|
||||
m_uri(""),
|
||||
/* m_uri_no_query_string_decoded(""), */
|
||||
m_uri_no_query_string_decoded(std::make_shared<std::string>("")),
|
||||
m_ARGScombinedSizeDouble(0),
|
||||
m_clientPort(0),
|
||||
m_highestSeverityAction(255),
|
||||
|
@ -175,11 +175,11 @@ Transaction::Transaction(ModSecurity *ms, RulesSet *rules, void *logCbData)
|
|||
|
||||
Transaction::Transaction(ModSecurity *ms, RulesSet *rules, char *id, void *logCbData)
|
||||
: m_creationTimeStamp(utils::cpu_seconds()),
|
||||
/* m_clientIpAddress(""), */
|
||||
m_clientIpAddress(std::make_shared<std::string>("")),
|
||||
m_httpVersion(""),
|
||||
/* m_serverIpAddress(""), */
|
||||
m_serverIpAddress(std::make_shared<std::string>("")),
|
||||
m_uri(""),
|
||||
/* m_uri_no_query_string_decoded(""), */
|
||||
m_uri_no_query_string_decoded(std::make_shared<std::string>("")),
|
||||
m_ARGScombinedSizeDouble(0),
|
||||
m_clientPort(0),
|
||||
m_highestSeverityAction(255),
|
||||
|
@ -814,7 +814,8 @@ int Transaction::processRequestBody() {
|
|||
m_variableReqbodyError.set("1", 0);
|
||||
m_variableReqbodyErrorMsg.set("Request body excluding files is bigger than the maximum expected.", 0);
|
||||
m_variableInboundDataError.set("1", m_variableOffset);
|
||||
ms_dbg(5, "Request body excluding files is bigger than the maximum expected.");
|
||||
ms_dbg(5, "Request body excluding files is bigger than the maximum expected. Limit: " \
|
||||
+ std::to_string(m_rules->m_requestBodyNoFilesLimit.m_value));
|
||||
requestBodyNoFilesLimitExceeded = true;
|
||||
}
|
||||
}
|
||||
|
@ -901,7 +902,8 @@ int Transaction::processRequestBody() {
|
|||
m_variableReqbodyError.set("1", 0);
|
||||
m_variableReqbodyErrorMsg.set("Request body excluding files is bigger than the maximum expected.", 0);
|
||||
m_variableInboundDataError.set("1", m_variableOffset);
|
||||
ms_dbg(5, "Request body excluding files is bigger than the maximum expected.");
|
||||
ms_dbg(5, "Request body excluding files is bigger than the maximum expected. Limit: " \
|
||||
+ std::to_string(m_rules->m_requestBodyNoFilesLimit.m_value));
|
||||
} else {
|
||||
m_variableReqbodyError.set("0", m_variableOffset);
|
||||
m_variableReqbodyProcessorError.set("0", m_variableOffset);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ModSecurity, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
* Copyright (c) 2015 - 2022 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
@ -88,6 +88,10 @@ IpTree::~IpTree() {
|
|||
bool IpTree::addFromBuffer(std::istream *ss, std::string *error) {
|
||||
char *error_msg = NULL;
|
||||
for (std::string line; std::getline(*ss, line); ) {
|
||||
size_t comment_start = line.find('#');
|
||||
if (comment_start != std::string::npos) {
|
||||
line = line.substr(0, comment_start);
|
||||
}
|
||||
int res = add_ip_from_param(line.c_str(), &m_tree, &error_msg);
|
||||
if (res != 0) {
|
||||
if (error_msg != NULL) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||||
* Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
* Copyright (c) 2015 - 2023 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||||
*
|
||||
* You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
|
@ -259,6 +259,7 @@ int InsertNetmask(TreeNode *node, TreeNode *parent, TreeNode *new_node,
|
|||
|
||||
node->count++;
|
||||
node->netmasks = reinterpret_cast<unsigned char *>(malloc(node->count * sizeof(unsigned char)));
|
||||
memset(node->netmasks, 0, (node->count * sizeof(unsigned char)));
|
||||
|
||||
if(node->netmasks == NULL)
|
||||
return 0;
|
||||
|
@ -410,6 +411,7 @@ TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree
|
|||
node->count++;
|
||||
new_node = node;
|
||||
node->netmasks = reinterpret_cast<unsigned char *>(malloc(node->count * sizeof(unsigned char)));
|
||||
memset(node->netmasks, 0, (node->count * sizeof(unsigned char)));
|
||||
|
||||
if ((node->count -1) == 0) {
|
||||
node->netmasks[0] = netmask;
|
||||
|
@ -418,16 +420,16 @@ TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree
|
|||
|
||||
node->netmasks[node->count - 1] = netmask;
|
||||
|
||||
i = node->count - 2;
|
||||
while (i >= 0) {
|
||||
if (netmask < node->netmasks[i]) {
|
||||
node->netmasks[i + 1] = netmask;
|
||||
int index = node->count - 2;
|
||||
while (index >= 0) {
|
||||
if (netmask < node->netmasks[index]) {
|
||||
node->netmasks[index + 1] = netmask;
|
||||
break;
|
||||
}
|
||||
|
||||
node->netmasks[i + 1] = node->netmasks[i];
|
||||
node->netmasks[i] = netmask;
|
||||
i--;
|
||||
node->netmasks[index + 1] = node->netmasks[index];
|
||||
node->netmasks[index] = netmask;
|
||||
index--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -481,6 +483,7 @@ TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree
|
|||
}
|
||||
|
||||
i_node->netmasks = reinterpret_cast<unsigned char *>(malloc((node->count - i) * sizeof(unsigned char)));
|
||||
memset(i_node->netmasks, 0, ((node->count - i) * sizeof(unsigned char)));
|
||||
|
||||
if(i_node->netmasks == NULL) {
|
||||
free(new_node->prefix);
|
||||
|
|
|
@ -73,6 +73,7 @@ Regex::Regex(const std::string& pattern_, bool ignoreCase)
|
|||
PCRE2_SIZE erroroffset = 0;
|
||||
m_pc = pcre2_compile(pcre2_pattern, PCRE2_ZERO_TERMINATED,
|
||||
pcre2_options, &errornumber, &erroroffset, NULL);
|
||||
m_pcje = pcre2_jit_compile(m_pc, PCRE2_JIT_COMPLETE);
|
||||
#else
|
||||
const char *errptr = NULL;
|
||||
int erroffset;
|
||||
|
@ -111,15 +112,22 @@ Regex::~Regex() {
|
|||
|
||||
std::list<SMatch> Regex::searchAll(const std::string& s) const {
|
||||
std::list<SMatch> retList;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
#ifdef WITH_PCRE2
|
||||
PCRE2_SPTR pcre2_s = reinterpret_cast<PCRE2_SPTR>(s.c_str());
|
||||
PCRE2_SIZE offset = 0;
|
||||
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(m_pc, NULL);
|
||||
do {
|
||||
rc = pcre2_match(m_pc, pcre2_s, s.length(),
|
||||
if (m_pcje == 0) {
|
||||
rc = pcre2_jit_match(m_pc, pcre2_s, s.length(),
|
||||
offset, 0, match_data, NULL);
|
||||
}
|
||||
|
||||
if (m_pcje != 0 || rc == PCRE2_ERROR_JIT_STACKLIMIT) {
|
||||
rc = pcre2_match(m_pc, pcre2_s, s.length(),
|
||||
offset, PCRE2_NO_JIT, match_data, NULL);
|
||||
}
|
||||
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
||||
#else
|
||||
const char *subject = s.c_str();
|
||||
|
@ -159,7 +167,14 @@ bool Regex::searchOneMatch(const std::string& s, std::vector<SMatchCapture>& cap
|
|||
#ifdef WITH_PCRE2
|
||||
PCRE2_SPTR pcre2_s = reinterpret_cast<PCRE2_SPTR>(s.c_str());
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(m_pc, NULL);
|
||||
int rc = pcre2_match(m_pc, pcre2_s, s.length(), 0, 0, match_data, NULL);
|
||||
int rc = 0;
|
||||
if (m_pcje == 0) {
|
||||
rc = pcre2_jit_match(m_pc, pcre2_s, s.length(), 0, 0, match_data, NULL);
|
||||
}
|
||||
|
||||
if (m_pcje != 0 || rc == PCRE2_ERROR_JIT_STACKLIMIT) {
|
||||
rc = pcre2_match(m_pc, pcre2_s, s.length(), 0, PCRE2_NO_JIT, match_data, NULL);
|
||||
}
|
||||
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
||||
#else
|
||||
const char *subject = s.c_str();
|
||||
|
@ -270,9 +285,16 @@ int Regex::search(const std::string& s, SMatch *match) const {
|
|||
#ifdef WITH_PCRE2
|
||||
PCRE2_SPTR pcre2_s = reinterpret_cast<PCRE2_SPTR>(s.c_str());
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(m_pc, NULL);
|
||||
int ret = pcre2_match(m_pc, pcre2_s, s.length(),
|
||||
int ret = 0;
|
||||
if (m_pcje == 0) {
|
||||
ret = pcre2_match(m_pc, pcre2_s, s.length(),
|
||||
0, 0, match_data, NULL) > 0;
|
||||
}
|
||||
|
||||
if (m_pcje != 0 || ret == PCRE2_ERROR_JIT_STACKLIMIT) {
|
||||
ret = pcre2_match(m_pc, pcre2_s, s.length(),
|
||||
0, PCRE2_NO_JIT, match_data, NULL) > 0;
|
||||
}
|
||||
if (ret > 0) { // match
|
||||
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
||||
#else
|
||||
|
@ -297,7 +319,14 @@ int Regex::search(const std::string& s) const {
|
|||
#ifdef WITH_PCRE2
|
||||
PCRE2_SPTR pcre2_s = reinterpret_cast<PCRE2_SPTR>(s.c_str());
|
||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(m_pc, NULL);
|
||||
int rc = pcre2_match(m_pc, pcre2_s, s.length(), 0, 0, match_data, NULL);
|
||||
int rc = 0;
|
||||
if (m_pcje == 0) {
|
||||
rc = pcre2_jit_match(m_pc, pcre2_s, s.length(), 0, 0, match_data, NULL);
|
||||
}
|
||||
|
||||
if (m_pcje != 0 || rc == PCRE2_ERROR_JIT_STACKLIMIT) {
|
||||
rc = pcre2_match(m_pc, pcre2_s, s.length(), 0, PCRE2_NO_JIT, match_data, NULL);
|
||||
}
|
||||
pcre2_match_data_free(match_data);
|
||||
if (rc > 0) {
|
||||
return 1; // match
|
||||
|
|
|
@ -85,6 +85,7 @@ class Regex {
|
|||
private:
|
||||
#if WITH_PCRE2
|
||||
pcre2_code *m_pc;
|
||||
int m_pcje;
|
||||
#else
|
||||
pcre *m_pc = NULL;
|
||||
pcre_extra *m_pce = NULL;
|
||||
|
|
|
@ -135,13 +135,14 @@ std::string string_to_hex(const std::string& input) {
|
|||
return output;
|
||||
}
|
||||
|
||||
|
||||
std::string toHexIfNeeded(const std::string &str) {
|
||||
std::string toHexIfNeeded(const std::string &str, bool escape_spec) {
|
||||
// escape_spec: escape special chars or not
|
||||
// spec chars: '"' (quotation mark, ascii 34), '\' (backslash, ascii 92)
|
||||
std::stringstream res;
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
int c = (unsigned char)str.at(i);
|
||||
if (c < 32 || c > 126) {
|
||||
if (c < 32 || c > 126 || (escape_spec == true && (c == 34 || c == 92))) {
|
||||
res << "\\x" << std::setw(2) << std::setfill('0') << std::hex << c;
|
||||
} else {
|
||||
res << str.at(i);
|
||||
|
@ -267,7 +268,6 @@ void replaceAll(std::string *str, const std::string& from,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace string
|
||||
} // namespace utils
|
||||
} // namespace modsecurity
|
||||
|
|
|
@ -61,7 +61,7 @@ std::string dash_if_empty(const std::string *str);
|
|||
std::string limitTo(int amount, const std::string &str);
|
||||
std::string removeBracketsIfNeeded(std::string a);
|
||||
std::string string_to_hex(const std::string& input);
|
||||
std::string toHexIfNeeded(const std::string &str);
|
||||
std::string toHexIfNeeded(const std::string &str, bool escape_spec = false);
|
||||
std::string tolower(std::string str);
|
||||
std::string toupper(std::string str);
|
||||
std::vector<std::string> ssplit(std::string str, char delimiter);
|
||||
|
|
|
@ -60,7 +60,7 @@ ctunullpointer:src/rule_with_operator.cc:135
|
|||
ctunullpointer:src/rule_with_operator.cc:95
|
||||
passedByValue:src/variables/global.h:109
|
||||
passedByValue:src/variables/global.h:110
|
||||
passedByValue:src/parser/driver.cc:45
|
||||
passedByValue:src/parser/driver.cc:46
|
||||
passedByValue:test/common/modsecurity_test.cc:49
|
||||
passedByValue:test/common/modsecurity_test.cc:98
|
||||
unreadVariable:src/rule_with_operator.cc:219
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
127.0.0.1
|
||||
# Comment line
|
||||
10.10.10.1
|
||||
::1
|
||||
200.249.12.31
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
},
|
||||
"rules":[
|
||||
"SecRuleEngine On",
|
||||
"SecRemoteRules key https://gist.githubusercontent.com/zimmerle/a4c1ec028999f7df71d0cc80f4f271ca/raw/4c74363bf4eae974180f1a82007196e58729dd16/modsecurity-regression-test-secremoterules.txt",
|
||||
"SecRemoteRules key https://gist.githubusercontent.com/martinhsv/20705a36b7cfa8ff6d0dee0d4efce7e7/raw/faa96c7838b1fe972c1f0881efacbb440f9a4a5e/modsecurity-regression-rules.txt",
|
||||
"SecRule ARGS \"@contains somethingelse\" \"id:9,pass,t:trim\""
|
||||
]
|
||||
},
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
},
|
||||
"rules":[
|
||||
"SecRuleEngine On",
|
||||
"SecRule REMOTE_ADDR \"@ipMatchFromFile https://www.modsecurity.org/modsecurity-regression-test.txt\" \"id:1,phase:3,pass,t:trim\""
|
||||
"SecRule REMOTE_ADDR \"@ipMatchFromFile https://gist.githubusercontent.com/martinhsv/20705a36b7cfa8ff6d0dee0d4efce7e7/raw/b9321f190eb0e81b98cb65a56db3d7e0a4f59314/modsecurity-regression-ip-list.txt\" \"id:1,phase:3,pass,t:trim\""
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
[
|
||||
{
|
||||
"enabled":1,
|
||||
"version_min":300000,
|
||||
"title":"Testing XPath expression with equals sign",
|
||||
"expected":{
|
||||
"http_code": 403
|
||||
},
|
||||
"client":{
|
||||
"ip":"200.249.12.31",
|
||||
"port":123
|
||||
},
|
||||
"request":{
|
||||
"headers":{
|
||||
"Host":"localhost",
|
||||
"User-Agent":"curl/7.38.0",
|
||||
"Accept":"*/*",
|
||||
"Content-Type": "text/xml"
|
||||
},
|
||||
"uri":"/?key=value&key=other_value",
|
||||
"method":"POST",
|
||||
"body": [
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||
"<!DOCTYPE author [",
|
||||
"<!ELEMENT book ANY>",
|
||||
"<!ENTITY js SYSTEM \"/etc/passwd\">",
|
||||
"]>",
|
||||
"<bookstore>",
|
||||
"<some-tag>aaa</some-tag><some-tag>bbb</some-tag>",
|
||||
"</bookstore>"
|
||||
]
|
||||
|
||||
},
|
||||
"server":{
|
||||
"ip":"200.249.12.31",
|
||||
"port":80
|
||||
},
|
||||
"rules":[
|
||||
"SecRuleEngine On",
|
||||
"SecRequestBodyAccess On",
|
||||
"SecRule REQUEST_HEADERS:Content-Type \"^text/xml$\" \"id:500011,phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML\"",
|
||||
"SecRule XML://bookstore/*[local-name()='some-tag'] \"bbb\" \"id:500012,phase:3,t:none,t:lowercase,log,deny,status:403\""
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -8,7 +8,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
cc: ["gcc", "clang"]
|
||||
luaVersion: ["5.1", "5.2", "5.3", "luajit", "luajit-openresty"]
|
||||
luaVersion: ["5.1", "5.2", "5.3", "5.4", "luajit", "luajit-openresty"]
|
||||
include:
|
||||
- luaVersion: "luajit"
|
||||
runtestArgs: "LUA_INCLUDE_DIR=.lua/include/luajit-2.1"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
sudo: required
|
||||
dist: trusty
|
||||
dist: Focal
|
||||
|
||||
os: linux
|
||||
|
||||
|
@ -7,7 +7,6 @@ language: c
|
|||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
addons:
|
||||
apt:
|
||||
|
@ -18,6 +17,7 @@ addons:
|
|||
- libipc-run3-perl
|
||||
- lua5.1
|
||||
- lua5.1-dev
|
||||
- cmake
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
|
@ -31,6 +31,7 @@ env:
|
|||
- LUAJIT=1 LUA_DIR=/usr/local LUA_INCLUDE_DIR=$LUA_DIR/include/luajit-2.1 LUA_SUFFIX=--lua-suffix=jit
|
||||
|
||||
install:
|
||||
- sudo ln -s /usr/bin/cmake /usr/local/bin/cmake
|
||||
- if [ -n "$LUAJIT" ]; then git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git; fi
|
||||
- if [ -n "$LUAJIT" ]; then cd ./luajit2; fi
|
||||
- if [ -n "$LUAJIT" ]; then make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1); fi
|
||||
|
@ -47,8 +48,14 @@ install:
|
|||
- cd ..
|
||||
|
||||
script:
|
||||
- cppcheck -i ./luajit2 -i --force --error-exitcode=1 --enable=warning . > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- sh runtests.sh
|
||||
- cppcheck -i ./luajit2 --force --error-exitcode=1 --enable=warning . > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- bash runtests.sh
|
||||
- make
|
||||
- prove -Itests tests
|
||||
- TEST_LUA_USE_VALGRIND=1 prove -Itests tests > build.log 2>&1; export e=$?
|
||||
- cat build.log
|
||||
- grep -E '^==[0-9]+==' build.log; if [ "$?" == 0 ]; then exit 1; else exit $e; fi
|
||||
- cmake -DUSE_INTERNAL_FPCONV=1 .
|
||||
- make
|
||||
- prove -Itests tests
|
||||
- TEST_LUA_USE_VALGRIND=1 prove -Itests tests > build.log 2>&1; export e=$?
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# Windows: set LUA_DIR=c:\lua51
|
||||
|
||||
project(lua-cjson C)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance")
|
||||
option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON)
|
||||
|
|
|
@ -165,7 +165,7 @@ encode_escape_forward_slash
|
|||
|
||||
**default:** true
|
||||
|
||||
If enabled, forward slash '/' will be encoded as '\/'.
|
||||
If enabled, forward slash '/' will be encoded as '\\/'.
|
||||
|
||||
If disabled, forward slash '/' will be encoded as '/' (no escape is applied).
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,7 @@
|
|||
#define IEEE_8087
|
||||
#endif
|
||||
|
||||
#define MALLOC(n) xmalloc(n)
|
||||
#define MALLOC xmalloc
|
||||
|
||||
static void *xmalloc(size_t size)
|
||||
{
|
||||
|
@ -50,6 +50,10 @@ static pthread_mutex_t private_dtoa_lock[2] = {
|
|||
PTHREAD_MUTEX_INITIALIZER
|
||||
};
|
||||
|
||||
#define dtoa_get_threadno pthread_self
|
||||
void
|
||||
set_max_dtoa_threads(unsigned int n);
|
||||
|
||||
#define ACQUIRE_DTOA_LOCK(n) do { \
|
||||
int r = pthread_mutex_lock(&private_dtoa_lock[n]); \
|
||||
if (r) { \
|
||||
|
|
|
@ -55,7 +55,7 @@ static char locale_decimal_point = '.';
|
|||
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||
* not supported on some platforms. Use sprintf() instead - if the
|
||||
* locale does change, at least Lua CJSON won't crash. */
|
||||
static void fpconv_update_locale()
|
||||
static void fpconv_update_locale(void)
|
||||
{
|
||||
char buf[8];
|
||||
|
||||
|
@ -202,7 +202,7 @@ int fpconv_g_fmt(char *str, double num, int precision)
|
|||
return len;
|
||||
}
|
||||
|
||||
void fpconv_init()
|
||||
void fpconv_init(void)
|
||||
{
|
||||
fpconv_update_locale();
|
||||
}
|
||||
|
|
|
@ -7,12 +7,22 @@
|
|||
# define FPCONV_G_FMT_BUFSIZE 32
|
||||
|
||||
#ifdef USE_INTERNAL_FPCONV
|
||||
#ifdef MULTIPLE_THREADS
|
||||
#include "dtoa_config.h"
|
||||
#include <unistd.h>
|
||||
static inline void fpconv_init()
|
||||
{
|
||||
// Add one to try and avoid core id multiplier alignment
|
||||
set_max_dtoa_threads((sysconf(_SC_NPROCESSORS_CONF) + 1) * 3);
|
||||
}
|
||||
#else
|
||||
static inline void fpconv_init()
|
||||
{
|
||||
/* Do nothing - not required */
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
extern void fpconv_init();
|
||||
extern void fpconv_init(void);
|
||||
#endif
|
||||
|
||||
extern int fpconv_g_fmt(char*, double, int);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package = "lua-cjson"
|
||||
version = "2.1.0.9-1"
|
||||
version = "2.1.0.11-1"
|
||||
|
||||
source = {
|
||||
url = "git+https://github.com/openresty/lua-cjson",
|
||||
tag = "2.1.0.9",
|
||||
tag = "2.1.0.11",
|
||||
}
|
||||
|
||||
description = {
|
||||
|
@ -34,6 +34,9 @@ build = {
|
|||
-- Uncomment the line below on Solaris platforms if required.
|
||||
-- "USE_INTERNAL_ISINF"
|
||||
}
|
||||
},
|
||||
["cjson.safe"] = {
|
||||
sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" }
|
||||
}
|
||||
},
|
||||
install = {
|
|
@ -52,7 +52,7 @@
|
|||
#endif
|
||||
|
||||
#ifndef CJSON_VERSION
|
||||
#define CJSON_VERSION "2.1.0.9"
|
||||
#define CJSON_VERSION "2.1.0.11"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -82,6 +82,7 @@
|
|||
#define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1
|
||||
#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0
|
||||
#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1
|
||||
#define DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES 0
|
||||
|
||||
#ifdef DISABLE_INVALID_NUMBERS
|
||||
#undef DEFAULT_DECODE_INVALID_NUMBERS
|
||||
|
@ -165,6 +166,7 @@ typedef struct {
|
|||
int decode_invalid_numbers;
|
||||
int decode_max_depth;
|
||||
int decode_array_with_array_mt;
|
||||
int encode_skip_unsupported_value_types;
|
||||
} json_config_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -356,6 +358,16 @@ static int json_cfg_decode_array_with_array_mt(lua_State *l)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Configure how to treat invalid types */
|
||||
static int json_cfg_encode_skip_unsupported_value_types(lua_State *l)
|
||||
{
|
||||
json_config_t *cfg = json_arg_init(l, 1);
|
||||
|
||||
json_enum_option(l, 1, &cfg->encode_skip_unsupported_value_types, NULL, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Configures JSON encoding buffer persistence */
|
||||
static int json_cfg_encode_keep_buffer(lua_State *l)
|
||||
{
|
||||
|
@ -463,6 +475,7 @@ static void json_create_config(lua_State *l)
|
|||
cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT;
|
||||
cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT;
|
||||
cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH;
|
||||
cfg->encode_skip_unsupported_value_types = DEFAULT_ENCODE_SKIP_UNSUPPORTED_VALUE_TYPES;
|
||||
|
||||
#if DEFAULT_ENCODE_KEEP_BUFFER > 0
|
||||
strbuf_init(&cfg->encode_buf, 0);
|
||||
|
@ -627,7 +640,7 @@ static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
|
|||
current_depth);
|
||||
}
|
||||
|
||||
static void json_append_data(lua_State *l, json_config_t *cfg,
|
||||
static int json_append_data(lua_State *l, json_config_t *cfg,
|
||||
int current_depth, strbuf_t *json);
|
||||
|
||||
/* json_append_array args:
|
||||
|
@ -637,19 +650,24 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
|
|||
static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth,
|
||||
strbuf_t *json, int array_length)
|
||||
{
|
||||
int comma, i;
|
||||
int comma, i, json_pos, err;
|
||||
|
||||
strbuf_append_char(json, '[');
|
||||
|
||||
comma = 0;
|
||||
for (i = 1; i <= array_length; i++) {
|
||||
if (comma)
|
||||
json_pos = strbuf_length(json);
|
||||
if (comma++ > 0)
|
||||
strbuf_append_char(json, ',');
|
||||
else
|
||||
comma = 1;
|
||||
|
||||
lua_rawgeti(l, -1, i);
|
||||
json_append_data(l, cfg, current_depth, json);
|
||||
err = json_append_data(l, cfg, current_depth, json);
|
||||
if (err) {
|
||||
strbuf_set_length(json, json_pos);
|
||||
if (comma == 1) {
|
||||
comma = 0;
|
||||
}
|
||||
}
|
||||
lua_pop(l, 1);
|
||||
}
|
||||
|
||||
|
@ -697,7 +715,7 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
|
|||
static void json_append_object(lua_State *l, json_config_t *cfg,
|
||||
int current_depth, strbuf_t *json)
|
||||
{
|
||||
int comma, keytype;
|
||||
int comma, keytype, json_pos, err;
|
||||
|
||||
/* Object */
|
||||
strbuf_append_char(json, '{');
|
||||
|
@ -706,10 +724,9 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
|
|||
/* table, startkey */
|
||||
comma = 0;
|
||||
while (lua_next(l, -2) != 0) {
|
||||
if (comma)
|
||||
json_pos = strbuf_length(json);
|
||||
if (comma++ > 0)
|
||||
strbuf_append_char(json, ',');
|
||||
else
|
||||
comma = 1;
|
||||
|
||||
/* table, key, value */
|
||||
keytype = lua_type(l, -2);
|
||||
|
@ -727,7 +744,14 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
|
|||
}
|
||||
|
||||
/* table, key, value */
|
||||
json_append_data(l, cfg, current_depth, json);
|
||||
err = json_append_data(l, cfg, current_depth, json);
|
||||
if (err) {
|
||||
strbuf_set_length(json, json_pos);
|
||||
if (comma == 1) {
|
||||
comma = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(l, 1);
|
||||
/* table, key */
|
||||
}
|
||||
|
@ -735,8 +759,8 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
|
|||
strbuf_append_char(json, '}');
|
||||
}
|
||||
|
||||
/* Serialise Lua data into JSON string. */
|
||||
static void json_append_data(lua_State *l, json_config_t *cfg,
|
||||
/* Serialise Lua data into JSON string. Return 1 if error an error happened, else 0 */
|
||||
static int json_append_data(lua_State *l, json_config_t *cfg,
|
||||
int current_depth, strbuf_t *json)
|
||||
{
|
||||
int len;
|
||||
|
@ -800,16 +824,22 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
|
|||
case LUA_TLIGHTUSERDATA:
|
||||
if (lua_touserdata(l, -1) == NULL) {
|
||||
strbuf_append_mem(json, "null", 4);
|
||||
} else if (lua_touserdata(l, -1) == &json_array) {
|
||||
} else if (lua_touserdata(l, -1) == json_lightudata_mask(&json_array)) {
|
||||
json_append_array(l, cfg, current_depth, json, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,
|
||||
* and LUA_TLIGHTUSERDATA) cannot be serialised */
|
||||
if (cfg->encode_skip_unsupported_value_types) {
|
||||
return 1;
|
||||
} else {
|
||||
json_encode_exception(l, cfg, json, -1, "type not supported");
|
||||
}
|
||||
|
||||
/* never returns */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_encode(lua_State *l)
|
||||
|
@ -1479,6 +1509,7 @@ static int lua_cjson_new(lua_State *l)
|
|||
{ "encode_invalid_numbers", json_cfg_encode_invalid_numbers },
|
||||
{ "decode_invalid_numbers", json_cfg_decode_invalid_numbers },
|
||||
{ "encode_escape_forward_slash", json_cfg_encode_escape_forward_slash },
|
||||
{ "encode_skip_unsupported_value_types", json_cfg_encode_skip_unsupported_value_types },
|
||||
{ "new", lua_cjson_new },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
|
|
@ -67,6 +67,16 @@ if [ -z "$SKIP_CMAKE" ]; then
|
|||
cp -r lua/cjson build/cjson.so tests
|
||||
do_tests
|
||||
rm -rf build tests/cjson{,.so}
|
||||
|
||||
echo "===== Testing Cmake fpconv build ====="
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DUSE_INTERNAL_FPCONV=1 ..
|
||||
make
|
||||
cd ..
|
||||
cp -r lua/cjson build/cjson.so tests
|
||||
do_tests
|
||||
rm -rf build tests/cjson{,.so}
|
||||
else
|
||||
echo "===== Skipping Cmake build ====="
|
||||
fi
|
||||
|
|
|
@ -150,7 +150,7 @@ static int calculate_new_size(strbuf_t *s, int len)
|
|||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
} else if (s->increment != 0) {
|
||||
/* Linear sizing */
|
||||
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ static char *strbuf_string(strbuf_t *s, int *len);
|
|||
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
static void strbuf_set_length(strbuf_t *s, int len);
|
||||
|
||||
/* Update */
|
||||
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||
|
@ -108,6 +109,11 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
|
|||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_set_length(strbuf_t *s, int len)
|
||||
{
|
||||
s->length = len;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
{
|
||||
s->length += len;
|
||||
|
|
|
@ -306,3 +306,29 @@ print(b)
|
|||
{"test":"http:\/\/google.com\/google"}
|
||||
{"test":"http://google.com/google"}
|
||||
{"test":"http:\/\/google.com\/google"}
|
||||
|
||||
|
||||
|
||||
=== TEST 22: disable error on invalid type
|
||||
--- lua
|
||||
local cjson = require "cjson"
|
||||
local f = function (x) return 2*x end
|
||||
local res, err = pcall(cjson.encode, f)
|
||||
print(err)
|
||||
local t = {f = f, valid = "valid"}
|
||||
local res, err = pcall(cjson.encode, t)
|
||||
print(err)
|
||||
local arr = {"one", "two", f, "three"}
|
||||
local res, err = pcall(cjson.encode, arr)
|
||||
print(err)
|
||||
cjson.encode_skip_unsupported_value_types(true)
|
||||
print(cjson.encode(f))
|
||||
print(cjson.encode(t))
|
||||
print(cjson.encode(arr))
|
||||
--- out
|
||||
Cannot serialise function: type not supported
|
||||
Cannot serialise function: type not supported
|
||||
Cannot serialise function: type not supported
|
||||
|
||||
{"valid":"valid"}
|
||||
["one","two","three"]
|
||||
|
|
|
@ -93,7 +93,7 @@ local cjson_tests = {
|
|||
-- Test API variables
|
||||
{ "Check module name, version",
|
||||
function () return json._NAME, json._VERSION end, { },
|
||||
true, { "cjson", "2.1.0.9" } },
|
||||
true, { "cjson", "2.1.0.11" } },
|
||||
|
||||
-- Test decoding simple types
|
||||
{ "Decode string",
|
||||
|
|
|
@ -64,8 +64,8 @@ before_install:
|
|||
- sudo apt install --only-upgrade ca-certificates
|
||||
- '! grep -n -P ''(?<=.{80}).+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Found C source lines exceeding 80 columns." > /dev/stderr; exit 1)'
|
||||
- '! grep -n -P ''\t+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Cannot use tabs." > /dev/stderr; exit 1)'
|
||||
- sudo cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
|
||||
|
||||
- /usr/bin/env perl $(command -v cpanm) --sudo --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- pyenv global 2.7
|
||||
install:
|
||||
- if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then wget -P download-cache http://openresty.org/download/drizzle7-$DRIZZLE_VER.tar.gz; fi
|
||||
- if [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache https://downloads.sourceforge.net/project/pcre/pcre/${PCRE_VER}/pcre-${PCRE_VER}.tar.gz; fi
|
||||
|
@ -143,4 +143,4 @@ script:
|
|||
- dig +short myip.opendns.com @resolver1.opendns.com || exit 0
|
||||
- dig +short @$TEST_NGINX_RESOLVER openresty.org || exit 0
|
||||
- dig +short @$TEST_NGINX_RESOLVER agentzh.org || exit 0
|
||||
- prove -I. -Itest-nginx/lib -r t/
|
||||
- /usr/bin/env perl $(command -v prove) -I. -Itest-nginx/lib -r t/
|
||||
|
|
|
@ -8200,7 +8200,7 @@ tcpsock:setoption
|
|||
|
||||
**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua**
|
||||
|
||||
This function is added for [LuaSocket](http://w3.impa.br/~diego/software/luasocket/tcp.html) API compatibility and does nothing for now. Its functionality is implemented `v0.10.18`.
|
||||
This function is added for [LuaSocket](http://w3.impa.br/~diego/software/luasocket/tcp.html) API compatibility, its functionality is implemented `v0.10.18`.
|
||||
|
||||
This feature was first introduced in the `v0.5.0rc1` release.
|
||||
|
||||
|
@ -9034,7 +9034,7 @@ ngx.worker.pids
|
|||
|
||||
**context:** *set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, exit_worker_by_lua**
|
||||
|
||||
This function returns a Lua table for all Nginx worker process ID (PID). Nginx uses channel to send the current worker PID to another worker in the worker process start or restart. So this API can get all current worker PID.
|
||||
This function returns a Lua table for all Nginx worker process IDs (PIDs). Nginx uses channel to send the current worker PID to another worker in the worker process start or restart. So this API can get all current worker PIDs. Windows does not have this API.
|
||||
|
||||
This API was first introduced in the `0.10.23` release.
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ END
|
|||
case "$NGX_PLATFORM" in
|
||||
Darwin:*)
|
||||
case "$NGX_MACHINE" in
|
||||
amd64 | arm64 | x86_64 | i386)
|
||||
amd64 | x86_64 | i386)
|
||||
echo "adding extra linking options needed by LuaJIT on $NGX_MACHINE"
|
||||
luajit_ld_opt="$luajit_ld_opt -pagezero_size 10000 -image_base 100000000"
|
||||
ngx_feature_libs="$ngx_feature_libs -pagezero_size 10000 -image_base 100000000"
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
/* Public API for other Nginx modules */
|
||||
|
||||
|
||||
#define ngx_http_lua_version 10023
|
||||
#define ngx_http_lua_version 10024
|
||||
|
||||
|
||||
typedef struct ngx_http_lua_co_ctx_s ngx_http_lua_co_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
@ -56,6 +59,17 @@ ngx_shm_zone_t *ngx_http_lua_find_zone(u_char *name_data, size_t name_len);
|
|||
ngx_shm_zone_t *ngx_http_lua_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,
|
||||
size_t size, void *tag);
|
||||
|
||||
ngx_http_lua_co_ctx_t *ngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r);
|
||||
|
||||
void ngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r,
|
||||
ngx_http_lua_co_ctx_t *coctx);
|
||||
|
||||
lua_State *ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx);
|
||||
|
||||
void ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets);
|
||||
|
||||
int ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r);
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_LUA_API_H_INCLUDED_ */
|
||||
|
||||
|
|
|
@ -213,4 +213,132 @@ ngx_http_lua_shared_memory_init(ngx_shm_zone_t *shm_zone, void *data)
|
|||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_http_lua_co_ctx_t *
|
||||
ngx_http_lua_get_cur_co_ctx(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_lua_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
|
||||
|
||||
return ctx->cur_co_ctx;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_http_lua_set_cur_co_ctx(ngx_http_request_t *r, ngx_http_lua_co_ctx_t *coctx)
|
||||
{
|
||||
ngx_http_lua_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
|
||||
|
||||
coctx->data = r;
|
||||
|
||||
ctx->cur_co_ctx = coctx;
|
||||
}
|
||||
|
||||
|
||||
lua_State *
|
||||
ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx)
|
||||
{
|
||||
return coctx->co;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_lua_co_ctx_resume(ngx_http_request_t *r)
|
||||
{
|
||||
lua_State *vm;
|
||||
ngx_connection_t *c;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t nreqs;
|
||||
ngx_http_lua_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->resume_handler = ngx_http_lua_wev_handler;
|
||||
|
||||
c = r->connection;
|
||||
vm = ngx_http_lua_get_lua_vm(r, ctx);
|
||||
nreqs = c->requests;
|
||||
|
||||
rc = ngx_http_lua_run_thread(vm, r, ctx, ctx->cur_co_ctx->nrets);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"lua run thread returned %d", rc);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
ngx_http_lua_finalize_request(r, NGX_DONE);
|
||||
return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs);
|
||||
}
|
||||
|
||||
if (ctx->entered_content_phase) {
|
||||
ngx_http_lua_finalize_request(r, rc);
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_lua_ctx_t *ctx;
|
||||
ngx_http_log_ctx_t *log_ctx;
|
||||
|
||||
r = coctx->data;
|
||||
c = r->connection;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->fd != (ngx_socket_t) -1) { /* not a fake connection */
|
||||
log_ctx = c->log->data;
|
||||
log_ctx->current_request = r;
|
||||
}
|
||||
|
||||
coctx->nrets = nrets;
|
||||
coctx->cleanup = NULL;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"lua coctx resume handler: \"%V?%V\"", &r->uri, &r->args);
|
||||
|
||||
ctx->cur_co_ctx = coctx;
|
||||
|
||||
if (ctx->entered_content_phase) {
|
||||
(void) ngx_http_lua_co_ctx_resume(r);
|
||||
|
||||
} else {
|
||||
ctx->resume_handler = ngx_http_lua_co_ctx_resume;
|
||||
ngx_http_core_run_phases(r);
|
||||
}
|
||||
|
||||
ngx_http_run_posted_requests(c);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_lua_loc_conf_t *llcf;
|
||||
|
||||
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
|
||||
|
||||
return llcf->http10_buffering;
|
||||
}
|
||||
|
||||
|
||||
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
|
||||
|
|
|
@ -497,6 +497,7 @@ struct ngx_http_lua_co_ctx_s {
|
|||
unsigned nresults_from_worker_thread; /* number of results
|
||||
* from worker
|
||||
* thread callback */
|
||||
unsigned nrets; /* ngx_http_lua_run_thread nrets arg. */
|
||||
|
||||
unsigned nsubreqs; /* number of subrequests of the
|
||||
* current request */
|
||||
|
|
|
@ -1250,8 +1250,10 @@ ngx_http_lua_ngx_raw_header_cleanup(void *data)
|
|||
int
|
||||
ngx_http_lua_ffi_set_resp_header_macos(ngx_http_lua_set_resp_header_params_t *p)
|
||||
{
|
||||
return ngx_http_lua_ffi_set_resp_header(p->r, p->key_data, p->key_len,
|
||||
p->is_nil, p->sval, p->sval_len,
|
||||
return ngx_http_lua_ffi_set_resp_header(p->r, (const u_char *) p->key_data,
|
||||
p->key_len, p->is_nil,
|
||||
(const u_char *) p->sval,
|
||||
p->sval_len,
|
||||
p->mvals, p->mvals_len,
|
||||
p->override, p->errmsg);
|
||||
}
|
||||
|
|
|
@ -2096,7 +2096,8 @@ ngx_http_lua_ffi_shdict_free_space(ngx_shm_zone_t *zone)
|
|||
int
|
||||
ngx_http_lua_ffi_shdict_get_macos(ngx_http_lua_shdict_get_params_t *p)
|
||||
{
|
||||
return ngx_http_lua_ffi_shdict_get(p->zone, p->key, p->key_len,
|
||||
return ngx_http_lua_ffi_shdict_get(p->zone,
|
||||
(u_char *) p->key, p->key_len,
|
||||
p->value_type, p->str_value_buf,
|
||||
p->str_value_len, p->num_value,
|
||||
p->user_flags, p->get_stale,
|
||||
|
@ -2107,8 +2108,10 @@ ngx_http_lua_ffi_shdict_get_macos(ngx_http_lua_shdict_get_params_t *p)
|
|||
int
|
||||
ngx_http_lua_ffi_shdict_store_macos(ngx_http_lua_shdict_store_params_t *p)
|
||||
{
|
||||
return ngx_http_lua_ffi_shdict_store(p->zone, p->op, p->key, p->key_len,
|
||||
p->value_type, p->str_value_buf,
|
||||
return ngx_http_lua_ffi_shdict_store(p->zone, p->op,
|
||||
(u_char *) p->key, p->key_len,
|
||||
p->value_type,
|
||||
(u_char *) p->str_value_buf,
|
||||
p->str_value_len, p->num_value,
|
||||
p->exptime, p->user_flags,
|
||||
p->errmsg, p->forcible);
|
||||
|
@ -2118,7 +2121,7 @@ ngx_http_lua_ffi_shdict_store_macos(ngx_http_lua_shdict_store_params_t *p)
|
|||
int
|
||||
ngx_http_lua_ffi_shdict_incr_macos(ngx_http_lua_shdict_incr_params_t *p)
|
||||
{
|
||||
return ngx_http_lua_ffi_shdict_incr(p->zone, p->key, p->key_len,
|
||||
return ngx_http_lua_ffi_shdict_incr(p->zone, (u_char *) p->key, p->key_len,
|
||||
p->num_value, p->errmsg,
|
||||
p->has_init, p->init, p->init_ttl,
|
||||
p->forcible);
|
||||
|
|
|
@ -1744,7 +1744,7 @@ ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,
|
|||
|
||||
/* read rest of the chain */
|
||||
|
||||
for (i = 1; i < sk_X509_num(chain); i++) {
|
||||
for (i = 1; i < (ngx_int_t) sk_X509_num(chain); i++) {
|
||||
x509 = sk_X509_value(chain, i);
|
||||
if (x509 == NULL) {
|
||||
ERR_clear_error();
|
||||
|
@ -6483,7 +6483,7 @@ ngx_http_lua_ffi_socket_tcp_getoption(ngx_http_lua_socket_tcp_upstream_t *u,
|
|||
|
||||
fd = u->peer.connection->fd;
|
||||
|
||||
if (fd == (ngx_socket_t) -1) {
|
||||
if (fd == (int) -1) {
|
||||
*errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
@ -6540,7 +6540,7 @@ ngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u,
|
|||
|
||||
fd = u->peer.connection->fd;
|
||||
|
||||
if (fd == (ngx_socket_t) -1) {
|
||||
if (fd == (int) -1) {
|
||||
*errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
@ -6601,7 +6601,7 @@ ngx_http_lua_ffi_socket_tcp_hack_fd(ngx_http_lua_socket_tcp_upstream_t *u,
|
|||
}
|
||||
|
||||
rc = u->peer.connection->fd;
|
||||
if (rc == (ngx_socket_t) -1) {
|
||||
if (rc == (int) -1) {
|
||||
*errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
#define DDEBUG 0
|
||||
#endif
|
||||
#include "ddebug.h"
|
||||
|
||||
#if !(NGX_WIN32)
|
||||
#include <ngx_channel.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_PROCESS_PRIVILEGED_AGENT 99
|
||||
|
@ -21,22 +24,25 @@ ngx_http_lua_ffi_worker_pid(void)
|
|||
}
|
||||
|
||||
|
||||
#if !(NGX_WIN32)
|
||||
int
|
||||
ngx_http_lua_ffi_worker_pids(int *pids, size_t *pids_len)
|
||||
{
|
||||
ngx_int_t i, n;
|
||||
size_t n;
|
||||
ngx_int_t i;
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < NGX_MAX_PROCESSES; i++) {
|
||||
for (i = 0; n < *pids_len && i < NGX_MAX_PROCESSES; i++) {
|
||||
if (i != ngx_process_slot && ngx_processes[i].pid == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ngx_process_slot && ngx_processes[i].pid == 0) {
|
||||
/* The current process */
|
||||
if (i == ngx_process_slot) {
|
||||
pids[n++] = ngx_pid;
|
||||
}
|
||||
|
||||
if (ngx_processes[i].pid > 0) {
|
||||
if (ngx_processes[i].channel[0] > 0 && ngx_processes[i].pid > 0) {
|
||||
pids[n++] = ngx_processes[i].pid;
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +55,7 @@ ngx_http_lua_ffi_worker_pids(int *pids, size_t *pids_len)
|
|||
|
||||
return NGX_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int
|
||||
|
|
49
src/deps/src/lua-nginx-module/t/122-worker-2.t
Normal file
49
src/deps/src/lua-nginx-module/t/122-worker-2.t
Normal file
|
@ -0,0 +1,49 @@
|
|||
# vim:set ft= ts=4 sw=4 et fdm=marker:
|
||||
|
||||
use Test::Nginx::Socket::Lua;
|
||||
|
||||
#worker_connections(1014);
|
||||
master_on();
|
||||
workers(4);
|
||||
#log_level('warn');
|
||||
|
||||
repeat_each(2);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3);
|
||||
|
||||
#no_diff();
|
||||
no_long_string();
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: get worker pids with multiple worker
|
||||
--- config
|
||||
location /lua {
|
||||
content_by_lua_block {
|
||||
local pids, err = ngx.worker.pids()
|
||||
if err ~= nil then
|
||||
return
|
||||
end
|
||||
local pid = ngx.worker.pid()
|
||||
ngx.say("worker pid: ", pid)
|
||||
local count = ngx.worker.count()
|
||||
ngx.say("worker count: ", count)
|
||||
ngx.say("worker pids count: ", #pids)
|
||||
for i = 1, count do
|
||||
if pids[i] == pid then
|
||||
ngx.say("worker pid is correct.")
|
||||
return
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /lua
|
||||
--- response_body_like
|
||||
worker pid: \d+
|
||||
worker count: 4
|
||||
worker pids count: 4
|
||||
worker pid is correct\.
|
||||
--- no_error_log
|
||||
[error]
|
58
src/deps/src/lua-nginx-module/t/122-worker-3.t
Normal file
58
src/deps/src/lua-nginx-module/t/122-worker-3.t
Normal file
|
@ -0,0 +1,58 @@
|
|||
# vim:set ft= ts=4 sw=4 et fdm=marker:
|
||||
our $SkipReason;
|
||||
|
||||
BEGIN {
|
||||
if ($ENV{TEST_NGINX_CHECK_LEAK}) {
|
||||
$SkipReason = "unavailable for the hup tests";
|
||||
|
||||
} else {
|
||||
$ENV{TEST_NGINX_USE_HUP} = 1;
|
||||
undef $ENV{TEST_NGINX_USE_STAP};
|
||||
}
|
||||
}
|
||||
|
||||
use Test::Nginx::Socket::Lua 'no_plan';
|
||||
|
||||
#worker_connections(1014);
|
||||
master_on();
|
||||
workers(4);
|
||||
#log_level('warn');
|
||||
|
||||
repeat_each(2);
|
||||
|
||||
#no_diff();
|
||||
no_long_string();
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: get worker pids with multiple worker
|
||||
--- config
|
||||
location /lua {
|
||||
content_by_lua_block {
|
||||
local pids, err = ngx.worker.pids()
|
||||
if err ~= nil then
|
||||
return
|
||||
end
|
||||
local pid = ngx.worker.pid()
|
||||
ngx.say("worker pid: ", pid)
|
||||
local count = ngx.worker.count()
|
||||
ngx.say("worker count: ", count)
|
||||
ngx.say("worker pids count: ", #pids)
|
||||
for i = 1, count do
|
||||
if pids[i] == pid then
|
||||
ngx.say("worker pid is correct.")
|
||||
return
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /lua
|
||||
--- response_body_like
|
||||
worker pid: \d+
|
||||
worker count: 4
|
||||
worker pids count: 4
|
||||
worker pid is correct\.
|
||||
--- no_error_log
|
||||
[error]
|
|
@ -18,8 +18,8 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
openresty_version:
|
||||
- 1.17.8.1
|
||||
- 1.19.3.1
|
||||
- 1.17.8.2
|
||||
- 1.19.9.1
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
|
@ -28,9 +28,10 @@ jobs:
|
|||
options: --init
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install deps
|
||||
run: |
|
||||
apk add --no-cache curl perl bash wget git perl-dev libarchive-tools
|
||||
apk add --no-cache curl perl bash wget git perl-dev libarchive-tools nodejs
|
||||
ln -s /usr/bin/bsdtar /usr/bin/tar
|
||||
|
||||
- name: Install CPAN
|
||||
|
@ -48,14 +49,12 @@ jobs:
|
|||
run: cpanm -q -n Test::Nginx
|
||||
|
||||
- name: Install Luacov
|
||||
run: luarocks install luacov
|
||||
run: /usr/local/openresty/luajit/bin/luarocks install luacov
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
TEST_COVERAGE: '1'
|
||||
run: /usr/bin/prove -I../test-nginx/lib -r t/
|
||||
run: make coverage
|
||||
|
||||
- name: Coverage
|
||||
run: |
|
||||
|
|
|
@ -19,6 +19,7 @@ Production ready.
|
|||
* Request pipelining
|
||||
* Trailers
|
||||
* HTTP proxy connections
|
||||
* mTLS (requires `ngx_lua_http_module` >= v0.10.23)
|
||||
|
||||
|
||||
## API
|
||||
|
@ -91,9 +92,9 @@ local body = res.body
|
|||
local httpc = require("resty.http").new()
|
||||
|
||||
-- First establish a connection
|
||||
local ok, err = httpc:connect({
|
||||
local ok, err, ssl_session = httpc:connect({
|
||||
scheme = "https",
|
||||
host = "127.0.0.1"
|
||||
host = "127.0.0.1",
|
||||
port = 8080,
|
||||
})
|
||||
if not ok then
|
||||
|
@ -153,7 +154,7 @@ Creates the HTTP connection object. In case of failures, returns `nil` and a str
|
|||
|
||||
## connect
|
||||
|
||||
`syntax: ok, err = httpc:connect(options)`
|
||||
`syntax: ok, err, ssl_session = httpc:connect(options)`
|
||||
|
||||
Attempts to connect to the web server while incorporating the following activities:
|
||||
|
||||
|
@ -172,9 +173,12 @@ The options table has the following fields:
|
|||
* `pool_size`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
|
||||
* `backlog`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
|
||||
* `proxy_opts`: sub-table, defaults to the global proxy options set, see [set\_proxy\_options](#set_proxy_options).
|
||||
* `ssl_reused_session`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
|
||||
* `ssl_verify`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake), except that it defaults to `true`.
|
||||
* `ssl_server_name`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
|
||||
* `ssl_send_status_req`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
|
||||
* `ssl_client_cert`: will be passed to `tcpsock:setclientcert`. Requires `ngx_lua_http_module` >= v0.10.23.
|
||||
* `ssl_client_priv_key`: as above.
|
||||
|
||||
## set\_timeout
|
||||
|
||||
|
@ -251,6 +255,8 @@ When the request is successful, `res` will contain the following fields:
|
|||
* `read_body`: A method to read the entire body into a string.
|
||||
* `read_trailers`: A method to merge any trailers underneath the headers, after reading the body.
|
||||
|
||||
If the response has a body, then before the same connection can be used for another request, you must read the body using `read_body` or `body_reader`.
|
||||
|
||||
## request\_uri
|
||||
|
||||
`syntax: res, err = httpc:request_uri(uri, params)`
|
||||
|
|
|
@ -106,7 +106,7 @@ end
|
|||
|
||||
|
||||
local _M = {
|
||||
_VERSION = '0.16.1',
|
||||
_VERSION = '0.17.1',
|
||||
}
|
||||
_M._USER_AGENT = "lua-resty-http/" .. _M._VERSION .. " (Lua) ngx_lua/" .. ngx.config.ngx_lua_version
|
||||
|
||||
|
@ -165,7 +165,7 @@ end
|
|||
do
|
||||
local aio_connect = require "resty.http_connect"
|
||||
-- Function signatures to support:
|
||||
-- ok, err = httpc:connect(options_table)
|
||||
-- ok, err, ssl_session = httpc:connect(options_table)
|
||||
-- ok, err = httpc:connect(host, port, options_table?)
|
||||
-- ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)
|
||||
function _M.connect(self, options, ...)
|
||||
|
@ -313,8 +313,10 @@ local function _format_request(self, params)
|
|||
|
||||
local query = params.query or ""
|
||||
if type(query) == "table" then
|
||||
query = "?" .. ngx_encode_args(query)
|
||||
elseif query ~= "" and str_sub(query, 1, 1) ~= "?" then
|
||||
query = ngx_encode_args(query)
|
||||
end
|
||||
|
||||
if query ~= "" and str_sub(query, 1, 1) ~= "?" then
|
||||
query = "?" .. query
|
||||
end
|
||||
|
||||
|
@ -362,7 +364,21 @@ local function _receive_status(sock)
|
|||
return nil, nil, nil, err
|
||||
end
|
||||
|
||||
return tonumber(str_sub(line, 10, 12)), tonumber(str_sub(line, 6, 8)), str_sub(line, 14)
|
||||
local version = tonumber(str_sub(line, 6, 8))
|
||||
if not version then
|
||||
return nil, nil, nil,
|
||||
"couldn't parse HTTP version from response status line: " .. line
|
||||
end
|
||||
|
||||
local status = tonumber(str_sub(line, 10, 12))
|
||||
if not status then
|
||||
return nil, nil, nil,
|
||||
"couldn't parse status code from response status line: " .. line
|
||||
end
|
||||
|
||||
local reason = str_sub(line, 14)
|
||||
|
||||
return status, version, reason
|
||||
end
|
||||
|
||||
|
||||
|
@ -621,18 +637,23 @@ end
|
|||
local function _handle_continue(sock, body)
|
||||
local status, version, reason, err = _receive_status(sock) --luacheck: no unused
|
||||
if not status then
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
end
|
||||
|
||||
-- Only send body if we receive a 100 Continue
|
||||
if status == 100 then
|
||||
local ok, err = sock:receive("*l") -- Read carriage return
|
||||
-- Read headers
|
||||
local headers, err = _receive_headers(sock)
|
||||
if not headers then
|
||||
return nil, nil, nil, err
|
||||
end
|
||||
|
||||
local ok, err = _send_body(sock, body)
|
||||
if not ok then
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
end
|
||||
_send_body(sock, body)
|
||||
end
|
||||
return status, version, err
|
||||
return status, version, reason, err
|
||||
end
|
||||
|
||||
|
||||
|
@ -750,11 +771,11 @@ function _M.read_response(self, params)
|
|||
-- If we expect: continue, we need to handle this, sending the body if allowed.
|
||||
-- If we don't get 100 back, then status is the actual status.
|
||||
if params.headers["Expect"] == "100-continue" then
|
||||
local _status, _version, _err = _handle_continue(sock, params.body)
|
||||
local _status, _version, _reason, _err = _handle_continue(sock, params.body)
|
||||
if not _status then
|
||||
return nil, _err
|
||||
elseif _status ~= 100 then
|
||||
status, version, err = _status, _version, _err -- luacheck: no unused
|
||||
status, version, reason, err = _status, _version, _reason, _err -- luacheck: no unused
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
local ngx_re_gmatch = ngx.re.gmatch
|
||||
local ngx_re_sub = ngx.re.sub
|
||||
local ngx_re_find = ngx.re.find
|
||||
local ngx_log = ngx.log
|
||||
local ngx_WARN = ngx.WARN
|
||||
|
||||
--[[
|
||||
A connection function that incorporates:
|
||||
|
@ -22,11 +24,17 @@ client:connect {
|
|||
backlog = nil,
|
||||
|
||||
-- ssl options as per: https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake
|
||||
ssl_reused_session = nil
|
||||
ssl_server_name = nil,
|
||||
ssl_send_status_req = nil,
|
||||
ssl_verify = true, -- NOTE: defaults to true
|
||||
ctx = nil, -- NOTE: not supported
|
||||
|
||||
-- mTLS options: These require support for mTLS in cosockets, which first
|
||||
-- appeared in `ngx_http_lua_module` v0.10.23.
|
||||
ssl_client_cert = nil,
|
||||
ssl_client_priv_key = nil,
|
||||
|
||||
proxy_opts, -- proxy opts, defaults to global proxy options
|
||||
}
|
||||
]]
|
||||
|
@ -53,15 +61,19 @@ local function connect(self, options)
|
|||
end
|
||||
|
||||
-- ssl settings
|
||||
local ssl, ssl_server_name, ssl_verify, ssl_send_status_req
|
||||
local ssl, ssl_reused_session, ssl_server_name
|
||||
local ssl_verify, ssl_send_status_req, ssl_client_cert, ssl_client_priv_key
|
||||
if request_scheme == "https" then
|
||||
ssl = true
|
||||
ssl_reused_session = options.ssl_reused_session
|
||||
ssl_server_name = options.ssl_server_name
|
||||
ssl_send_status_req = options.ssl_send_status_req
|
||||
ssl_verify = true -- default
|
||||
if options.ssl_verify == false then
|
||||
ssl_verify = false
|
||||
end
|
||||
ssl_client_cert = options.ssl_client_cert
|
||||
ssl_client_priv_key = options.ssl_client_priv_key
|
||||
end
|
||||
|
||||
-- proxy related settings
|
||||
|
@ -133,9 +145,10 @@ local function connect(self, options)
|
|||
end
|
||||
|
||||
if proxy then
|
||||
local proxy_uri_t, err = self:parse_uri(proxy_uri)
|
||||
local proxy_uri_t
|
||||
proxy_uri_t, err = self:parse_uri(proxy_uri)
|
||||
if not proxy_uri_t then
|
||||
return nil, err
|
||||
return nil, "uri parse error: ", err
|
||||
end
|
||||
|
||||
local proxy_scheme = proxy_uri_t[1]
|
||||
|
@ -169,7 +182,9 @@ local function connect(self, options)
|
|||
-- proxy based connection
|
||||
ok, err = sock:connect(proxy_host, proxy_port, tcp_opts)
|
||||
if not ok then
|
||||
return nil, err
|
||||
return nil, "failed to connect to: " .. (proxy_host or "") ..
|
||||
":" .. (proxy_port or "") ..
|
||||
": ", err
|
||||
end
|
||||
|
||||
if ssl and sock:getreusedtimes() == 0 then
|
||||
|
@ -178,7 +193,8 @@ local function connect(self, options)
|
|||
-- authority-form of RFC 7230 Section 5.3.3. See also RFC 7231 Section
|
||||
-- 4.3.6 for more details about the CONNECT request
|
||||
local destination = request_host .. ":" .. request_port
|
||||
local res, err = self:request({
|
||||
local res
|
||||
res, err = self:request({
|
||||
method = "CONNECT",
|
||||
path = destination,
|
||||
headers = {
|
||||
|
@ -188,7 +204,7 @@ local function connect(self, options)
|
|||
})
|
||||
|
||||
if not res then
|
||||
return nil, err
|
||||
return nil, "failed to issue CONNECT to proxy:", err
|
||||
end
|
||||
|
||||
if res.status < 200 or res.status > 299 then
|
||||
|
@ -211,10 +227,25 @@ local function connect(self, options)
|
|||
end
|
||||
end
|
||||
|
||||
local ssl_session
|
||||
-- Now do the ssl handshake
|
||||
if ssl and sock:getreusedtimes() == 0 then
|
||||
local ok, err = sock:sslhandshake(nil, ssl_server_name, ssl_verify, ssl_send_status_req)
|
||||
|
||||
-- Experimental mTLS support
|
||||
if ssl_client_cert and ssl_client_priv_key then
|
||||
if type(sock.setclientcert) ~= "function" then
|
||||
ngx_log(ngx_WARN, "cannot use SSL client cert and key without mTLS support")
|
||||
|
||||
else
|
||||
ok, err = sock:setclientcert(ssl_client_cert, ssl_client_priv_key)
|
||||
if not ok then
|
||||
ngx_log(ngx_WARN, "could not set client certificate: ", err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ssl_session, err = sock:sslhandshake(ssl_reused_session, ssl_server_name, ssl_verify, ssl_send_status_req)
|
||||
if not ssl_session then
|
||||
self:close()
|
||||
return nil, err
|
||||
end
|
||||
|
@ -228,7 +259,7 @@ local function connect(self, options)
|
|||
self.http_proxy_auth = request_scheme ~= "https" and proxy_authorization or nil
|
||||
self.path_prefix = path_prefix
|
||||
|
||||
return true
|
||||
return true, nil, ssl_session
|
||||
end
|
||||
|
||||
return connect
|
||||
|
|
|
@ -4,7 +4,7 @@ local rawget, rawset, setmetatable =
|
|||
local str_lower = string.lower
|
||||
|
||||
local _M = {
|
||||
_VERSION = '0.16.1',
|
||||
_VERSION = '0.17.1',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package = "lua-resty-http"
|
||||
version = "0.16.1-0"
|
||||
version = "0.17.1-0"
|
||||
source = {
|
||||
url = "git://github.com/ledgetech/lua-resty-http",
|
||||
tag = "v0.16.1"
|
||||
tag = "v0.17.1"
|
||||
}
|
||||
description = {
|
||||
summary = "Lua HTTP client cosocket driver for OpenResty / ngx_lua.",
|
|
@ -341,3 +341,93 @@ OK
|
|||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
|
||||
=== TEST 13: Should return error on invalid HTTP version in response status line
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
content_by_lua_block {
|
||||
local http = require "resty.http"
|
||||
local httpc = http.new()
|
||||
local res, err = httpc:request_uri("http://127.0.0.1:12345")
|
||||
|
||||
assert(err == "couldn't parse HTTP version from response status line: TEAPOT/1.1 OMG")
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 12345
|
||||
--- tcp_reply
|
||||
TEAPOT/1.1 OMG
|
||||
Server: Teapot
|
||||
|
||||
OK
|
||||
--- request
|
||||
GET /a
|
||||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
|
||||
=== TEST 14: Should return error on invalid status code in response status line
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
content_by_lua_block {
|
||||
local http = require "resty.http"
|
||||
local httpc = http.new()
|
||||
local res, err = httpc:request_uri("http://127.0.0.1:12345")
|
||||
|
||||
assert(err == "couldn't parse status code from response status line: HTTP/1.1 OMG")
|
||||
}
|
||||
}
|
||||
--- tcp_listen: 12345
|
||||
--- tcp_reply
|
||||
HTTP/1.1 OMG
|
||||
Server: Teapot
|
||||
|
||||
OK
|
||||
--- request
|
||||
GET /a
|
||||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
|
||||
|
||||
|
||||
=== TEST 14: Empty query
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
content_by_lua '
|
||||
local http = require "resty.http"
|
||||
local httpc = http.new()
|
||||
httpc:connect{
|
||||
scheme = "http",
|
||||
host = "127.0.0.1",
|
||||
port = ngx.var.server_port
|
||||
}
|
||||
|
||||
local res, err = httpc:request{
|
||||
query = {},
|
||||
path = "/b"
|
||||
}
|
||||
|
||||
ngx.status = res.status
|
||||
|
||||
ngx.print(ngx.header.test)
|
||||
|
||||
httpc:close()
|
||||
';
|
||||
}
|
||||
location = /b {
|
||||
content_by_lua '
|
||||
ngx.header.test = ngx.var.request_uri
|
||||
';
|
||||
}
|
||||
--- request
|
||||
GET /a
|
||||
--- response_headers
|
||||
/b
|
||||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
|
||||
|
||||
|
|
|
@ -205,7 +205,153 @@ Expectation Failed
|
|||
[warn]
|
||||
|
||||
|
||||
=== TEST 5: Non string request bodies are converted with correct length
|
||||
=== TEST 5: Return 100 Continue with headers
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
content_by_lua_block {
|
||||
local http = require "resty.http"
|
||||
local httpc = http.new()
|
||||
httpc:connect({
|
||||
scheme = "http",
|
||||
host = "127.0.0.1",
|
||||
port = ngx.var.server_port
|
||||
})
|
||||
|
||||
local res, err = httpc:request{
|
||||
body = "a=1&b=2&c=3",
|
||||
path = "/b",
|
||||
headers = {
|
||||
["Expect"] = "100-continue",
|
||||
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||
}
|
||||
}
|
||||
|
||||
if not res then
|
||||
ngx.log(ngx.ERR, "httpc:request failed: ", err)
|
||||
end
|
||||
|
||||
ngx.say(res.status)
|
||||
ngx.say(res:read_body())
|
||||
httpc:close()
|
||||
}
|
||||
}
|
||||
location = /b {
|
||||
content_by_lua_block {
|
||||
local len = ngx.req.get_headers()["Content-Length"]
|
||||
|
||||
local sock, err = ngx.req.socket(true)
|
||||
if not sock then
|
||||
ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err)
|
||||
return
|
||||
end
|
||||
|
||||
-- with additional header
|
||||
local ok, err = sock:send("HTTP/1.1 100 Continue\r\nConnection: keep-alive\r\n\r\n")
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "failed to send 100 response: ", err)
|
||||
end
|
||||
|
||||
local data, err = sock:receive(len)
|
||||
if not data then
|
||||
ngx.log(ngx.ERR, "failed to receive: ", err)
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = sock:send("HTTP/1.1 200 OK\r\n" ..
|
||||
"Content-Length: " .. len .. "\r\n" ..
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n\r\n" ..
|
||||
data)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "failed to send 200 response: ", err)
|
||||
return
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /a
|
||||
--- response_body
|
||||
200
|
||||
a=1&b=2&c=3
|
||||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
|
||||
|
||||
=== TEST 6: Return 100 Continue without headers
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
content_by_lua_block {
|
||||
local http = require "resty.http"
|
||||
local httpc = http.new()
|
||||
httpc:connect({
|
||||
scheme = "http",
|
||||
host = "127.0.0.1",
|
||||
port = ngx.var.server_port
|
||||
})
|
||||
|
||||
local res, err = httpc:request{
|
||||
body = "a=1&b=2&c=3",
|
||||
path = "/b",
|
||||
headers = {
|
||||
["Expect"] = "100-continue",
|
||||
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||
}
|
||||
}
|
||||
|
||||
if not res then
|
||||
ngx.log(ngx.ERR, "httpc:request failed: ", err)
|
||||
end
|
||||
|
||||
ngx.say(res.status)
|
||||
ngx.say(res:read_body())
|
||||
httpc:close()
|
||||
}
|
||||
}
|
||||
location = /b {
|
||||
content_by_lua_block {
|
||||
local len = ngx.req.get_headers()["Content-Length"]
|
||||
|
||||
local sock, err = ngx.req.socket(true)
|
||||
if not sock then
|
||||
ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err)
|
||||
return
|
||||
end
|
||||
|
||||
-- without additional headers
|
||||
local ok, err = sock:send("HTTP/1.1 100 Continue\r\n\r\n")
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "failed to send 100 response: ", err)
|
||||
end
|
||||
|
||||
local data, err = sock:receive(len)
|
||||
if not data then
|
||||
ngx.log(ngx.ERR, "failed to receive: ", err)
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = sock:send("HTTP/1.1 200 OK\r\n" ..
|
||||
"Content-Length: " .. len .. "\r\n" ..
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n\r\n" ..
|
||||
data)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "failed to send 200 response: ", err)
|
||||
return
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /a
|
||||
--- response_body
|
||||
200
|
||||
a=1&b=2&c=3
|
||||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
|
||||
|
||||
=== TEST 7: Non string request bodies are converted with correct length
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
|
@ -247,7 +393,7 @@ mix123edtable
|
|||
[warn]
|
||||
|
||||
|
||||
=== TEST 6: Request body as iterator
|
||||
=== TEST 8: Request body as iterator
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
|
@ -284,7 +430,7 @@ foobar
|
|||
[warn]
|
||||
|
||||
|
||||
=== TEST 7: Request body as iterator, errors with missing length
|
||||
=== TEST 9: Request body as iterator, errors with missing length
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
|
@ -319,7 +465,7 @@ Request body is a function but a length or chunked encoding is not specified
|
|||
[warn]
|
||||
|
||||
|
||||
=== TEST 8: Request body as iterator with chunked encoding
|
||||
=== TEST 10: Request body as iterator with chunked encoding
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /a {
|
||||
|
|
136
src/deps/src/lua-resty-http/t/19-ssl_reused_session.t
Normal file
136
src/deps/src/lua-resty-http/t/19-ssl_reused_session.t
Normal file
|
@ -0,0 +1,136 @@
|
|||
use Test::Nginx::Socket::Lua 'no_plan';
|
||||
use Cwd qw(abs_path realpath);
|
||||
use File::Basename;
|
||||
|
||||
$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
|
||||
|
||||
$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';
|
||||
$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__)));
|
||||
$ENV{TEST_COVERAGE} ||= 0;
|
||||
|
||||
my $realpath = realpath();
|
||||
|
||||
our $HttpConfig = qq{
|
||||
lua_package_path "$realpath/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
|
||||
init_by_lua_block {
|
||||
if $ENV{TEST_COVERAGE} == 1 then
|
||||
jit.off()
|
||||
require("luacov.runner").init()
|
||||
end
|
||||
|
||||
TEST_SERVER_SOCK = "unix:/$ENV{TEST_NGINX_HTML_DIR}/nginx.sock"
|
||||
|
||||
num_handshakes = 0
|
||||
}
|
||||
|
||||
server {
|
||||
listen unix:$ENV{TEST_NGINX_HTML_DIR}/nginx.sock ssl;
|
||||
server_name example.com;
|
||||
ssl_certificate $ENV{TEST_NGINX_CERT_DIR}/cert/test.crt;
|
||||
ssl_certificate_key $ENV{TEST_NGINX_CERT_DIR}/cert/test.key;
|
||||
ssl_session_tickets off;
|
||||
|
||||
server_tokens off;
|
||||
}
|
||||
};
|
||||
|
||||
no_long_string();
|
||||
#no_diff();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: connect returns session userdata
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
server_tokens off;
|
||||
resolver $TEST_NGINX_RESOLVER ipv6=off;
|
||||
lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
|
||||
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local httpc = assert(require("resty.http").new())
|
||||
local ok, err, session = assert(httpc:connect {
|
||||
scheme = "https",
|
||||
host = TEST_SERVER_SOCK,
|
||||
})
|
||||
|
||||
assert(type(session) == "userdata" or type(session) == "cdata", "expected session to be userdata or cdata")
|
||||
assert(httpc:close())
|
||||
}
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
[alert]
|
||||
|
||||
|
||||
=== TEST 2: ssl_reused_session false does not return session userdata
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
server_tokens off;
|
||||
resolver $TEST_NGINX_RESOLVER ipv6=off;
|
||||
lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
|
||||
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local httpc = assert(require("resty.http").new())
|
||||
local ok, err, session = assert(httpc:connect {
|
||||
scheme = "https",
|
||||
host = TEST_SERVER_SOCK,
|
||||
ssl_reused_session = false,
|
||||
})
|
||||
|
||||
assert(type(session) == "boolean", "expected session to be a boolean")
|
||||
assert(session == true, "expected session to be true")
|
||||
assert(httpc:close())
|
||||
}
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
[alert]
|
||||
|
||||
|
||||
=== TEST 3: ssl_reused_session accepts userdata
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
server_tokens off;
|
||||
resolver $TEST_NGINX_RESOLVER ipv6=off;
|
||||
lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
|
||||
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local httpc = assert(require("resty.http").new())
|
||||
local ok, err, session = assert(httpc:connect {
|
||||
scheme = "https",
|
||||
host = TEST_SERVER_SOCK,
|
||||
})
|
||||
|
||||
assert(type(session) == "userdata" or type(session) == "cdata", "expected session to be userdata or cdata")
|
||||
|
||||
local httpc2 = assert(require("resty.http").new())
|
||||
local ok, err, session2 = assert(httpc2:connect {
|
||||
scheme = "https",
|
||||
host = TEST_SERVER_SOCK,
|
||||
ssl_reused_session = session,
|
||||
})
|
||||
|
||||
assert(type(session2) == "userdata" or type(session2) == "cdata", "expected session2 to be userdata or cdata")
|
||||
|
||||
assert(httpc:close())
|
||||
assert(httpc2:close())
|
||||
}
|
||||
}
|
||||
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
[alert]
|
211
src/deps/src/lua-resty-http/t/20-mtls.t
Normal file
211
src/deps/src/lua-resty-http/t/20-mtls.t
Normal file
|
@ -0,0 +1,211 @@
|
|||
use Test::Nginx::Socket::Lua 'no_plan';
|
||||
|
||||
|
||||
#$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';
|
||||
#$ENV{TEST_COVERAGE} ||= 0;
|
||||
|
||||
log_level 'debug';
|
||||
|
||||
no_long_string();
|
||||
#no_diff();
|
||||
|
||||
sub read_file {
|
||||
my $infile = shift;
|
||||
open my $in, $infile
|
||||
or die "cannot open $infile for reading: $!";
|
||||
my $cert = do { local $/; <$in> };
|
||||
close $in;
|
||||
$cert;
|
||||
}
|
||||
|
||||
our $MTLSCA = read_file("t/cert/mtls_ca.crt");
|
||||
our $MTLSClient = read_file("t/cert/mtls_client.crt");
|
||||
our $MTLSClientKey = read_file("t/cert/mtls_client.key");
|
||||
our $TestCert = read_file("t/cert/test.crt");
|
||||
our $TestKey = read_file("t/cert/test.key");
|
||||
|
||||
our $HtmlDir = html_dir;
|
||||
|
||||
use Cwd qw(cwd);
|
||||
my $pwd = cwd();
|
||||
|
||||
our $mtls_http_config = <<"_EOC_";
|
||||
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
|
||||
server {
|
||||
listen unix:$::HtmlDir/mtls.sock ssl;
|
||||
|
||||
ssl_certificate $::HtmlDir/test.crt;
|
||||
ssl_certificate_key $::HtmlDir/test.key;
|
||||
ssl_client_certificate $::HtmlDir/mtls_ca.crt;
|
||||
ssl_verify_client on;
|
||||
server_tokens off;
|
||||
server_name example.com;
|
||||
|
||||
location / {
|
||||
echo -n "hello, \$ssl_client_s_dn";
|
||||
}
|
||||
}
|
||||
_EOC_
|
||||
|
||||
our $mtls_user_files = <<"_EOC_";
|
||||
>>> mtls_ca.crt
|
||||
$::MTLSCA
|
||||
>>> mtls_client.key
|
||||
$::MTLSClientKey
|
||||
>>> mtls_client.crt
|
||||
$::MTLSClient
|
||||
>>> test.crt
|
||||
$::TestCert
|
||||
>>> test.key
|
||||
$::TestKey
|
||||
_EOC_
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: Connection fails during handshake without client cert and key
|
||||
--- http_config eval: $::mtls_http_config
|
||||
--- config eval
|
||||
"
|
||||
lua_ssl_trusted_certificate $::HtmlDir/test.crt;
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local httpc = assert(require('resty.http').new())
|
||||
|
||||
local ok, err = httpc:connect {
|
||||
scheme = 'https',
|
||||
host = 'unix:$::HtmlDir/mtls.sock',
|
||||
}
|
||||
|
||||
if ok and not err then
|
||||
local res, err = assert(httpc:request {
|
||||
method = 'GET',
|
||||
path = '/',
|
||||
headers = {
|
||||
['Host'] = 'example.com',
|
||||
},
|
||||
})
|
||||
|
||||
ngx.status = res.status -- expect 400
|
||||
end
|
||||
|
||||
httpc:close()
|
||||
}
|
||||
}
|
||||
"
|
||||
--- user_files eval: $::mtls_user_files
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 400
|
||||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
|
||||
|
||||
=== TEST 2: Connection fails during handshake with not priv_key
|
||||
--- http_config eval: $::mtls_http_config
|
||||
--- SKIP
|
||||
--- config eval
|
||||
"
|
||||
lua_ssl_trusted_certificate $::HtmlDir/test.crt;
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local f = assert(io.open('$::HtmlDir/mtls_client.crt'))
|
||||
local cert_data = f:read('*a')
|
||||
f:close()
|
||||
|
||||
local ssl = require('ngx.ssl')
|
||||
|
||||
local cert = assert(ssl.parse_pem_cert(cert_data))
|
||||
|
||||
local httpc = assert(require('resty.http').new())
|
||||
|
||||
local ok, err = httpc:connect {
|
||||
scheme = 'https',
|
||||
host = 'unix:$::HtmlDir/mtls.sock',
|
||||
ssl_client_cert = cert,
|
||||
ssl_client_priv_key = 'foo',
|
||||
}
|
||||
|
||||
if ok and not err then
|
||||
local res, err = assert(httpc:request {
|
||||
method = 'GET',
|
||||
path = '/',
|
||||
headers = {
|
||||
['Host'] = 'example.com',
|
||||
},
|
||||
})
|
||||
|
||||
ngx.say(res:read_body())
|
||||
end
|
||||
|
||||
httpc:close()
|
||||
}
|
||||
}
|
||||
"
|
||||
--- user_files eval: $::mtls_user_files
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 200
|
||||
--- error_log
|
||||
could not set client certificate: bad client pkey type
|
||||
--- response_body_unlike: hello, CN=foo@example.com,O=OpenResty,ST=California,C=US
|
||||
|
||||
|
||||
=== TEST 3: Connection succeeds with client cert and key. SKIP'd for CI until feature is merged.
|
||||
--- SKIP
|
||||
--- http_config eval: $::mtls_http_config
|
||||
--- config eval
|
||||
"
|
||||
lua_ssl_trusted_certificate $::HtmlDir/test.crt;
|
||||
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local f = assert(io.open('$::HtmlDir/mtls_client.crt'))
|
||||
local cert_data = f:read('*a')
|
||||
f:close()
|
||||
|
||||
f = assert(io.open('$::HtmlDir/mtls_client.key'))
|
||||
local key_data = f:read('*a')
|
||||
f:close()
|
||||
|
||||
local ssl = require('ngx.ssl')
|
||||
|
||||
local cert = assert(ssl.parse_pem_cert(cert_data))
|
||||
local key = assert(ssl.parse_pem_priv_key(key_data))
|
||||
|
||||
local httpc = assert(require('resty.http').new())
|
||||
|
||||
local ok, err = httpc:connect {
|
||||
scheme = 'https',
|
||||
host = 'unix:$::HtmlDir/mtls.sock',
|
||||
ssl_client_cert = cert,
|
||||
ssl_client_priv_key = key,
|
||||
}
|
||||
|
||||
if ok and not err then
|
||||
local res, err = assert(httpc:request {
|
||||
method = 'GET',
|
||||
path = '/',
|
||||
headers = {
|
||||
['Host'] = 'example.com',
|
||||
},
|
||||
})
|
||||
|
||||
ngx.say(res:read_body())
|
||||
end
|
||||
|
||||
httpc:close()
|
||||
}
|
||||
}
|
||||
"
|
||||
--- user_files eval: $::mtls_user_files
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
[warn]
|
||||
--- response_body
|
||||
hello, CN=foo@example.com,O=OpenResty,ST=California,C=US
|
||||
|
33
src/deps/src/lua-resty-http/t/cert/mtls_ca.crt
Normal file
33
src/deps/src/lua-resty-http/t/cert/mtls_ca.crt
Normal file
|
@ -0,0 +1,33 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFpTCCA42gAwIBAgIUfTh89NyxxmbVwNZ/YFddssWc+WkwDQYJKoZIhvcNAQEL
|
||||
BQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoM
|
||||
CU9wZW5SZXN0eTEiMCAGA1UEAwwZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe
|
||||
Fw0xOTA5MTMyMjI4MTJaFw0zOTA5MDgyMjI4MTJaMFoxCzAJBgNVBAYTAlVTMRMw
|
||||
EQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQKDAlPcGVuUmVzdHkxIjAgBgNVBAMM
|
||||
GU9wZW5SZXN0eSBUZXN0aW5nIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC
|
||||
DwAwggIKAoICAQDcMg2DeV8z+0E2ZiXUax111lKzhAbMCK0RJlV9tAi+YdcsDR/t
|
||||
zvvAZNGONUoewUuz/7E88oweh+Xi1GJtvd0DjB70y7tgpf5PUXovstWVwy7s5jZo
|
||||
kgn62yi9ZOOZpjwnYTBviirtRTnZRwkzL6wF0xMyJjAbKBJuPMrMiyFdh82lt7wI
|
||||
NS4mhyEdM0UiVVxfC2uzsddTOcOJURfGbW7UZm4Xohzq4QZ8geQj2OT5YTqw7dZ7
|
||||
Xxre5H7IcNcAh+vIk5SEBV1WE+S5MnFly7gaLYNc49OSfz5Hcpv59Vr+4bZ+olbW
|
||||
nQ/uU8BQovtkW6pjuT8nC4OKs2e8osoAZuk0rFS1uC501C+yES48mzaU8ttAidu6
|
||||
nb/JgsdkrnJQeTc5rAoER4M2ne5kqtEXN8wzf3/sazo2PLywbfrUXUTV6kJilrGr
|
||||
RkBN+fr6HTBkf+ooQMBOQPTojUdwbR86CLCyiJov2bzmBfGcOgSakv59S+uvUZFp
|
||||
FLTiahuzLfcgYsG3UKQA47pYlNdUqP8vCCaf1nwmqjx2KS3Z/YFnO/gQgtY+f0Bh
|
||||
UpnUDv+zBxpVFfVCyxByEsDPdwDkqLSwB6+YZINl36S48iXpoPhNXIYmO6GnhNWV
|
||||
k2/RyCDTxEO+MbXHVg6iyIVHJWth7m18vl4uuSK/LbJHV9Q9Z7G99DQ0NwIDAQAB
|
||||
o2MwYTAdBgNVHQ4EFgQUuoo+ehdlDFcQU+j5qONMKh0NtFQwHwYDVR0jBBgwFoAU
|
||||
uoo+ehdlDFcQU+j5qONMKh0NtFQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
|
||||
BAMCAYYwDQYJKoZIhvcNAQELBQADggIBAM7c9q41K8306lfAVLqtbtaeETy+xxYG
|
||||
XE2HfFW1IuukrXQ8d/JH4stL/HcHJzzhHPf5p3ja3Snu9zPmTk3pgUPDYPZf57tR
|
||||
NCqwxjn6blwXWlzQqSavto9KAx3IWHuj0OTrZz/a1KPb9NGvatBhgthyRCRTbvhL
|
||||
OA5tveuYSHb724cp3NZ1xaTQmDZsSgHCoCJ/7RnlbcJ7RsKCOzCWNFRomH410vdv
|
||||
TajkUBlEC4OC1RIvxuVePHHb1ogbbe93SA/9mzw/E5SfoeF3mvByN4Ay8awXbNlH
|
||||
26RfuIdGc4fZRc/87s4yPwhYScZBG+pHO0gn42E0FyiG6Jp3rhHMH5Sa2hNlPMpn
|
||||
hYMaA6zQI4n/3AeFNM0VGxA+Yg/Al2WpXEJARrZqMW/qcrdMcPj5WeY6Tb6er04S
|
||||
kfImwhMIajl3nNc9tHoad8r2VuMWMltH/dnWuEdo+pPdIY3fdJdyQeoLQDDLEQwL
|
||||
AYrFy4uzKfQogfQBIHRdIMZTJh5v3mAFDpK59I5yzSt1GtUnFMC5MVOg+LbOo5UW
|
||||
FCtwaW5EZiTszmakvvWMMZe9HwZMYNCeSGGtiPA/GA2zNci/n2TEcB11HgiY52y2
|
||||
E/40nS61oL81zMwhV7l5psgJxQ2ORsKRJPHjADwvwh3xyCEJgVyBRCDX7J3PpAUO
|
||||
79DprjVU8t7p
|
||||
-----END CERTIFICATE-----
|
111
src/deps/src/lua-resty-http/t/cert/mtls_client.crt
Normal file
111
src/deps/src/lua-resty-http/t/cert/mtls_client.crt
Normal file
|
@ -0,0 +1,111 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 4096 (0x1000)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=California, O=OpenResty, CN=OpenResty Testing Root CA
|
||||
Validity
|
||||
Not Before: Sep 13 22:30:49 2019 GMT
|
||||
Not After : Sep 10 22:30:49 2029 GMT
|
||||
Subject: C=US, ST=California, O=OpenResty, CN=foo@example.com
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
RSA Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:d0:8c:1e:2c:25:7f:00:9d:8a:3d:8a:f3:b5:1d:
|
||||
6b:24:f5:ac:35:7d:cd:b7:d0:af:db:88:7c:ee:82:
|
||||
46:16:47:f3:43:08:a1:04:6e:0d:3e:ce:69:fa:d5:
|
||||
89:14:82:20:f1:47:f2:38:c8:ab:ea:2f:1e:f0:15:
|
||||
04:c0:f4:8b:3c:c3:d4:78:56:87:4c:f1:70:ac:11:
|
||||
86:2e:c4:6a:6d:10:84:27:81:ca:2a:8b:85:3e:62:
|
||||
13:5e:40:6c:19:e4:49:3d:f3:de:aa:e8:5e:11:a1:
|
||||
f2:66:83:6a:40:d1:34:c5:bf:b8:cb:97:7c:6a:ea:
|
||||
46:bf:17:be:32:8d:a8:31:56:e5:8b:6d:08:03:d0:
|
||||
44:69:b9:af:1e:15:1d:a5:64:9e:12:84:83:db:d9:
|
||||
c6:71:90:3b:c2:7b:41:21:57:af:70:15:0b:56:59:
|
||||
21:a6:4e:46:71:66:90:f1:ef:bc:b2:48:f9:8b:ea:
|
||||
e5:72:4a:ba:4a:ae:2d:74:0b:33:03:f6:2e:47:0f:
|
||||
56:a4:00:e8:1e:62:cb:b8:af:9c:98:1a:89:7c:d0:
|
||||
a3:7a:5a:e1:84:50:64:e4:5d:a5:70:a4:69:54:c4:
|
||||
f4:76:44:a2:be:1b:ef:dc:a3:d8:1d:0d:30:a2:d4:
|
||||
79:fb:39:76:ab:b7:18:f2:f7:92:f8:81:83:94:b8:
|
||||
11:b1
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Cert Type:
|
||||
SSL Client, S/MIME
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Client Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
D2:E4:F5:21:1C:17:A4:FF:13:F4:1A:28:A8:A7:DC:C6:DE:89:A0:31
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:BA:8A:3E:7A:17:65:0C:57:10:53:E8:F9:A8:E3:4C:2A:1D:0D:B4:54
|
||||
|
||||
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: sha256WithRSAEncryption
|
||||
47:77:51:37:00:f7:28:da:c4:d9:4e:38:c4:ec:ea:24:c9:83:
|
||||
36:4c:90:93:a7:b2:2b:10:bf:75:df:0b:72:d8:e7:4f:4f:68:
|
||||
e5:32:2f:35:89:17:95:5c:bb:43:fc:70:89:46:08:43:61:ac:
|
||||
41:62:84:01:94:88:d1:dc:8a:bd:30:2c:18:eb:51:79:0b:b7:
|
||||
1b:b6:49:df:c9:85:55:f6:73:9f:b7:83:99:52:23:fe:e6:bd:
|
||||
09:da:90:b9:e2:9b:68:c4:df:bd:fe:23:94:55:34:be:0d:7d:
|
||||
84:0c:53:69:2a:0f:3c:47:68:34:3f:2a:3f:89:3f:3e:d3:26:
|
||||
ce:b7:58:bc:d0:6f:ee:f8:bd:5d:c6:48:ae:a0:6c:1f:6d:e0:
|
||||
66:93:7d:db:3c:07:e6:15:ae:aa:e3:d0:3d:ef:04:b6:dd:53:
|
||||
16:93:61:70:e9:af:c0:e9:1d:ff:2b:e5:0a:03:56:48:3f:1c:
|
||||
dc:fe:1b:a6:6d:f6:54:ab:41:e5:3b:5b:ab:f5:81:10:46:26:
|
||||
bb:ea:d7:0e:33:b1:5e:30:4d:81:86:63:9a:4a:4f:1e:44:b9:
|
||||
c2:c6:08:4e:da:fa:3a:55:da:96:7c:01:f6:d5:e8:3b:ba:e9:
|
||||
31:3b:1c:51:39:1a:59:f0:e0:c7:17:2e:f6:18:9d:ec:a7:48:
|
||||
30:b8:4c:6d:e5:4a:4f:43:41:cb:0e:6b:ac:ad:87:44:90:76:
|
||||
85:23:2b:eb:8f:97:4b:22:13:60:20:3a:37:a4:dc:74:7d:85:
|
||||
3d:a1:f5:1a:03:f6:d5:78:c7:bc:9b:09:f2:c8:05:27:43:2a:
|
||||
ac:50:21:3a:ee:83:2d:db:02:6f:c7:91:de:63:d6:36:7d:7a:
|
||||
9f:1f:fb:48:62:f4:fb:8e:3a:ea:61:9b:3c:03:f9:f8:a5:df:
|
||||
1b:02:14:2c:de:e6:e3:47:d2:44:65:94:1a:c6:e1:fd:ba:8d:
|
||||
b6:f8:93:a9:46:46:26:79:b0:bf:57:a8:a2:20:66:56:7e:c9:
|
||||
f5:a4:0b:5e:76:70:0a:47:a4:db:45:2e:15:99:69:f9:6b:14:
|
||||
93:2a:0a:b6:ee:53:a6:b9:02:9b:a2:25:37:1e:37:70:a2:7c:
|
||||
7f:c3:ce:98:17:2f:9b:5b:fa:6f:ae:d8:0e:d4:6a:b2:03:5a:
|
||||
fe:ba:4b:7f:f6:98:20:ea:cb:be:17:34:e0:43:74:d1:0c:e5:
|
||||
d4:cc:5d:13:41:d3:5e:a4:f6:94:f7:15:b8:15:a9:65:f8:28:
|
||||
3f:da:ef:b2:30:34:6d:96:3a:7a:f4:20:ec:9e:62:13:36:f1:
|
||||
a7:04:e1:7a:d2:33:20:f6:61:4a:68:44:cb:92:d7:62:f0:e4:
|
||||
70:f0:a5:e3:dd:2f:e2:a3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFGTCCAwGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCVVMx
|
||||
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEiMCAGA1UE
|
||||
AwwZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAeFw0xOTA5MTMyMjMwNDlaFw0y
|
||||
OTA5MTAyMjMwNDlaMFAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
|
||||
MRIwEAYDVQQKDAlPcGVuUmVzdHkxGDAWBgNVBAMMD2Zvb0BleGFtcGxlLmNvbTCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCMHiwlfwCdij2K87UdayT1
|
||||
rDV9zbfQr9uIfO6CRhZH80MIoQRuDT7OafrViRSCIPFH8jjIq+ovHvAVBMD0izzD
|
||||
1HhWh0zxcKwRhi7Eam0QhCeByiqLhT5iE15AbBnkST3z3qroXhGh8maDakDRNMW/
|
||||
uMuXfGrqRr8XvjKNqDFW5YttCAPQRGm5rx4VHaVknhKEg9vZxnGQO8J7QSFXr3AV
|
||||
C1ZZIaZORnFmkPHvvLJI+Yvq5XJKukquLXQLMwP2LkcPVqQA6B5iy7ivnJgaiXzQ
|
||||
o3pa4YRQZORdpXCkaVTE9HZEor4b79yj2B0NMKLUefs5dqu3GPL3kviBg5S4EbEC
|
||||
AwEAAaOB8jCB7zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAzBglghkgB
|
||||
hvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRpZmljYXRlMB0G
|
||||
A1UdDgQWBBTS5PUhHBek/xP0Giiop9zG3omgMTAfBgNVHSMEGDAWgBS6ij56F2UM
|
||||
VxBT6Pmo40wqHQ20VDAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUH
|
||||
AwIGCCsGAQUFBwMEMCsGA1UdEQQkMCKBD2Zvb0BleGFtcGxlLmNvbYEPYmFyQGV4
|
||||
YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBHd1E3APco2sTZTjjE7OokyYM2
|
||||
TJCTp7IrEL913wty2OdPT2jlMi81iReVXLtD/HCJRghDYaxBYoQBlIjR3Iq9MCwY
|
||||
61F5C7cbtknfyYVV9nOft4OZUiP+5r0J2pC54ptoxN+9/iOUVTS+DX2EDFNpKg88
|
||||
R2g0Pyo/iT8+0ybOt1i80G/u+L1dxkiuoGwfbeBmk33bPAfmFa6q49A97wS23VMW
|
||||
k2Fw6a/A6R3/K+UKA1ZIPxzc/humbfZUq0HlO1ur9YEQRia76tcOM7FeME2BhmOa
|
||||
Sk8eRLnCxghO2vo6VdqWfAH21eg7uukxOxxRORpZ8ODHFy72GJ3sp0gwuExt5UpP
|
||||
Q0HLDmusrYdEkHaFIyvrj5dLIhNgIDo3pNx0fYU9ofUaA/bVeMe8mwnyyAUnQyqs
|
||||
UCE67oMt2wJvx5HeY9Y2fXqfH/tIYvT7jjrqYZs8A/n4pd8bAhQs3ubjR9JEZZQa
|
||||
xuH9uo22+JOpRkYmebC/V6iiIGZWfsn1pAtednAKR6TbRS4VmWn5axSTKgq27lOm
|
||||
uQKboiU3Hjdwonx/w86YFy+bW/pvrtgO1GqyA1r+ukt/9pgg6su+FzTgQ3TRDOXU
|
||||
zF0TQdNepPaU9xW4Fall+Cg/2u+yMDRtljp69CDsnmITNvGnBOF60jMg9mFKaETL
|
||||
ktdi8ORw8KXj3S/iow==
|
||||
-----END CERTIFICATE-----
|
27
src/deps/src/lua-resty-http/t/cert/mtls_client.key
Normal file
27
src/deps/src/lua-resty-http/t/cert/mtls_client.key
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA0IweLCV/AJ2KPYrztR1rJPWsNX3Nt9Cv24h87oJGFkfzQwih
|
||||
BG4NPs5p+tWJFIIg8UfyOMir6i8e8BUEwPSLPMPUeFaHTPFwrBGGLsRqbRCEJ4HK
|
||||
KouFPmITXkBsGeRJPfPequheEaHyZoNqQNE0xb+4y5d8aupGvxe+Mo2oMVbli20I
|
||||
A9BEabmvHhUdpWSeEoSD29nGcZA7wntBIVevcBULVlkhpk5GcWaQ8e+8skj5i+rl
|
||||
ckq6Sq4tdAszA/YuRw9WpADoHmLLuK+cmBqJfNCjelrhhFBk5F2lcKRpVMT0dkSi
|
||||
vhvv3KPYHQ0wotR5+zl2q7cY8veS+IGDlLgRsQIDAQABAoIBAEpoY++OZVT74LH6
|
||||
nN+XIn5qZUokm7yk6cnjVefndUhH3aSiNIkXFwS8sxV7ENDPaR+NcwANoUEKFPjG
|
||||
Fw8dcXx5xpo1DUtHrdLG4eBX1j0Zsn1CErbBVwYeChkL1UYbrII9O8ow5DdYV9t5
|
||||
sfR0cGbJ9A43+31OH3XY69SvtD39xItgxK0Wg4Ciz475kCvG51Q1iBWAkm4koXhz
|
||||
VCha4wghs81wJ28HRMFZAFf2C+72rk6EypMUX2dYirvW/+7zONirk298NDMAOSBh
|
||||
mRWyPV8qipYx42hBQ9vSVm0UVb0ZbqVomKKUZfj11LL6Ad/OzCyVAiNLXyZREV6r
|
||||
d324Bu0CgYEA/YPsE6p6H3MTPIoVTnsyyQw0pNmXtIAuYTgSveeuPoTYn3ZBWoGN
|
||||
iLpbnW4EH3xNKfrdMjbqqLls1iwm7/ZAP5klAuL4s10onrcjMt65fyfa3Lw1gavG
|
||||
SUFFdsueH2k3FohqNsbQUSXZILVQnXsRoldi38b7NKrAqABcEMAIqXMCgYEA0pde
|
||||
nt4aMmrGBRPLnjCs1UlC5PbXzCE8XxxQ7HZKx4Sy5ErQ0EW1wzF6c0fEYI7i+j1/
|
||||
ESKqekzc5ue0T8acoioB+VUybO1oxQZsZUPY7roqXOYwZH9LQOdPYUOh9k33CZHw
|
||||
6KFfx8bKCpdXn7FkwR2UUtCSp/6CZcyYr89Qn0sCgYAQ0L5I86bUDTL6cgJFyWAt
|
||||
+7RGNvScEWCCLFD57bMeDHu93/8nvK4hopLPF2wIlpsbrLsdSI06EcqJTjZq9j9+
|
||||
uG6/CUULyKMYG/emuSU+rOsUdxtpdXZah4zO+2SKmtT/lp7M8VUB/OuxArXNLEuY
|
||||
JAm35B/nd2f9/MAekE5CxwKBgQCV660w7G0590mB09XhiEWCkeVNm22FpSOVklMK
|
||||
BCy4XX/9hkWh//6mN1M1PqJPG2n7PEx5pnQ3HQEmYU28fWiFCeLd3glIArvTh/8j
|
||||
GGoXifEescFByl2IlyOr2roy3s4/weX/tuK5Fow/ff6jcWaJFMXDLzk437d1QXJx
|
||||
tuVugQKBgByfr2eakXFQvAVGJUfVXA3M2BoBODZEPYTgryVMoEEduFy0HZiw4xKi
|
||||
Dngwewy6/UJMAGA+8ak9Ca367FxnegZU9knm6ujYVyhU5WzbKpR8v7OaUP8d5icq
|
||||
rCZZtglG0c8XfVpJjR4FsKA/qrFvKZpu5NdEw3o5/LSrV4HjqZQ6
|
||||
-----END RSA PRIVATE KEY-----
|
112
src/deps/src/lua-resty-http/t/cert/mtls_server.crt
Normal file
112
src/deps/src/lua-resty-http/t/cert/mtls_server.crt
Normal file
|
@ -0,0 +1,112 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 4097 (0x1001)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=California, O=OpenResty, CN=OpenResty Testing Root CA
|
||||
Validity
|
||||
Not Before: Sep 13 22:32:03 2019 GMT
|
||||
Not After : Sep 10 22:32:03 2029 GMT
|
||||
Subject: C=US, ST=California, O=OpenResty, CN=example.com
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
RSA Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:bb:69:2b:30:43:b0:b4:cc:84:3f:22:39:65:65:
|
||||
6a:bd:75:a4:2b:7d:f7:ec:e4:12:e8:d1:c4:ce:7e:
|
||||
4e:54:5c:22:cc:d2:18:7f:3b:9e:cb:70:d9:7d:79:
|
||||
8f:05:93:b6:9f:2f:d5:33:d7:98:a2:ed:c5:00:93:
|
||||
e4:ca:bc:cb:f0:e1:63:3e:07:6b:38:6f:4d:09:45:
|
||||
f1:a1:3b:a3:ca:c0:47:c1:a1:0a:f8:c9:bb:c7:da:
|
||||
26:9d:d3:0b:35:24:01:3e:16:14:2e:44:38:8c:c9:
|
||||
09:02:41:9e:b6:fb:0c:aa:fc:d6:44:5e:27:ab:aa:
|
||||
d5:c3:68:e1:dd:57:06:6c:4f:f6:24:33:a8:2b:49:
|
||||
60:82:0e:15:aa:55:9f:61:cc:74:39:7e:9f:a6:4f:
|
||||
71:4a:8b:eb:43:dd:c2:f7:90:38:df:a6:a6:a8:f6:
|
||||
77:bc:9e:54:69:30:83:4c:2a:eb:b8:62:7c:c7:14:
|
||||
84:9e:f3:e1:4a:15:33:51:65:a3:af:9d:09:c6:b8:
|
||||
89:30:a3:d2:18:e9:dc:5d:6b:ea:68:ca:8b:5c:e4:
|
||||
3b:fe:32:7f:48:c3:4c:f0:b5:06:f6:23:97:3e:f2:
|
||||
50:90:68:26:39:6d:b2:e2:53:89:71:6a:48:f0:f1:
|
||||
fc:89:3c:6d:db:87:6c:79:23:ed:87:5d:c5:fa:8a:
|
||||
0d:b9
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Cert Type:
|
||||
SSL Server
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Server Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
8C:8E:18:2C:13:84:C9:2A:61:6B:73:3F:18:76:A4:85:55:5F:5C:5F
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:BA:8A:3E:7A:17:65:0C:57:10:53:E8:F9:A8:E3:4C:2A:1D:0D:B4:54
|
||||
DirName:/C=US/ST=California/O=OpenResty/CN=OpenResty Testing Root CA
|
||||
serial:7D:38:7C:F4:DC:B1:C6:66:D5:C0:D6:7F:60:57:5D:B2:C5:9C:F9:69
|
||||
|
||||
X509v3 Key Usage: critical
|
||||
Digital Signature, Key Encipherment
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
80:9f:3e:f5:8b:50:ee:cb:e4:c2:f0:16:01:07:a1:af:76:bc:
|
||||
da:c8:cd:24:e9:63:df:d3:47:28:8c:7f:58:14:5d:d4:fd:44:
|
||||
16:c3:06:15:be:90:ec:1c:8f:78:34:11:e7:cc:86:d8:2a:a2:
|
||||
e5:99:70:83:76:4a:65:a4:e1:9a:68:20:29:c0:7a:c6:4a:08:
|
||||
b3:74:c3:53:b5:7b:79:92:f1:99:b5:a1:a3:90:ce:9a:cb:26:
|
||||
a5:a6:33:de:74:98:99:ec:18:d1:1e:41:be:f8:c3:d2:8d:aa:
|
||||
07:de:9a:97:28:0d:bf:70:ac:2b:cf:b7:ff:bc:ac:e4:16:0c:
|
||||
1c:03:a7:5a:2d:64:0d:90:16:bd:97:c3:1f:f5:bf:a9:fa:15:
|
||||
d1:e0:d4:0d:f7:b3:51:23:ce:ad:16:4f:41:72:17:aa:01:d5:
|
||||
44:e2:9e:d5:ce:ea:54:98:04:43:14:2e:51:4b:c7:d9:21:4f:
|
||||
e1:a4:fa:dd:e0:f0:82:ec:6f:9f:be:a2:3c:3b:85:f7:6d:96:
|
||||
ee:0d:e6:08:2b:1b:be:06:a4:b7:5f:a3:f2:f2:b9:d0:5a:8f:
|
||||
90:86:1a:f4:7a:9f:c8:ae:09:1d:60:a2:8b:e0:0b:f6:00:21:
|
||||
d9:df:33:4b:39:75:b6:64:9b:c7:df:e4:85:7a:ae:df:72:8c:
|
||||
8b:7e:98:8e:47:0a:27:1f:8e:2c:11:7f:7b:fc:a0:db:1b:6e:
|
||||
f6:de:4e:85:ac:30:e6:e8:6a:7a:e6:f9:f4:18:0a:c6:ad:1c:
|
||||
e1:0c:dd:e0:e0:8d:5a:d7:08:34:e7:22:b4:44:bd:99:39:b1:
|
||||
71:74:3f:7c:aa:65:f5:37:46:85:d3:79:f7:a8:35:8d:2b:30:
|
||||
99:d2:47:ce:a6:74:eb:f3:9f:d3:9a:4e:99:96:50:7b:ba:22:
|
||||
c8:72:47:d4:da:6e:9a:73:01:3c:89:e9:3f:56:17:b7:ba:22:
|
||||
71:db:66:a2:d2:fb:33:51:36:f6:b6:f2:5b:32:70:9d:e7:e3:
|
||||
36:d6:ae:cb:9b:62:ef:69:c7:f7:ba:95:49:16:f5:7c:d9:29:
|
||||
bb:0a:02:b1:6b:72:15:ab:2c:27:7b:c8:bc:f6:15:1f:fa:ae:
|
||||
08:fd:e0:11:36:b1:ab:9c:c8:11:d1:d3:0d:7d:49:4e:ca:e6:
|
||||
73:ee:0d:c3:8d:6f:f5:a4:fe:a1:af:6b:91:f7:53:fd:10:df:
|
||||
77:dd:ef:ec:7b:cf:32:75:df:04:8a:d1:a1:f7:36:68:ee:65:
|
||||
e3:43:90:37:43:e8:d1:a8:e2:90:5c:1c:75:0a:29:94:4a:6a:
|
||||
9b:89:28:43:bd:85:56:0d:f1:2b:44:bd:e6:7a:4c:b7:85:10:
|
||||
77:b7:a8:0f:33:29:a7:26
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFWTCCA0GgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCVVMx
|
||||
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEiMCAGA1UE
|
||||
AwwZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAeFw0xOTA5MTMyMjMyMDNaFw0y
|
||||
OTA5MTAyMjMyMDNaMEwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
|
||||
MRIwEAYDVQQKDAlPcGVuUmVzdHkxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu2krMEOwtMyEPyI5ZWVqvXWkK333
|
||||
7OQS6NHEzn5OVFwizNIYfzuey3DZfXmPBZO2ny/VM9eYou3FAJPkyrzL8OFjPgdr
|
||||
OG9NCUXxoTujysBHwaEK+Mm7x9omndMLNSQBPhYULkQ4jMkJAkGetvsMqvzWRF4n
|
||||
q6rVw2jh3VcGbE/2JDOoK0lggg4VqlWfYcx0OX6fpk9xSovrQ93C95A436amqPZ3
|
||||
vJ5UaTCDTCrruGJ8xxSEnvPhShUzUWWjr50JxriJMKPSGOncXWvqaMqLXOQ7/jJ/
|
||||
SMNM8LUG9iOXPvJQkGgmOW2y4lOJcWpI8PH8iTxt24dseSPth13F+ooNuQIDAQAB
|
||||
o4IBNTCCATEwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4
|
||||
QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNV
|
||||
HQ4EFgQUjI4YLBOEySpha3M/GHakhVVfXF8wgZcGA1UdIwSBjzCBjIAUuoo+ehdl
|
||||
DFcQU+j5qONMKh0NtFShXqRcMFoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
|
||||
Zm9ybmlhMRIwEAYDVQQKDAlPcGVuUmVzdHkxIjAgBgNVBAMMGU9wZW5SZXN0eSBU
|
||||
ZXN0aW5nIFJvb3QgQ0GCFH04fPTcscZm1cDWf2BXXbLFnPlpMA4GA1UdDwEB/wQE
|
||||
AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAgJ8+
|
||||
9YtQ7svkwvAWAQehr3a82sjNJOlj39NHKIx/WBRd1P1EFsMGFb6Q7ByPeDQR58yG
|
||||
2Cqi5Zlwg3ZKZaThmmggKcB6xkoIs3TDU7V7eZLxmbWho5DOmssmpaYz3nSYmewY
|
||||
0R5BvvjD0o2qB96alygNv3CsK8+3/7ys5BYMHAOnWi1kDZAWvZfDH/W/qfoV0eDU
|
||||
DfezUSPOrRZPQXIXqgHVROKe1c7qVJgEQxQuUUvH2SFP4aT63eDwguxvn76iPDuF
|
||||
922W7g3mCCsbvgakt1+j8vK50FqPkIYa9HqfyK4JHWCii+AL9gAh2d8zSzl1tmSb
|
||||
x9/khXqu33KMi36YjkcKJx+OLBF/e/yg2xtu9t5Ohaww5uhqeub59BgKxq0c4Qzd
|
||||
4OCNWtcINOcitES9mTmxcXQ/fKpl9TdGhdN596g1jSswmdJHzqZ06/Of05pOmZZQ
|
||||
e7oiyHJH1NpumnMBPInpP1YXt7oicdtmotL7M1E29rbyWzJwnefjNtauy5ti72nH
|
||||
97qVSRb1fNkpuwoCsWtyFassJ3vIvPYVH/quCP3gETaxq5zIEdHTDX1JTsrmc+4N
|
||||
w41v9aT+oa9rkfdT/RDfd93v7HvPMnXfBIrRofc2aO5l40OQN0Po0ajikFwcdQop
|
||||
lEpqm4koQ72FVg3xK0S95npMt4UQd7eoDzMppyY=
|
||||
-----END CERTIFICATE-----
|
27
src/deps/src/lua-resty-http/t/cert/mtls_server.key
Normal file
27
src/deps/src/lua-resty-http/t/cert/mtls_server.key
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAu2krMEOwtMyEPyI5ZWVqvXWkK3337OQS6NHEzn5OVFwizNIY
|
||||
fzuey3DZfXmPBZO2ny/VM9eYou3FAJPkyrzL8OFjPgdrOG9NCUXxoTujysBHwaEK
|
||||
+Mm7x9omndMLNSQBPhYULkQ4jMkJAkGetvsMqvzWRF4nq6rVw2jh3VcGbE/2JDOo
|
||||
K0lggg4VqlWfYcx0OX6fpk9xSovrQ93C95A436amqPZ3vJ5UaTCDTCrruGJ8xxSE
|
||||
nvPhShUzUWWjr50JxriJMKPSGOncXWvqaMqLXOQ7/jJ/SMNM8LUG9iOXPvJQkGgm
|
||||
OW2y4lOJcWpI8PH8iTxt24dseSPth13F+ooNuQIDAQABAoIBABnT/KfCLHA+X1t0
|
||||
FATtXTCPLfjwe2KibBi6EC2FKrZlnEYuDkI6rT/MZaztO9DA8sItjWx/ogGSUzwp
|
||||
JbbrHhAsf8jkrNoyPKOyiAJ4fbJLnZgJ4cE3zDFW10uY8kp4k9NCp7VYoZKFgkBV
|
||||
WtJM9wn5nm39q+n0uVEc+0PN4oy6m54Aqb1HqVCyXFp+/pVhL6PtgaClbqJd3oKV
|
||||
0/HLWfWaI3nvV6ltAphUfPoCmYIUtSl90sRgSeEaJ61UZXh0OkhhtD7Iw/JUlHDk
|
||||
a0J7owrh0Wf1kDsaSn+j1ba8MELsspFYYVm0gAMKAvRXbVrYgUMdb+HVdZ0odULl
|
||||
ezFWeAECgYEA9nnoZs+PzKWNxTzYPtgvvrSLmpXLzNrs/p41JUhy6GjQIixTSBFy
|
||||
WHkjwu0k2fvRgODfcaetAyK6sV4uTpRgqUhtFSeyNMelZ2yiIEqvhUrtHoVov1C+
|
||||
BqwwlUnmkQZNQODXOpKCvnqnOaPwMILKLtxDGmPtW0tCTR2dVVaht6ECgYEAwqb/
|
||||
h0Fh3YtykOnhV8wOZRrpVr8jS1RIgg/hklt2xh6+OYtL16sKFaLBF/BhzZRBapqd
|
||||
fB2Cx3B6rxZ5PLTse8yjEvjt6Ly7TusYWpaKbYKFnnEbmdsm5sBepuLUv4AoMYbk
|
||||
99ZejFcQI2gNbzX7eIrFitCQGxT+Wu7Gncv+vxkCgYBvAYCVrS2KcZVkG38Y7qyy
|
||||
KwYk3QoofQD3u7Eb1YFLAsmaWnQ3pQPmrMhaZguO0UcN0DlSKr5VBzMl5tDcOx89
|
||||
noziVjqAYtovtlFeUcSzN4eLk3IVl/u9bZeD5QCemEP60Eie7JVNzFe8MgVfE8iT
|
||||
Skg+fnrL/x0hNhFB+f5jgQKBgDgOEX4o5P8A3nA++gbnm6mgE1xI1OgnkG3sFuCn
|
||||
+E9boRo/NAsalV/fq82yCuhB7oi9l+abNQMsMBhl12oVDBkmuDuJdjHUz/gNGclU
|
||||
mu6obMRQ/ErVYqGG+nsCzZOMW4bPuvZoRHgTxnD70QqauB1hkTvFjgpOhGU5Z/cf
|
||||
PPBZAoGBAJQK7NF6VoF9nm5CT8vufrQ2vvp2aiLdOLLx5JXt/6seEnPZWtEvjp8/
|
||||
+ExIsfOIaU5elhv8ze8iKmRP9f04XdWpbRm6k6AR5cOkkQQ1oO7N9abU7KbD/gqX
|
||||
pJIWOlaUrbKO4Dprx7HyMYYPs9mu/UoF0Dvd/+bYXM5ZKiFrQ3Ly
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -1,24 +1,21 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID8DCCAtigAwIBAgIJALL9eJPZ6neGMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
|
||||
BAYTAkdCMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYDVQQKEwRU
|
||||
ZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwR0ZXN0MB4XDTE1MTAyMTE2MjQ1
|
||||
NloXDTE1MTEyMDE2MjQ1NlowWDELMAkGA1UEBhMCR0IxDTALBgNVBAgTBFRlc3Qx
|
||||
DTALBgNVBAcTBFRlc3QxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRlc3QxDTAL
|
||||
BgNVBAMTBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDz/AoE
|
||||
c+TPdm+Aqcchq8fLNWksFQZqbsCBGnq8rUG1b6MsVlAOkDUQGRlNPs9v0/+pzgX7
|
||||
IYXPCFcV7YONNsTUfvBYTq43mfOycmAdb3SX6kBygxdhYsDRZR+vCAIkjoRmRB20
|
||||
meh1motqM58spq3IcT8VADTRJl1OI48VTnxmXdCtmkOymU948DcauMoxm03eL/hU
|
||||
6eniNEujbnbB305noNG0W5c3h6iz9CvqUAD1kwyjick+f1atB2YYn1bymA+db6YN
|
||||
3iTo0v2raWmIc7D+qqpkNaCRxgMb2HN6X3/SfkijtNJidjqHMbs2ftlKJ5/lODPZ
|
||||
rCPQOcYK6TT8MIZ1AgMBAAGjgbwwgbkwHQYDVR0OBBYEFFUC1GrAhUp7IvJH5iyf
|
||||
+fJQliEIMIGJBgNVHSMEgYEwf4AUVQLUasCFSnsi8kfmLJ/58lCWIQihXKRaMFgx
|
||||
CzAJBgNVBAYTAkdCMQ0wCwYDVQQIEwRUZXN0MQ0wCwYDVQQHEwRUZXN0MQ0wCwYD
|
||||
VQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MQ0wCwYDVQQDEwR0ZXN0ggkAsv14k9nq
|
||||
d4YwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAtaUQOr3Qn87KXmmP
|
||||
GbSvCLSl+bScE09VYZsYaB6iq0pGN9y+Vh4/HjBUUsFexopw1dY25MEEJXEVi1xV
|
||||
2krLYAsfKCM6c1QBVmdqfVuxUvxpXwr+CNRNAlzz6PhjkeY/Ds/j4sg7EqN8hMmT
|
||||
gu8GuogX7+ZCgrzRSMMclWej+W8D1xSIuCC+rqv4w9SZdtVb3XGpCyizpTNsQAuV
|
||||
ACXvq9KXkEEj+XNvKrNdWd4zG715RdMnVm+WM53d9PLp63P+4/kwhwHULYhXygQ3
|
||||
DzzVPaojBBdw3VaHbbPHnv73FtAzOb7ky6zJ01DlmEPxEahCFpklMkY9T2uCdpj9
|
||||
oOzaNA==
|
||||
MIIDYzCCAkugAwIBAgIUXCmnoPKJ60jFkycVZ04mVj3B8aswDQYJKoZIhvcNAQEL
|
||||
BQAwQTELMAkGA1UEBhMCUFQxDjAMBgNVBAgMBVBvcnRvMQ4wDAYDVQQHDAVQb3J0
|
||||
bzESMBAGA1UECgwJbGVkZ2V0ZWNoMB4XDTIyMTIxNTEyMDQzMVoXDTMyMDkxMzEy
|
||||
MDQzMVowQTELMAkGA1UEBhMCUFQxDjAMBgNVBAgMBVBvcnRvMQ4wDAYDVQQHDAVQ
|
||||
b3J0bzESMBAGA1UECgwJbGVkZ2V0ZWNoMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAqzJINgQtchR799ahPpu2gweHCmx7cytQ59ZFW4J3QBRIB+DFnLSM
|
||||
fJa797s0Sl6f+t9pT3QLYjbdNK+R60HGd5tM31cEIa518AcDokfkzGo/clY+dbs4
|
||||
OcFe10HxAlXpu6S5/yiQ0u4CIf1uQSsS53kNpaRWcEz6Z/sw80ksCEnYSfD3YXEh
|
||||
sJm41i5Hd4F2/Y1WrMg82YGZG7Y81cM9LgUxKcikTm4JnEn9G1yg56hSbKs1a0E0
|
||||
J9Gk84lufWpiUqpX9ASsUPttnYzgljo24x7zeNEaXsVOKgu+88Cc+bBn7AORGXM4
|
||||
YtYbxVGhAgPf0vOalNl/kDwfE1jwBz0r6QIDAQABo1MwUTAdBgNVHQ4EFgQUxuo7
|
||||
FdlD/iQ7cMnORpKFQ0JW9I4wHwYDVR0jBBgwFoAUxuo7FdlD/iQ7cMnORpKFQ0JW
|
||||
9I4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfINyfrlE/f3W
|
||||
cTNFiVMzqCa+oeNtvlkaCKgtjJdow8xwAc/Mxae76dkN3Mh8yegKV9+QRY6rw6q0
|
||||
dfma/Rg0tJgrI9El64XdGRcMpwGdtujZbE/bCGTJwLpcIM051cr/NCtYYduM2RU8
|
||||
i5uc8z5sQbMIJHdwDDm4PevA4WrqteUo5bXFQp9jYessDIkjIg7n5hGNvSNtfpVV
|
||||
0fGDYPD9yNydgRMe6EwqQ0Z9p6yfq4o60JceYt4MfbbcGxzwrQc41ou2wKM/iKnQ
|
||||
FBwCOaa+imgn01Qno/PdisV05KM7uSv/dK1v33nrk4xPSVG8u0aGe440U1CcGXbB
|
||||
jy3ACMw/ZQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,27 +1,28 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA8/wKBHPkz3ZvgKnHIavHyzVpLBUGam7AgRp6vK1BtW+jLFZQ
|
||||
DpA1EBkZTT7Pb9P/qc4F+yGFzwhXFe2DjTbE1H7wWE6uN5nzsnJgHW90l+pAcoMX
|
||||
YWLA0WUfrwgCJI6EZkQdtJnodZqLajOfLKatyHE/FQA00SZdTiOPFU58Zl3QrZpD
|
||||
splPePA3GrjKMZtN3i/4VOnp4jRLo252wd9OZ6DRtFuXN4eos/Qr6lAA9ZMMo4nJ
|
||||
Pn9WrQdmGJ9W8pgPnW+mDd4k6NL9q2lpiHOw/qqqZDWgkcYDG9hzel9/0n5Io7TS
|
||||
YnY6hzG7Nn7ZSief5Tgz2awj0DnGCuk0/DCGdQIDAQABAoIBAGjKc7L94+SHRdTJ
|
||||
FtILacCJrCZW0W6dKulIajbnYzV+QWMlnzTiEyha31ciBw5My54u8rqt5z7Ioj60
|
||||
yK+6OkfaTXhgMsuGv/iAz29VE4q7/fow+7XEKHTHLhiLJAB3hb42u1t6TzFTs1Vl
|
||||
3pPa8wEIQsPOVuENzT1mYGoST7PW+LBIMr9ScMnRHfC0MNdV/ntQiXideOAd5PkA
|
||||
4O7fNgYZ8CTAZ8rOLYTMFF76/c/jLiqfeghqbIhqMykk36kd7Lud//FRykVsn1aJ
|
||||
REUva/SjVEth5kITot1hpMC4SIElWpha2YxiiZFoSXSaUbtHpymiUGV01cYtMWk0
|
||||
MZ5HN3ECgYEA/74U8DpwPxd4up9syKyNqOqrCrYnhEEC/tdU/W5wECi4y5kppjdd
|
||||
88lZzICVPzk2fezYXlCO9HiSHU1UfcEsY3u16qNCvylK7Qz1OqXV/Ncj59891Q5Z
|
||||
K0UBcbnrv+YD6muZuhlHEbyDPqYO091G9Gf/BbL5JIBDzg1qFO9Dh9cCgYEA9Drt
|
||||
O9PJ5Sjz3mXQVtVHpwyhOVnd7CUv8a1zkUQCK5uQeaiF5kal1FIo7pLOr3KAvG0C
|
||||
pXbm/TobwlfAfcERQN88aPN8Z/l1CB0oKV6ipBMD2/XLzDRtx8lpTeh/BB8jIhrz
|
||||
+FDJY54HCzLfW0P5kT+Cyw51ofjziPnFdO/Z6pMCgYEAon17gEchGnUnWCwDSl2Y
|
||||
hELV+jBSW02TQag/b+bDfQDiqTnfpKR5JXRBghYQveL0JH5f200EB4C0FboUfPJH
|
||||
6c2ogDTLK/poiMU66tCDbeqj/adx+fTr4votOL0QdRUIV+GWAxAcf8BvA1cvBJ4L
|
||||
fy60ckKM2gxFCJ6tUC/VkHECgYBoMDNAUItSnXPbrmeAg5/7naGxy6qmsP6RBUPF
|
||||
9tNOMyEhJUlqAT2BJEOd8zcFFb3hpEd6uwyzfnSVJcZSX2iy2gj1ZNnvqTXJ7lZR
|
||||
v7N2dz4wOd1lEgC7OCsaN1LoOThNtl3Z0uz2+FVc66jpUEhJNGThpxt7q66JArS/
|
||||
vAqkzQKBgFkzqA6QpnH5KhOCoZcuLQ4MtvnNHOx1xSm2B0gKDVJzGkHexTmOJvwM
|
||||
ZhHXRl9txS4icejS+AGUXNBzCWEusfhDaZpZqS6zt6UxEjMsLj/Te7z++2KQn4t/
|
||||
aI77jClydW1pJvICtqm5v+sukVZvQTTJza9ujta6fj7u2s671np9
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrMkg2BC1yFHv3
|
||||
1qE+m7aDB4cKbHtzK1Dn1kVbgndAFEgH4MWctIx8lrv3uzRKXp/632lPdAtiNt00
|
||||
r5HrQcZ3m0zfVwQhrnXwBwOiR+TMaj9yVj51uzg5wV7XQfECVem7pLn/KJDS7gIh
|
||||
/W5BKxLneQ2lpFZwTPpn+zDzSSwISdhJ8PdhcSGwmbjWLkd3gXb9jVasyDzZgZkb
|
||||
tjzVwz0uBTEpyKRObgmcSf0bXKDnqFJsqzVrQTQn0aTziW59amJSqlf0BKxQ+22d
|
||||
jOCWOjbjHvN40RpexU4qC77zwJz5sGfsA5EZczhi1hvFUaECA9/S85qU2X+QPB8T
|
||||
WPAHPSvpAgMBAAECggEADrAtoUwCQpRYWIv9LkJ5sJjmPX8DDn9Fnaz099d9qy1L
|
||||
5GuLRbLF9PCRdKO55k5jenvnOXs5RZym2QNEfHaA9GuKKmvF8N/awmFpSE46M4Gp
|
||||
xOhHMdG1qFroOLhkv1cNfhB+XTjzrm1GS8VGZU5jr+5Jo4/lUxUZTO+XCq1mAXK0
|
||||
io58x7K3Z7P26/46yeuuqNggbFCxeHfHSK8SXYg6QxgjlZyA/A8tHDuvhDCH81q/
|
||||
AISHxIFmly8ZQ2KxuVCSyHXQkPwODPh2F/4UpOq5EdN781CuV/W4e73WKBwcDUQM
|
||||
aEX5KF8D2C0nXjk592SS9SFmXqw9ZD77sAL2JmbE4wKBgQDGoKT8i3xn3x4nAju0
|
||||
yTxK6jaV72Lrdn5HmOZyJb8irAUr9B0t33zaubzXlM1LpuS/JAQwtppShGo1jPlt
|
||||
T8iE2+JR9UKi6siHV/3EKC15i5UT3p30txdR2KS+hAasb7qXvM6sgM0nwzoS9PUy
|
||||
LbKnRN8UYTvJmEKU1vuyeFQAQwKBgQDcpUQpNhtf4GrT7EoBh8E24yOO2k9XludW
|
||||
yhaEddTYe2FXmqf9MGuPPX+dBj3G1SY6RNXvjmVjr3uvFN5qz+mtOuAkm7g99KkI
|
||||
bKjkVuCgx6mpiH89dt7d/3uTcKXEkai0F7JgSPd1mcSxjRybhyna4qC4CuCHjICU
|
||||
Ug00LAqGYwKBgGjX6N6JPgySABd1HVDrG9ErWc7AwkUpkbR3J8S+2eoSRNSTkUdi
|
||||
fUPy4JQmrkqteHbQKwoPiNvfmzRTCmHByEUgz5CVViwqo9iVAJUm5AIRRIptapD+
|
||||
h+ei5CrQA7nHbAWmGq2Be0juytuwwzBOYMvcFahrPqTFovdvlwH4c9aDAoGAYAk6
|
||||
4qkfPxrhxH3rNEFPUsGIX4wbzqbq6DarmFnlG5iQJN420hf6KO1+luz5hIqPyfre
|
||||
Fxemf74Imor9yAXY0sJ2fticV7Mew4Dv/frmaHSfHyA/KZSMqpmhwunb7PPtNv29
|
||||
cPUxaClWmGUwF228RP4xMAnj8nuwF16jSpsEtbsCgYEAxEaEzpuknqV4AOnlJFBQ
|
||||
7JqSkvW7w4Zy8hnUNmqhOOX1KwB0YudfirHceGtOpjxWQmHqjCHZqau0nEU02+ev
|
||||
qUQsdiGKeJvWHYyqEldOdNd49qaux19IPQBeKRJQGWheGHDWGlg1aSDw3jY0sESS
|
||||
3VqdbL5Z3XRXozQdwmNyfbw=
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
1
src/deps/src/lua-resty-lock/.gitattributes
vendored
Normal file
1
src/deps/src/lua-resty-lock/.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.t linguist-language=Text
|
10
src/deps/src/lua-resty-lock/.gitignore
vendored
Normal file
10
src/deps/src/lua-resty-lock/.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
go
|
||||
t/servroot/
|
||||
reindex
|
||||
nginx
|
||||
ctags
|
||||
tags
|
||||
a.lua
|
67
src/deps/src/lua-resty-lock/.travis.yml
Normal file
67
src/deps/src/lua-resty-lock/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
sudo: required
|
||||
dist: bionic
|
||||
|
||||
os: linux
|
||||
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- download-cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- JOBS=3
|
||||
- NGX_BUILD_JOBS=$JOBS
|
||||
- LUAJIT_PREFIX=/opt/luajit21
|
||||
- LUAJIT_LIB=$LUAJIT_PREFIX/lib
|
||||
- LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
|
||||
- LUA_INCLUDE_DIR=$LUAJIT_INC
|
||||
- LUA_CMODULE_DIR=/lib
|
||||
- OPENSSL_PREFIX=/opt/ssl
|
||||
- OPENSSL_LIB=$OPENSSL_PREFIX/lib
|
||||
- OPENSSL_INC=$OPENSSL_PREFIX/include
|
||||
- OPENSSL_VER=1.1.1k
|
||||
- LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
|
||||
- TEST_NGINX_SLEEP=0.006
|
||||
matrix:
|
||||
- NGINX_VERSION=1.19.9
|
||||
|
||||
install:
|
||||
- if [ ! -d download-cache ]; then mkdir download-cache; fi
|
||||
- if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -O download-cache/openssl-$OPENSSL_VER.tar.gz https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz; fi
|
||||
- sudo apt-get install -qq -y cpanminus axel
|
||||
- sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- git clone https://github.com/openresty/openresty.git ../openresty
|
||||
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
|
||||
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
|
||||
- git clone https://github.com/openresty/nginx-devel-utils.git
|
||||
- git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
|
||||
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
|
||||
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
|
||||
- git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git
|
||||
- git clone https://github.com/openresty/mockeagain.git
|
||||
|
||||
script:
|
||||
- cd luajit2/
|
||||
- make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- cd ..
|
||||
- tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz
|
||||
- cd openssl-$OPENSSL_VER/
|
||||
- ./config shared --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- cd ../mockeagain/ && make CC=$CC -j$JOBS && cd ..
|
||||
- export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH
|
||||
- export LD_PRELOAD=$PWD/mockeagain/mockeagain.so
|
||||
- export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH
|
||||
- export TEST_NGINX_RESOLVER=8.8.4.4
|
||||
- export NGX_BUILD_CC=$CC
|
||||
- ngx-build $NGINX_VERSION --with-ipv6 --with-http_realip_module --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --with-debug > build.log 2>&1 || (cat build.log && exit 1)
|
||||
- nginx -V
|
||||
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
|
||||
- prove -r t
|
18
src/deps/src/lua-resty-lock/Makefile
Normal file
18
src/deps/src/lua-resty-lock/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
OPENRESTY_PREFIX=/usr/local/openresty
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
LUA_INCLUDE_DIR ?= $(PREFIX)/include
|
||||
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
|
||||
INSTALL ?= install
|
||||
|
||||
.PHONY: all test install
|
||||
|
||||
all: ;
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty/
|
||||
$(INSTALL) lib/resty/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty/
|
||||
|
||||
test: all
|
||||
PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t
|
||||
|
419
src/deps/src/lua-resty-lock/README.markdown
Normal file
419
src/deps/src/lua-resty-lock/README.markdown
Normal file
|
@ -0,0 +1,419 @@
|
|||
Name
|
||||
====
|
||||
|
||||
lua-resty-lock - Simple shm-based nonblocking lock API
|
||||
|
||||
Table of Contents
|
||||
=================
|
||||
|
||||
* [Name](#name)
|
||||
* [Status](#status)
|
||||
* [Synopsis](#synopsis)
|
||||
* [Description](#description)
|
||||
* [Methods](#methods)
|
||||
* [new](#new)
|
||||
* [lock](#lock)
|
||||
* [unlock](#unlock)
|
||||
* [expire](#expire)
|
||||
* [For Multiple Lua Light Threads](#for-multiple-lua-light-threads)
|
||||
* [For Cache Locks](#for-cache-locks)
|
||||
* [Limitations](#limitations)
|
||||
* [Prerequisites](#prerequisites)
|
||||
* [Installation](#installation)
|
||||
* [TODO](#todo)
|
||||
* [Community](#community)
|
||||
* [English Mailing List](#english-mailing-list)
|
||||
* [Chinese Mailing List](#chinese-mailing-list)
|
||||
* [Bugs and Patches](#bugs-and-patches)
|
||||
* [Author](#author)
|
||||
* [Copyright and License](#copyright-and-license)
|
||||
* [See Also](#see-also)
|
||||
|
||||
Status
|
||||
======
|
||||
|
||||
This library is still under early development and is production ready.
|
||||
|
||||
Synopsis
|
||||
========
|
||||
|
||||
```lua
|
||||
# nginx.conf
|
||||
|
||||
http {
|
||||
# you do not need the following line if you are using the
|
||||
# OpenResty bundle:
|
||||
lua_package_path "/path/to/lua-resty-core/lib/?.lua;/path/to/lua-resty-lock/lib/?.lua;;";
|
||||
|
||||
lua_shared_dict my_locks 100k;
|
||||
|
||||
server {
|
||||
...
|
||||
|
||||
location = /t {
|
||||
content_by_lua '
|
||||
local resty_lock = require "resty.lock"
|
||||
for i = 1, 2 do
|
||||
local lock, err = resty_lock:new("my_locks")
|
||||
if not lock then
|
||||
ngx.say("failed to create lock: ", err)
|
||||
end
|
||||
|
||||
local elapsed, err = lock:lock("my_key")
|
||||
ngx.say("lock: ", elapsed, ", ", err)
|
||||
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
ngx.say("failed to unlock: ", err)
|
||||
end
|
||||
ngx.say("unlock: ", ok)
|
||||
end
|
||||
';
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
This library implements a simple mutex lock in a similar way to ngx_proxy module's [proxy_cache_lock directive](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_lock).
|
||||
|
||||
Under the hood, this library uses [ngx_lua](https://github.com/openresty/lua-nginx-module) module's shared memory dictionaries. The lock waiting is nonblocking because we use stepwise [ngx.sleep](https://github.com/openresty/lua-nginx-module#ngxsleep) to poll the lock periodically.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Methods
|
||||
=======
|
||||
|
||||
To load this library,
|
||||
|
||||
1. you need to specify this library's path in ngx_lua's [lua_package_path](https://github.com/openresty/lua-nginx-module#lua_package_path) directive. For example, `lua_package_path "/path/to/lua-resty-lock/lib/?.lua;;";`.
|
||||
2. you use `require` to load the library into a local Lua variable:
|
||||
|
||||
```lua
|
||||
local lock = require "resty.lock"
|
||||
```
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
new
|
||||
---
|
||||
`syntax: obj, err = lock:new(dict_name)`
|
||||
|
||||
`syntax: obj, err = lock:new(dict_name, opts)`
|
||||
|
||||
Creates a new lock object instance by specifying the shared dictionary name (created by [lua_shared_dict](http://https://github.com/openresty/lua-nginx-module#lua_shared_dict)) and an optional options table `opts`.
|
||||
|
||||
In case of failure, returns `nil` and a string describing the error.
|
||||
|
||||
The options table accepts the following options:
|
||||
|
||||
* `exptime`
|
||||
Specifies expiration time (in seconds) for the lock entry in the shared memory dictionary. You can specify up to `0.001` seconds. Default to 30 (seconds). Even if the invoker does not call `unlock` or the object holding the lock is not GC'd, the lock will be released after this time. So deadlock won't happen even when the worker process holding the lock crashes.
|
||||
* `timeout`
|
||||
Specifies the maximal waiting time (in seconds) for the [lock](#lock) method calls on the current object instance. You can specify up to `0.001` seconds. Default to 5 (seconds). This option value cannot be bigger than `exptime`. This timeout is to prevent a [lock](#lock) method call from waiting forever.
|
||||
You can specify `0` to make the [lock](#lock) method return immediately without waiting if it cannot acquire the lock right away.
|
||||
* `step`
|
||||
Specifies the initial step (in seconds) of sleeping when waiting for the lock. Default to `0.001` (seconds). When the [lock](#lock) method is waiting on a busy lock, it sleeps by steps. The step size is increased by a ratio (specified by the `ratio` option) until reaching the step size limit (specified by the `max_step` option).
|
||||
* `ratio`
|
||||
Specifies the step increasing ratio. Default to 2, that is, the step size doubles at each waiting iteration.
|
||||
* `max_step`
|
||||
Specifies the maximal step size (i.e., sleep interval, in seconds) allowed. See also the `step` and `ratio` options). Default to 0.5 (seconds).
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
lock
|
||||
----
|
||||
`syntax: elapsed, err = obj:lock(key)`
|
||||
|
||||
Tries to lock a key across all the Nginx worker processes in the current Nginx server instance. Different keys are different locks.
|
||||
|
||||
The length of the key string must not be larger than 65535 bytes.
|
||||
|
||||
Returns the waiting time (in seconds) if the lock is successfully acquired. Otherwise returns `nil` and a string describing the error.
|
||||
|
||||
The waiting time is not from the wallclock, but rather is from simply adding up all the waiting "steps". A nonzero `elapsed` return value indicates that someone else has just hold this lock. But a zero return value cannot gurantee that no one else has just acquired and released the lock.
|
||||
|
||||
When this method is waiting on fetching the lock, no operating system threads will be blocked and the current Lua "light thread" will be automatically yielded behind the scene.
|
||||
|
||||
It is strongly recommended to always call the [unlock()](#unlock) method to actively release the lock as soon as possible.
|
||||
|
||||
If the [unlock()](#unlock) method is never called after this method call, the lock will get released when
|
||||
|
||||
1. the current `resty.lock` object instance is collected automatically by the Lua GC.
|
||||
2. the `exptime` for the lock entry is reached.
|
||||
|
||||
Common errors for this method call is
|
||||
* "timeout"
|
||||
: The timeout threshold specified by the `timeout` option of the [new](#new) method is exceeded.
|
||||
* "locked"
|
||||
: The current `resty.lock` object instance is already holding a lock (not necessarily of the same key).
|
||||
|
||||
Other possible errors are from ngx_lua's shared dictionary API.
|
||||
|
||||
It is required to create different `resty.lock` instances for multiple simultaneous locks (i.e., those around different keys).
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
unlock
|
||||
------
|
||||
`syntax: ok, err = obj:unlock()`
|
||||
|
||||
Releases the lock held by the current `resty.lock` object instance.
|
||||
|
||||
Returns `1` on success. Returns `nil` and a string describing the error otherwise.
|
||||
|
||||
If you call `unlock` when no lock is currently held, the error "unlocked" will be returned.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
expire
|
||||
------
|
||||
`syntax: ok, err = obj:expire(timeout)`
|
||||
|
||||
Sets the TTL of the lock held by the current `resty.lock` object instance. This will reset the
|
||||
timeout of the lock to `timeout` seconds if it is given, otherwise the `timeout` provided while
|
||||
calling [new](#new) will be used.
|
||||
|
||||
Note that the `timeout` supplied inside this function is independent from the `timeout` provided while
|
||||
calling [new](#new). Calling `expire()` will not change the `timeout` value specified inside [new](#new)
|
||||
and subsequent `expire(nil)` call will still use the `timeout` number from [new](#new).
|
||||
|
||||
Returns `true` on success. Returns `nil` and a string describing the error otherwise.
|
||||
|
||||
If you call `expire` when no lock is currently held, the error "unlocked" will be returned.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
For Multiple Lua Light Threads
|
||||
==============================
|
||||
|
||||
It is always a bad idea to share a single `resty.lock` object instance across multiple ngx_lua "light threads" because the object itself is stateful and is vulnerable to race conditions. It is highly recommended to always allocate a separate `resty.lock` object instance for each "light thread" that needs one.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
For Cache Locks
|
||||
===============
|
||||
|
||||
One common use case for this library is avoid the so-called "dog-pile effect", that is, to limit concurrent backend queries for the same key when a cache miss happens. This usage is similar to the standard ngx_proxy module's [proxy_cache_lock](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_lock) directive.
|
||||
|
||||
The basic workflow for a cache lock is as follows:
|
||||
|
||||
1. Check the cache for a hit with the key. If a cache miss happens, proceed to step 2.
|
||||
2. Instantiate a `resty.lock` object, call the [lock](#lock) method on the key, and check the 1st return value, i.e., the lock waiting time. If it is `nil`, handle the error; otherwise proceed to step 3.
|
||||
3. Check the cache again for a hit. If it is still a miss, proceed to step 4; otherwise release the lock by calling [unlock](#unlock) and then return the cached value.
|
||||
4. Query the backend (the data source) for the value, put the result into the cache, and then release the lock currently held by calling [unlock](#unlock).
|
||||
|
||||
Below is a kinda complete code example that demonstrates the idea.
|
||||
|
||||
```lua
|
||||
local resty_lock = require "resty.lock"
|
||||
local cache = ngx.shared.my_cache
|
||||
|
||||
-- step 1:
|
||||
local val, err = cache:get(key)
|
||||
if val then
|
||||
ngx.say("result: ", val)
|
||||
return
|
||||
end
|
||||
|
||||
if err then
|
||||
return fail("failed to get key from shm: ", err)
|
||||
end
|
||||
|
||||
-- cache miss!
|
||||
-- step 2:
|
||||
local lock, err = resty_lock:new("my_locks")
|
||||
if not lock then
|
||||
return fail("failed to create lock: ", err)
|
||||
end
|
||||
|
||||
local elapsed, err = lock:lock(key)
|
||||
if not elapsed then
|
||||
return fail("failed to acquire the lock: ", err)
|
||||
end
|
||||
|
||||
-- lock successfully acquired!
|
||||
|
||||
-- step 3:
|
||||
-- someone might have already put the value into the cache
|
||||
-- so we check it here again:
|
||||
val, err = cache:get(key)
|
||||
if val then
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
return fail("failed to unlock: ", err)
|
||||
end
|
||||
|
||||
ngx.say("result: ", val)
|
||||
return
|
||||
end
|
||||
|
||||
--- step 4:
|
||||
local val = fetch_redis(key)
|
||||
if not val then
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
return fail("failed to unlock: ", err)
|
||||
end
|
||||
|
||||
-- FIXME: we should handle the backend miss more carefully
|
||||
-- here, like inserting a stub value into the cache.
|
||||
|
||||
ngx.say("no value found")
|
||||
return
|
||||
end
|
||||
|
||||
-- update the shm cache with the newly fetched value
|
||||
local ok, err = cache:set(key, val, 1)
|
||||
if not ok then
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
return fail("failed to unlock: ", err)
|
||||
end
|
||||
|
||||
return fail("failed to update shm cache: ", err)
|
||||
end
|
||||
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
return fail("failed to unlock: ", err)
|
||||
end
|
||||
|
||||
ngx.say("result: ", val)
|
||||
```
|
||||
|
||||
Here we assume that we use the ngx_lua shared memory dictionary to cache the Redis query results and we have the following configurations in `nginx.conf`:
|
||||
|
||||
```nginx
|
||||
# you may want to change the dictionary size for your cases.
|
||||
lua_shared_dict my_cache 10m;
|
||||
lua_shared_dict my_locks 1m;
|
||||
```
|
||||
|
||||
The `my_cache` dictionary is for the data cache while the `my_locks` dictionary is for `resty.lock` itself.
|
||||
|
||||
Several important things to note in the example above:
|
||||
|
||||
1. You need to release the lock as soon as possible, even when some other unrelated errors happen.
|
||||
2. You need to update the cache with the result got from the backend *before* releasing the lock so other threads already waiting on the lock can get cached value when they get the lock afterwards.
|
||||
3. When the backend returns no value at all, we should handle the case carefully by inserting some stub value into the cache.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
Some of this library's API functions may yield. So do not call those functions in `ngx_lua` module contexts where yielding is not supported (yet), like `init_by_lua*`,
|
||||
`init_worker_by_lua*`, `header_filter_by_lua*`, `body_filter_by_lua*`, `balancer_by_lua*`, and `log_by_lua*`.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
* [LuaJIT](http://luajit.org) 2.0+
|
||||
* [ngx_lua](https://github.com/openresty/lua-nginx-module) 0.8.10+
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
It is recommended to use the latest [OpenResty bundle](http://openresty.org) directly where this library
|
||||
is bundled and enabled by default. At least OpenResty 1.4.2.9 is required. And you need to enable LuaJIT when building your OpenResty
|
||||
bundle by passing the `--with-luajit` option to its `./configure` script. No extra Nginx configuration is required.
|
||||
|
||||
If you want to use this library with your own Nginx build (with ngx_lua), then you need to
|
||||
ensure you are using at least ngx_lua 0.8.10. Also, You need to configure
|
||||
the [lua_package_path](https://github.com/openresty/lua-nginx-module#lua_package_path) directive to
|
||||
add the path of your lua-resty-lock and lua-resty-core source directories to ngx_lua's Lua module search path, as in
|
||||
|
||||
```nginx
|
||||
# nginx.conf
|
||||
http {
|
||||
lua_package_path "/path/to/lua-resty-lock/lib/?.lua;/path/to/lua-resty-core/lib/?.lua;;";
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
and then load the library in Lua:
|
||||
|
||||
```lua
|
||||
local resty_lock = require "resty.lock"
|
||||
```
|
||||
|
||||
Note that this library depends on the [lua-resty-core](https://github.com/openresty/lua-resty-core) library
|
||||
which is also enabled by default in the OpenResty bundle.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
TODO
|
||||
====
|
||||
|
||||
* We should simplify the current implementation when LuaJIT 2.1 gets support for `__gc` metamethod on normal Lua tables. Right now we are using an FFI cdata and a ref/unref memo table to work around this, which is rather ugly and a bit inefficient.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Community
|
||||
=========
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
English Mailing List
|
||||
--------------------
|
||||
|
||||
The [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Chinese Mailing List
|
||||
--------------------
|
||||
|
||||
The [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Bugs and Patches
|
||||
================
|
||||
|
||||
Please report bugs or submit patches by
|
||||
|
||||
1. creating a ticket on the [GitHub Issue Tracker](https://github.com/openresty/lua-resty-lock/issues),
|
||||
1. or posting to the [OpenResty community](#community).
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Author
|
||||
======
|
||||
|
||||
Yichun "agentzh" Zhang (章亦春) <agentzh@gmail.com>, OpenResty Inc.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
Copyright and License
|
||||
=====================
|
||||
|
||||
This module is licensed under the BSD license.
|
||||
|
||||
Copyright (C) 2013-2019, by Yichun "agentzh" Zhang, OpenResty Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
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 HOLDER 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.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
See Also
|
||||
========
|
||||
* the ngx_lua module: https://github.com/openresty/lua-nginx-module
|
||||
* OpenResty: http://openresty.org
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
10
src/deps/src/lua-resty-lock/dist.ini
Normal file
10
src/deps/src/lua-resty-lock/dist.ini
Normal file
|
@ -0,0 +1,10 @@
|
|||
name=lua-resty-lock
|
||||
abstract=Simple shm-based nonblocking lock API
|
||||
author=Yichun Zhang (agentzh)
|
||||
is_original=yes
|
||||
license=2bsd
|
||||
lib_dir=lib
|
||||
doc_dir=lib
|
||||
repo_link=https://github.com/openresty/lua-resty-lock
|
||||
main_module=lib/resty/lock.lua
|
||||
requires = luajit
|
222
src/deps/src/lua-resty-lock/lib/resty/lock.lua
Normal file
222
src/deps/src/lua-resty-lock/lib/resty/lock.lua
Normal file
|
@ -0,0 +1,222 @@
|
|||
-- Copyright (C) Yichun Zhang (agentzh)
|
||||
|
||||
|
||||
require "resty.core.shdict" -- enforce this to avoid dead locks
|
||||
|
||||
local ffi = require "ffi"
|
||||
local ffi_new = ffi.new
|
||||
local shared = ngx.shared
|
||||
local sleep = ngx.sleep
|
||||
local log = ngx.log
|
||||
local max = math.max
|
||||
local min = math.min
|
||||
local debug = ngx.config.debug
|
||||
local setmetatable = setmetatable
|
||||
local tonumber = tonumber
|
||||
|
||||
local _M = { _VERSION = '0.08' }
|
||||
local mt = { __index = _M }
|
||||
|
||||
local ERR = ngx.ERR
|
||||
local FREE_LIST_REF = 0
|
||||
|
||||
-- FIXME: we don't need this when we have __gc metamethod support on Lua
|
||||
-- tables.
|
||||
local memo = {}
|
||||
if debug then _M.memo = memo end
|
||||
|
||||
|
||||
local function ref_obj(key)
|
||||
if key == nil then
|
||||
return -1
|
||||
end
|
||||
local ref = memo[FREE_LIST_REF]
|
||||
if ref and ref ~= 0 then
|
||||
memo[FREE_LIST_REF] = memo[ref]
|
||||
|
||||
else
|
||||
ref = #memo + 1
|
||||
end
|
||||
memo[ref] = key
|
||||
|
||||
-- print("ref key_id returned ", ref)
|
||||
return ref
|
||||
end
|
||||
if debug then _M.ref_obj = ref_obj end
|
||||
|
||||
|
||||
local function unref_obj(ref)
|
||||
if ref >= 0 then
|
||||
memo[ref] = memo[FREE_LIST_REF]
|
||||
memo[FREE_LIST_REF] = ref
|
||||
end
|
||||
end
|
||||
if debug then _M.unref_obj = unref_obj end
|
||||
|
||||
|
||||
local function gc_lock(cdata)
|
||||
local dict_id = tonumber(cdata.dict_id)
|
||||
local key_id = tonumber(cdata.key_id)
|
||||
|
||||
-- print("key_id: ", key_id, ", key: ", memo[key_id], "dict: ",
|
||||
-- type(memo[cdata.dict_id]))
|
||||
if key_id > 0 then
|
||||
local key = memo[key_id]
|
||||
unref_obj(key_id)
|
||||
local dict = memo[dict_id]
|
||||
-- print("dict.delete type: ", type(dict.delete))
|
||||
local ok, err = dict:delete(key)
|
||||
if not ok then
|
||||
log(ERR, 'failed to delete key "', key, '": ', err)
|
||||
end
|
||||
cdata.key_id = 0
|
||||
end
|
||||
|
||||
unref_obj(dict_id)
|
||||
end
|
||||
|
||||
|
||||
local ctype = ffi.metatype("struct { int key_id; int dict_id; }",
|
||||
{ __gc = gc_lock })
|
||||
|
||||
|
||||
function _M.new(_, dict_name, opts)
|
||||
local dict = shared[dict_name]
|
||||
if not dict then
|
||||
return nil, "dictionary not found"
|
||||
end
|
||||
local cdata = ffi_new(ctype)
|
||||
cdata.key_id = 0
|
||||
cdata.dict_id = ref_obj(dict)
|
||||
|
||||
local timeout, exptime, step, ratio, max_step
|
||||
if opts then
|
||||
timeout = opts.timeout
|
||||
exptime = opts.exptime
|
||||
step = opts.step
|
||||
ratio = opts.ratio
|
||||
max_step = opts.max_step
|
||||
end
|
||||
|
||||
if not exptime then
|
||||
exptime = 30
|
||||
end
|
||||
|
||||
if timeout then
|
||||
timeout = min(timeout, exptime)
|
||||
|
||||
if step then
|
||||
step = min(step, timeout)
|
||||
end
|
||||
end
|
||||
|
||||
local self = {
|
||||
cdata = cdata,
|
||||
dict = dict,
|
||||
timeout = timeout or 5,
|
||||
exptime = exptime,
|
||||
step = step or 0.001,
|
||||
ratio = ratio or 2,
|
||||
max_step = max_step or 0.5,
|
||||
}
|
||||
setmetatable(self, mt)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function _M.lock(self, key)
|
||||
if not key then
|
||||
return nil, "nil key"
|
||||
end
|
||||
|
||||
local dict = self.dict
|
||||
local cdata = self.cdata
|
||||
if cdata.key_id > 0 then
|
||||
return nil, "locked"
|
||||
end
|
||||
local exptime = self.exptime
|
||||
local ok, err = dict:add(key, true, exptime)
|
||||
if ok then
|
||||
cdata.key_id = ref_obj(key)
|
||||
self.key = key
|
||||
return 0
|
||||
end
|
||||
if err ~= "exists" then
|
||||
return nil, err
|
||||
end
|
||||
-- lock held by others
|
||||
local step = self.step
|
||||
local ratio = self.ratio
|
||||
local timeout = self.timeout
|
||||
local max_step = self.max_step
|
||||
local elapsed = 0
|
||||
while timeout > 0 do
|
||||
sleep(step)
|
||||
elapsed = elapsed + step
|
||||
timeout = timeout - step
|
||||
|
||||
local ok, err = dict:add(key, true, exptime)
|
||||
if ok then
|
||||
cdata.key_id = ref_obj(key)
|
||||
self.key = key
|
||||
return elapsed
|
||||
end
|
||||
|
||||
if err ~= "exists" then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if timeout <= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
step = min(max(0.001, step * ratio), timeout, max_step)
|
||||
end
|
||||
|
||||
return nil, "timeout"
|
||||
end
|
||||
|
||||
|
||||
function _M.unlock(self)
|
||||
local dict = self.dict
|
||||
local cdata = self.cdata
|
||||
local key_id = tonumber(cdata.key_id)
|
||||
if key_id <= 0 then
|
||||
return nil, "unlocked"
|
||||
end
|
||||
|
||||
local key = memo[key_id]
|
||||
unref_obj(key_id)
|
||||
|
||||
local ok, err = dict:delete(key)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
cdata.key_id = 0
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
function _M.expire(self, time)
|
||||
local dict = self.dict
|
||||
local cdata = self.cdata
|
||||
local key_id = tonumber(cdata.key_id)
|
||||
if key_id <= 0 then
|
||||
return nil, "unlocked"
|
||||
end
|
||||
|
||||
if not time then
|
||||
time = self.exptime
|
||||
end
|
||||
|
||||
local ok, err = dict:replace(self.key, true, time)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
return _M
|
546
src/deps/src/lua-resty-lock/t/sanity.t
Normal file
546
src/deps/src/lua-resty-lock/t/sanity.t
Normal file
|
@ -0,0 +1,546 @@
|
|||
# vim:set ft= ts=4 sw=4 et:
|
||||
|
||||
use Test::Nginx::Socket::Lua;
|
||||
use Cwd qw(cwd);
|
||||
|
||||
repeat_each(2);
|
||||
|
||||
plan tests => repeat_each() * (blocks() * 3);
|
||||
|
||||
my $pwd = cwd();
|
||||
|
||||
our $HttpConfig = qq{
|
||||
lua_package_path "../lua-resty-core/lib/?.lua;lib/?.lua;;";
|
||||
lua_package_cpath "/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;";
|
||||
lua_shared_dict cache_locks 100k;
|
||||
};
|
||||
|
||||
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
|
||||
$ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
|
||||
|
||||
no_long_string();
|
||||
#no_diff();
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: lock is subject to garbage collection
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
for i = 1, 2 do
|
||||
collectgarbage("collect")
|
||||
local lock = lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock("foo")
|
||||
ngx.say("lock: ", elapsed, ", ", err)
|
||||
end
|
||||
collectgarbage("collect")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
lock: 0, nil
|
||||
lock: 0, nil
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 2: serial lock and unlock
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
for i = 1, 2 do
|
||||
local lock = lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock("foo")
|
||||
ngx.say("lock: ", elapsed, ", ", err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
ngx.say("failed to unlock: ", err)
|
||||
end
|
||||
ngx.say("unlock: ", ok)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
lock: 0, nil
|
||||
unlock: 1
|
||||
lock: 0, nil
|
||||
unlock: 1
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 3: timed out locks
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
for i = 1, 2 do
|
||||
local lock1 = lock:new("cache_locks", { timeout = 0.01 })
|
||||
local lock2 = lock:new("cache_locks", { timeout = 0.01 })
|
||||
|
||||
local elapsed, err = lock1:lock("foo")
|
||||
ngx.say("lock 1: lock: ", elapsed, ", ", err)
|
||||
|
||||
local elapsed, err = lock2:lock("foo")
|
||||
ngx.say("lock 2: lock: ", elapsed, ", ", err)
|
||||
|
||||
local ok, err = lock1:unlock()
|
||||
ngx.say("lock 1: unlock: ", ok, ", ", err)
|
||||
|
||||
local ok, err = lock2:unlock()
|
||||
ngx.say("lock 2: unlock: ", ok, ", ", err)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
lock 1: lock: 0, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 1: unlock: 1, nil
|
||||
lock 2: unlock: nil, unlocked
|
||||
lock 1: lock: 0, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 1: unlock: 1, nil
|
||||
lock 2: unlock: nil, unlocked
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 4: waited locks
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_lock = require "resty.lock"
|
||||
local key = "blah"
|
||||
local t, err = ngx.thread.spawn(function ()
|
||||
local lock = resty_lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("sub thread: lock: ", elapsed, " ", err)
|
||||
ngx.sleep(0.1)
|
||||
ngx.say("sub thread: unlock: ", lock:unlock(key))
|
||||
end)
|
||||
|
||||
local lock = resty_lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("main thread: lock: ", elapsed, " ", err)
|
||||
ngx.say("main thread: unlock: ", lock:unlock())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like chop
|
||||
^sub thread: lock: 0 nil
|
||||
sub thread: unlock: 1
|
||||
main thread: lock: 0.12[6-9] nil
|
||||
main thread: unlock: 1
|
||||
$
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 5: waited locks (custom step)
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_lock = require "resty.lock"
|
||||
local key = "blah"
|
||||
local t, err = ngx.thread.spawn(function ()
|
||||
local lock = resty_lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("sub thread: lock: ", elapsed, " ", err)
|
||||
ngx.sleep(0.1)
|
||||
ngx.say("sub thread: unlock: ", lock:unlock(key))
|
||||
end)
|
||||
|
||||
local lock = resty_lock:new("cache_locks", { step = 0.01 })
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("main thread: lock: ", elapsed, " ", err)
|
||||
ngx.say("main thread: unlock: ", lock:unlock())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like chop
|
||||
^sub thread: lock: 0 nil
|
||||
sub thread: unlock: 1
|
||||
main thread: lock: 0.1[4-5]\d* nil
|
||||
main thread: unlock: 1
|
||||
$
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 6: waited locks (custom ratio)
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_lock = require "resty.lock"
|
||||
local key = "blah"
|
||||
local t, err = ngx.thread.spawn(function ()
|
||||
local lock = resty_lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("sub thread: lock: ", elapsed, " ", err)
|
||||
ngx.sleep(0.1)
|
||||
ngx.say("sub thread: unlock: ", lock:unlock(key))
|
||||
end)
|
||||
|
||||
local lock = resty_lock:new("cache_locks", { ratio = 3 })
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("main thread: lock: ", elapsed, " ", err)
|
||||
ngx.say("main thread: unlock: ", lock:unlock())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like chop
|
||||
^sub thread: lock: 0 nil
|
||||
sub thread: unlock: 1
|
||||
main thread: lock: 0.1[2]\d* nil
|
||||
main thread: unlock: 1
|
||||
$
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 7: waited locks (custom max step)
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_lock = require "resty.lock"
|
||||
local key = "blah"
|
||||
local t, err = ngx.thread.spawn(function ()
|
||||
local lock = resty_lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("sub thread: lock: ", elapsed, " ", err)
|
||||
ngx.sleep(0.1)
|
||||
ngx.say("sub thread: unlock: ", lock:unlock(key))
|
||||
end)
|
||||
|
||||
local lock = resty_lock:new("cache_locks", { max_step = 0.05 })
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("main thread: lock: ", elapsed, " ", err)
|
||||
ngx.say("main thread: unlock: ", lock:unlock())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like chop
|
||||
^sub thread: lock: 0 nil
|
||||
sub thread: unlock: 1
|
||||
main thread: lock: 0.11[2-4]\d* nil
|
||||
main thread: unlock: 1
|
||||
$
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 8: lock expired by itself
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local resty_lock = require "resty.lock"
|
||||
local key = "blah"
|
||||
local t, err = ngx.thread.spawn(function ()
|
||||
local lock = resty_lock:new("cache_locks", { exptime = 0.1 })
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("sub thread: lock: ", elapsed, " ", err)
|
||||
ngx.sleep(0.1)
|
||||
-- ngx.say("sub thread: unlock: ", lock:unlock(key))
|
||||
end)
|
||||
|
||||
local lock = resty_lock:new("cache_locks", { max_step = 0.05 })
|
||||
local elapsed, err = lock:lock(key)
|
||||
ngx.say("main thread: lock: ", elapsed, " ", err)
|
||||
ngx.say("main thread: unlock: ", lock:unlock())
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body_like chop
|
||||
^sub thread: lock: 0 nil
|
||||
main thread: lock: 0.11[2-4]\d* nil
|
||||
main thread: unlock: 1
|
||||
$
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 9: ref & unref (1 at most)
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
local memo = lock.memo
|
||||
local ref = lock.ref_obj("foo")
|
||||
ngx.say(#memo)
|
||||
lock.unref_obj(ref)
|
||||
ngx.say(#memo)
|
||||
ref = lock.ref_obj("bar")
|
||||
ngx.say(#memo)
|
||||
lock.unref_obj(ref)
|
||||
ngx.say(#memo)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
1
|
||||
0
|
||||
1
|
||||
0
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- skip_eval: 3: system("$NginxBinary -V 2>&1 | grep -- '--with-debug'") ne 0
|
||||
|
||||
|
||||
|
||||
=== TEST 10: ref & unref (2 at most)
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
local memo = lock.memo
|
||||
|
||||
for i = 1, 2 do
|
||||
local refs = {}
|
||||
|
||||
refs[1] = lock.ref_obj("foo")
|
||||
ngx.say(#memo)
|
||||
|
||||
refs[2] = lock.ref_obj("bar")
|
||||
ngx.say(#memo)
|
||||
|
||||
lock.unref_obj(refs[1])
|
||||
ngx.say(#memo)
|
||||
|
||||
lock.unref_obj(refs[2])
|
||||
ngx.say(#memo)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
1
|
||||
2
|
||||
2
|
||||
2
|
||||
2
|
||||
2
|
||||
1
|
||||
1
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- skip_eval: 3: system("$NginxBinary -V 2>&1 | grep -- '--with-debug'") ne 0
|
||||
|
||||
|
||||
|
||||
=== TEST 11: lock on a nil key
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
local lock = lock:new("cache_locks")
|
||||
local elapsed, err = lock:lock(nil)
|
||||
if elapsed then
|
||||
ngx.say("lock: ", elapsed, ", ", err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
ngx.say("failed to unlock: ", err)
|
||||
end
|
||||
else
|
||||
ngx.say("failed to lock: ", err)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
failed to lock: nil key
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 12: same shdict, multple locks
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
local memo = lock.memo
|
||||
local lock1 = lock:new("cache_locks", { timeout = 0.01 })
|
||||
for i = 1, 3 do
|
||||
lock1:lock("lock_key")
|
||||
lock1:unlock()
|
||||
collectgarbage("collect")
|
||||
end
|
||||
|
||||
local lock2 = lock:new("cache_locks", { timeout = 0.01 })
|
||||
local lock3 = lock:new("cache_locks", { timeout = 0.01 })
|
||||
lock2:lock("lock_key")
|
||||
lock3:lock("lock_key")
|
||||
collectgarbage("collect")
|
||||
|
||||
ngx.say(#memo)
|
||||
|
||||
lock2:unlock()
|
||||
lock3:unlock()
|
||||
collectgarbage("collect")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
4
|
||||
--- no_error_log
|
||||
[error]
|
||||
--- skip_eval: 3: system("$NginxBinary -V 2>&1 | grep -- '--with-debug'") ne 0
|
||||
|
||||
|
||||
|
||||
=== TEST 13: timed out locks (0 timeout)
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
for i = 1, 2 do
|
||||
local lock1 = lock:new("cache_locks", { timeout = 0 })
|
||||
local lock2 = lock:new("cache_locks", { timeout = 0 })
|
||||
|
||||
local elapsed, err = lock1:lock("foo")
|
||||
ngx.say("lock 1: lock: ", elapsed, ", ", err)
|
||||
|
||||
local elapsed, err = lock2:lock("foo")
|
||||
ngx.say("lock 2: lock: ", elapsed, ", ", err)
|
||||
|
||||
local ok, err = lock1:unlock()
|
||||
ngx.say("lock 1: unlock: ", ok, ", ", err)
|
||||
|
||||
local ok, err = lock2:unlock()
|
||||
ngx.say("lock 2: unlock: ", ok, ", ", err)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
lock 1: lock: 0, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 1: unlock: 1, nil
|
||||
lock 2: unlock: nil, unlocked
|
||||
lock 1: lock: 0, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 1: unlock: 1, nil
|
||||
lock 2: unlock: nil, unlocked
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 13: expire()
|
||||
--- http_config eval: $::HttpConfig
|
||||
--- config
|
||||
location = /t {
|
||||
content_by_lua_block {
|
||||
local lock = require "resty.lock"
|
||||
for i = 1, 2 do
|
||||
local lock1 = lock:new("cache_locks", { timeout = 0, exptime = 0.1 })
|
||||
local lock2 = lock:new("cache_locks", { timeout = 0, exptime = 0.1 })
|
||||
|
||||
local exp, err = lock1:expire()
|
||||
ngx.say("lock 1: expire: ", exp, ", ", err)
|
||||
|
||||
local elapsed, err = lock1:lock("foo")
|
||||
ngx.say("lock 1: lock: ", elapsed, ", ", err)
|
||||
|
||||
ngx.sleep(0.06)
|
||||
|
||||
local exp, err = lock1:expire()
|
||||
ngx.say("lock 1: expire: ", exp, ", ", err)
|
||||
|
||||
ngx.sleep(0.06)
|
||||
|
||||
local elapsed, err = lock2:lock("foo")
|
||||
ngx.say("lock 2: lock: ", elapsed, ", ", err)
|
||||
|
||||
local exp, err = lock1:expire(0.2)
|
||||
ngx.say("lock 1: expire: ", exp, ", ", err)
|
||||
|
||||
ngx.sleep(0.15)
|
||||
|
||||
local elapsed, err = lock2:lock("foo")
|
||||
ngx.say("lock 2: lock: ", elapsed, ", ", err)
|
||||
|
||||
ngx.sleep(0.1)
|
||||
|
||||
local elapsed, err = lock2:lock("foo")
|
||||
ngx.say("lock 2: lock: ", elapsed, ", ", err)
|
||||
|
||||
local ok, err = lock2:unlock()
|
||||
ngx.say("lock 2: unlock: ", ok, ", ", err)
|
||||
|
||||
local exp, err = lock2:expire(0.2)
|
||||
ngx.say("lock 2: expire: ", exp, ", ", err)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
lock 1: expire: nil, unlocked
|
||||
lock 1: lock: 0, nil
|
||||
lock 1: expire: true, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 1: expire: true, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 2: lock: 0, nil
|
||||
lock 2: unlock: 1, nil
|
||||
lock 2: expire: nil, unlocked
|
||||
lock 1: expire: nil, unlocked
|
||||
lock 1: lock: 0, nil
|
||||
lock 1: expire: true, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 1: expire: true, nil
|
||||
lock 2: lock: nil, timeout
|
||||
lock 2: lock: 0, nil
|
||||
lock 2: unlock: 1, nil
|
||||
lock 2: expire: nil, unlocked
|
||||
|
||||
--- no_error_log
|
||||
[error]
|
69
src/deps/src/lua-resty-lock/valgrind.suppress
Normal file
69
src/deps/src/lua-resty-lock/valgrind.suppress
Normal file
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
fun:ngx_alloc
|
||||
fun:ngx_event_process_init
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Param
|
||||
epoll_ctl(event)
|
||||
fun:epoll_ctl
|
||||
fun:ngx_epoll_add_event
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:index
|
||||
fun:expand_dynamic_string_token
|
||||
fun:_dl_map_object
|
||||
fun:map_doit
|
||||
fun:_dl_catch_error
|
||||
fun:do_preload
|
||||
fun:dl_main
|
||||
fun:_dl_sysdep_start
|
||||
fun:_dl_start
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Param
|
||||
epoll_ctl(event)
|
||||
fun:epoll_ctl
|
||||
fun:ngx_epoll_init
|
||||
fun:ngx_event_process_init
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Param
|
||||
epoll_ctl(event)
|
||||
fun:epoll_ctl
|
||||
fun:ngx_epoll_notify_init
|
||||
fun:ngx_epoll_init
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Param
|
||||
epoll_ctl(event)
|
||||
fun:epoll_ctl
|
||||
fun:ngx_epoll_test_rdhup
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
fun:ngx_alloc
|
||||
fun:ngx_set_environment
|
||||
fun:ngx_single_process_cycle
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
fun:ngx_alloc
|
||||
fun:ngx_set_environment
|
||||
fun:ngx_worker_process_init
|
||||
fun:ngx_worker_process_cycle
|
||||
}
|
|
@ -141,7 +141,7 @@ end
|
|||
-- true module stuffs
|
||||
|
||||
local _M = {
|
||||
_VERSION = '0.11'
|
||||
_VERSION = '0.13'
|
||||
}
|
||||
local mt = { __index = _M }
|
||||
|
||||
|
|
|
@ -307,7 +307,7 @@ end
|
|||
--========================================================================
|
||||
|
||||
local _M = {
|
||||
_VERSION = '0.11'
|
||||
_VERSION = '0.13'
|
||||
}
|
||||
local mt = { __index = _M }
|
||||
|
||||
|
|
59
src/deps/src/lua-resty-mlcache/.github/workflows/ci.yml
vendored
Normal file
59
src/deps/src/lua-resty-mlcache/.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
pull_request:
|
||||
branches: '*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
openresty:
|
||||
description: 'OpenResty version (e.g. 1.21.4.1rc2)'
|
||||
required: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
openresty:
|
||||
- 1.21.4.1
|
||||
- 1.19.9.1
|
||||
- 1.19.3.2
|
||||
- 1.17.8.2
|
||||
- 1.15.8.3
|
||||
- 1.13.6.2
|
||||
- 1.11.2.5
|
||||
steps:
|
||||
- if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
run: echo "OPENRESTY_VER=${{ github.event.inputs.openresty }}" >> $GITHUB_ENV
|
||||
- if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
|
||||
run: echo "OPENRESTY_VER=${{ matrix.openresty }}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup OpenResty
|
||||
uses: thibaultcha/setup-openresty@main
|
||||
with:
|
||||
version: ${{ env.OPENRESTY_VER }}
|
||||
- run: prove -r t/
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
openresty: [1.19.9.1]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup OpenResty
|
||||
uses: thibaultcha/setup-openresty@main
|
||||
with:
|
||||
version: ${{ matrix.openresty }}
|
||||
- run: |
|
||||
echo "luarocks check"
|
5
src/deps/src/lua-resty-mlcache/.gitignore
vendored
Normal file
5
src/deps/src/lua-resty-mlcache/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
t/servroot*
|
||||
lua-resty-mlcache-*/
|
||||
*.tar.gz
|
||||
*.rock
|
||||
work/
|
3
src/deps/src/lua-resty-mlcache/.luacheckrc
Normal file
3
src/deps/src/lua-resty-mlcache/.luacheckrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
std = "ngx_lua"
|
||||
redefined = false
|
||||
max_line_length = 80
|
270
src/deps/src/lua-resty-mlcache/CHANGELOG.md
Normal file
270
src/deps/src/lua-resty-mlcache/CHANGELOG.md
Normal file
|
@ -0,0 +1,270 @@
|
|||
# Table of Contents
|
||||
|
||||
- [2.6.0](#2.6.0)
|
||||
- [2.5.0](#2.5.0)
|
||||
- [2.4.1](#2.4.1)
|
||||
- [2.4.0](#2.4.0)
|
||||
- [2.3.0](#2.3.0)
|
||||
- [2.2.1](#2.2.1)
|
||||
- [2.2.0](#2.2.0)
|
||||
- [2.1.0](#2.1.0)
|
||||
- [2.0.2](#2.0.2)
|
||||
- [2.0.1](#2.0.1)
|
||||
- [2.0.0](#2.0.0)
|
||||
- [1.0.1](#1.0.1)
|
||||
- [1.0.0](#1.0.0)
|
||||
|
||||
## [2.6.0]
|
||||
|
||||
> Released on: 2022/08/22
|
||||
|
||||
#### Added
|
||||
|
||||
- Use the new LuaJIT `string.buffer` API for L2 (shm layer) encoding/decoding
|
||||
when available.
|
||||
[#110](https://github.com/thibaultcha/lua-resty-mlcache/pull/110)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.5.0]
|
||||
|
||||
> Released on: 2020/11/18
|
||||
|
||||
#### Added
|
||||
|
||||
- `get()` callback functions are now optional. Without a callback, `get()` now
|
||||
still performs on-cpu L1/L2 lookups (no yielding). This allows implementing
|
||||
new cache lookup patterns guaranteed to be on-cpu for a more constant,
|
||||
smoother latency tail end (e.g. values are refreshed in background timers with
|
||||
`set()`).
|
||||
Thanks Hamish Forbes and Corina Purcarea for proposing this feature and
|
||||
participating in its development!
|
||||
[#96](https://github.com/thibaultcha/lua-resty-mlcache/pull/96)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Improve `update()` robustness to worker crashes. Now, the library behind
|
||||
`cache:update()` is much more robust to re-spawned workers when initialized in
|
||||
the `init_by_lua` phase.
|
||||
[#97](https://github.com/thibaultcha/lua-resty-mlcache/pull/97)
|
||||
- Document the `peek()` method `stale` argument which was not mentioned, as well
|
||||
as the possibility of negative TTL return values for expired items.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.4.1]
|
||||
|
||||
> Released on: 2020/01/17
|
||||
|
||||
#### Fixed
|
||||
|
||||
- The IPC module now avoids replaying all events when spawning new workers, and
|
||||
gets initialized with the latest event index instead.
|
||||
[#88](https://github.com/thibaultcha/lua-resty-mlcache/pull/88)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.4.0]
|
||||
|
||||
> Released on: 2019/03/28
|
||||
|
||||
#### Added
|
||||
|
||||
- A new `get_bulk()` API allows for fetching several values from the layered
|
||||
caches in a single call, and will execute all L3 callback functions
|
||||
concurrently, in a configurable pool of threads.
|
||||
[#77](https://github.com/thibaultcha/lua-resty-mlcache/pull/77)
|
||||
- `purge()` now clears the L1 LRU cache with the new `flush_all()` method when
|
||||
used in OpenResty >= 1.13.6.2.
|
||||
Thanks [@Crack](https://github.com/Crack) for the patch!
|
||||
[#78](https://github.com/thibaultcha/lua-resty-mlcache/pull/78)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- `get()` is now resilient to L3 callback functions calling `error()` with
|
||||
non-string arguments. Such functions could result in a runtime error when
|
||||
LuaJIT is compiled with `-DLUAJIT_ENABLE_LUA52COMPAT`.
|
||||
Thanks [@MartinAmps](https://github.com/MartinAmps) for the patch!
|
||||
[#75](https://github.com/thibaultcha/lua-resty-mlcache/pull/75)
|
||||
- Instances using a custom L1 LRU cache in OpenResty < 1.13.6.2 are now
|
||||
restricted from calling `purge()`, since doing so would result in the LRU
|
||||
cache being overwritten.
|
||||
[#79](https://github.com/thibaultcha/lua-resty-mlcache/pull/79)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.3.0]
|
||||
|
||||
> Released on: 2019/01/17
|
||||
|
||||
#### Added
|
||||
|
||||
- Returning a negative `ttl` value from the L3 callback will now make the
|
||||
fetched data bypass the cache (it will still be returned by `get()`).
|
||||
This is useful when some fetched data indicates that it is not cacheable.
|
||||
Thanks [@eaufavor](https://github.com/eaufavor) for the patch!
|
||||
[#68](https://github.com/thibaultcha/lua-resty-mlcache/pull/68)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.2.1]
|
||||
|
||||
> Released on: 2018/07/28
|
||||
|
||||
#### Fixed
|
||||
|
||||
- When `get()` returns a value from L2 (shm) during its last millisecond of
|
||||
freshness, we do not erroneously cache the value in L1 (LRU) indefinitely
|
||||
anymore. Thanks [@jdesgats](https://github.com/jdesgats) and
|
||||
[@javierguerragiraldez](https://github.com/javierguerragiraldez) for the
|
||||
report and initial fix.
|
||||
[#58](https://github.com/thibaultcha/lua-resty-mlcache/pull/58)
|
||||
- When `get()` returns a previously resurrected value from L2 (shm), we now
|
||||
correctly set the `hit_lvl` return value to `4`, instead of `2`.
|
||||
[307feca](https://github.com/thibaultcha/lua-resty-mlcache/commit/307fecad6adac8755d4fcd931bbb498da23d069c)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.2.0]
|
||||
|
||||
> Released on: 2018/06/29
|
||||
|
||||
#### Added
|
||||
|
||||
- Implement a new `resurrect_ttl` option. When specified, `get()` will behave
|
||||
in a more resilient way upon errors, and in particular callback errors.
|
||||
[#52](https://github.com/thibaultcha/lua-resty-mlcache/pull/52)
|
||||
- New `stale` argument to `peek()`. When specified, `peek()` will return stale
|
||||
shm values.
|
||||
[#52](https://github.com/thibaultcha/lua-resty-mlcache/pull/52)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.1.0]
|
||||
|
||||
> Released on: 2018/06/14
|
||||
|
||||
#### Added
|
||||
|
||||
- Implement a new `shm_locks` option. This option receives the name of a
|
||||
lua_shared_dict, and, when specified, the mlcache instance will store
|
||||
lua-resty-lock objects in it instead of storing them in the cache hits
|
||||
lua_shared_dict. This can help reducing LRU churning in some workloads.
|
||||
[#55](https://github.com/thibaultcha/lua-resty-mlcache/pull/55)
|
||||
- Provide stack traceback in `err` return value when the L3 callback throws an
|
||||
error.
|
||||
[#56](https://github.com/thibaultcha/lua-resty-mlcache/pull/56)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Ensure `no memory` errors returned by shm insertions are properly returned
|
||||
by `set()`.
|
||||
[#53](https://github.com/thibaultcha/lua-resty-mlcache/pull/53)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.0.2]
|
||||
|
||||
> Released on: 2018/04/09
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Make `get()` lookup in shm after lock timeout. This prevents a possible (but
|
||||
rare) race condition under high load. Thanks to
|
||||
[@jdesgats](https://github.com/jdesgats) for the report and initial fix.
|
||||
[#49](https://github.com/thibaultcha/lua-resty-mlcache/pull/49)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.0.1]
|
||||
|
||||
> Released on: 2018/03/27
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Ensure the `set()`, `delete()`, `peek()`, and `purge()` method properly
|
||||
support the new `shm_miss` option.
|
||||
[#45](https://github.com/thibaultcha/lua-resty-mlcache/pull/45)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [2.0.0]
|
||||
|
||||
> Released on: 2018/03/18
|
||||
|
||||
This release implements numerous new features. The major version digit has been
|
||||
bumped to ensure that the changes to the interpretation of the callback return
|
||||
values (documented below) do not break any dependent application.
|
||||
|
||||
#### Added
|
||||
|
||||
- Implement a new `purge()` method to clear all cached items in both
|
||||
the L1 and L2 caches.
|
||||
[#34](https://github.com/thibaultcha/lua-resty-mlcache/pull/34)
|
||||
- Implement a new `shm_miss` option. This option receives the name
|
||||
of a lua_shared_dict, and when specified, will cache misses there instead of
|
||||
the instance's `shm` shared dict. This is particularly useful for certain
|
||||
types of workload where a large number of misses can be triggered and
|
||||
eventually evict too many cached values (hits) from the instance's `shm`.
|
||||
[#42](https://github.com/thibaultcha/lua-resty-mlcache/pull/42)
|
||||
- Implement a new `l1_serializer` callback option. It allows the
|
||||
deserialization of data from L2 or L3 into arbitrary Lua data inside the LRU
|
||||
cache (L1). This includes userdata, cdata, functions, etc...
|
||||
Thanks to [@jdesgats](https://github.com/jdesgats) for the contribution.
|
||||
[#29](https://github.com/thibaultcha/lua-resty-mlcache/pull/29)
|
||||
- Implement a new `shm_set_tries` option to retry `shm:set()`
|
||||
operations and ensure LRU eviction when caching values of disparate sizes.
|
||||
[#41](https://github.com/thibaultcha/lua-resty-mlcache/issues/41)
|
||||
- The L3 callback can now return `nil + err`, which will be bubbled up
|
||||
to the caller of `get()`. Prior to this change, the second return value of
|
||||
callbacks was ignored, and users had to throw hard Lua errors from inside
|
||||
their callbacks.
|
||||
[#35](https://github.com/thibaultcha/lua-resty-mlcache/pull/35)
|
||||
- Support for custom IPC module.
|
||||
[#31](https://github.com/thibaultcha/lua-resty-mlcache/issues/31)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- In the event of a `no memory` error returned by the L2 lua_shared_dict cache
|
||||
(after the number of `shm_set_tries` failed), we do not interrupt the `get()`
|
||||
flow to return an error anymore. Instead, the retrieved value is now bubbled
|
||||
up for insertion in L1, and returned to the caller. A warning log is (by
|
||||
default) printed in the nginx error logs.
|
||||
[#41](https://github.com/thibaultcha/lua-resty-mlcache/issues/41)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [1.0.1]
|
||||
|
||||
> Released on: 2017/08/26
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Do not rely on memory address of mlcache instance in invalidation events
|
||||
channel names. This ensures invalidation events are properly broadcasted to
|
||||
sibling instances in other workers.
|
||||
[#27](https://github.com/thibaultcha/lua-resty-mlcache/pull/27)
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## [1.0.0]
|
||||
|
||||
> Released on: 2017/08/23
|
||||
|
||||
Initial release.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
[2.6.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.5.0...2.6.0
|
||||
[2.5.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.4.1...2.5.0
|
||||
[2.4.1]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.4.0...2.4.1
|
||||
[2.4.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.3.0...2.4.0
|
||||
[2.3.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.2.1...2.3.0
|
||||
[2.2.1]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.2.0...2.2.1
|
||||
[2.2.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.1.0...2.2.0
|
||||
[2.1.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.0.2...2.1.0
|
||||
[2.0.2]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.0.1...2.0.2
|
||||
[2.0.1]: https://github.com/thibaultcha/lua-resty-mlcache/compare/2.0.0...2.0.1
|
||||
[2.0.0]: https://github.com/thibaultcha/lua-resty-mlcache/compare/1.0.1...2.0.0
|
||||
[1.0.1]: https://github.com/thibaultcha/lua-resty-mlcache/compare/1.0.0...1.0.1
|
||||
[1.0.0]: https://github.com/thibaultcha/lua-resty-mlcache/tree/1.0.0
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue