Init commit

This commit is contained in:
Onion Limited 2020-05-26 07:36:40 +00:00
commit 770d6da100
No known key found for this signature in database
GPG key ID: E4B6CAC49B242A44
40 changed files with 6875 additions and 0 deletions

258
resty/core/base.lua Normal file
View file

@ -0,0 +1,258 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local ffi_new = ffi.new
local error = error
local select = select
local ceil = math.ceil
local subsystem = ngx.config.subsystem
local str_buf_size = 4096
local str_buf
local size_ptr
local FREE_LIST_REF = 0
if subsystem == 'http' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 10016
then
error("ngx_http_lua_module 0.10.16 required")
end
elseif subsystem == 'stream' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 8
then
error("ngx_stream_lua_module 0.0.8 required")
end
else
error("ngx_http_lua_module 0.10.16 or "
.. "ngx_stream_lua_module 0.0.8 required")
end
if string.find(jit.version, " 2.0", 1, true) then
ngx.log(ngx.ALERT, "use of lua-resty-core with LuaJIT 2.0 is ",
"not recommended; use LuaJIT 2.1+ instead")
end
local ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function (narr, nrec) return {} end
end
local clear_tab
ok, clear_tab = pcall(require, "table.clear")
if not ok then
local pairs = pairs
clear_tab = function (tab)
for k, _ in pairs(tab) do
tab[k] = nil
end
end
end
-- XXX for now LuaJIT 2.1 cannot compile require()
-- so we make the fast code path Lua only in our own
-- wrapper so that most of the require() calls in hot
-- Lua code paths can be JIT compiled.
do
local orig_require = require
local pkg_loaded = package.loaded
local function my_require(name)
local mod = pkg_loaded[name]
if mod then
return mod
end
return orig_require(name)
end
getfenv(0).require = my_require
end
if not pcall(ffi.typeof, "ngx_str_t") then
ffi.cdef[[
typedef struct {
size_t len;
const unsigned char *data;
} ngx_str_t;
]]
end
if subsystem == 'http' then
if not pcall(ffi.typeof, "ngx_http_request_t") then
ffi.cdef[[
typedef struct ngx_http_request_s ngx_http_request_t;
]]
end
if not pcall(ffi.typeof, "ngx_http_lua_ffi_str_t") then
ffi.cdef[[
typedef struct {
int len;
const unsigned char *data;
} ngx_http_lua_ffi_str_t;
]]
end
elseif subsystem == 'stream' then
if not pcall(ffi.typeof, "ngx_stream_lua_request_t") then
ffi.cdef[[
typedef struct ngx_stream_lua_request_s ngx_stream_lua_request_t;
]]
end
if not pcall(ffi.typeof, "ngx_stream_lua_ffi_str_t") then
ffi.cdef[[
typedef struct {
int len;
const unsigned char *data;
} ngx_stream_lua_ffi_str_t;
]]
end
else
error("unknown subsystem: " .. subsystem)
end
local c_buf_type = ffi.typeof("char[?]")
local _M = new_tab(0, 18)
_M.version = "0.1.17"
_M.new_tab = new_tab
_M.clear_tab = clear_tab
local errmsg
function _M.get_errmsg_ptr()
if not errmsg then
errmsg = ffi_new("char *[1]")
end
return errmsg
end
if not ngx then
error("no existing ngx. table found")
end
function _M.set_string_buf_size(size)
if size <= 0 then
return
end
if str_buf then
str_buf = nil
end
str_buf_size = ceil(size)
end
function _M.get_string_buf_size()
return str_buf_size
end
function _M.get_size_ptr()
if not size_ptr then
size_ptr = ffi_new("size_t[1]")
end
return size_ptr
end
function _M.get_string_buf(size, must_alloc)
-- ngx.log(ngx.ERR, "str buf size: ", str_buf_size)
if size > str_buf_size or must_alloc then
return ffi_new(c_buf_type, size)
end
if not str_buf then
str_buf = ffi_new(c_buf_type, str_buf_size)
end
return str_buf
end
function _M.ref_in_table(tb, key)
if key == nil then
return -1
end
local ref = tb[FREE_LIST_REF]
if ref and ref ~= 0 then
tb[FREE_LIST_REF] = tb[ref]
else
ref = #tb + 1
end
tb[ref] = key
-- print("ref key_id returned ", ref)
return ref
end
function _M.allows_subsystem(...)
local total = select("#", ...)
for i = 1, total do
if select(i, ...) == subsystem then
return
end
end
error("unsupported subsystem: " .. subsystem, 2)
end
_M.FFI_OK = 0
_M.FFI_NO_REQ_CTX = -100
_M.FFI_BAD_CONTEXT = -101
_M.FFI_ERROR = -1
_M.FFI_AGAIN = -2
_M.FFI_BUSY = -3
_M.FFI_DONE = -4
_M.FFI_DECLINED = -5
do
local exdata
ok, exdata = pcall(require, "thread.exdata")
if ok and exdata then
function _M.get_request()
local r = exdata()
if r ~= nil then
return r
end
end
else
local getfenv = getfenv
function _M.get_request()
return getfenv(0).__ngx_req
end
end
end
return _M

115
resty/core/base64.lua Normal file
View file

@ -0,0 +1,115 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local type = type
local error = error
local floor = math.floor
local tostring = tostring
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_encode_base64
local ngx_lua_ffi_decode_base64
if subsystem == "http" then
ffi.cdef[[
size_t ngx_http_lua_ffi_encode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
int no_padding);
int ngx_http_lua_ffi_decode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
size_t *dlen);
]]
ngx_lua_ffi_encode_base64 = C.ngx_http_lua_ffi_encode_base64
ngx_lua_ffi_decode_base64 = C.ngx_http_lua_ffi_decode_base64
elseif subsystem == "stream" then
ffi.cdef[[
size_t ngx_stream_lua_ffi_encode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
int no_padding);
int ngx_stream_lua_ffi_decode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
size_t *dlen);
]]
ngx_lua_ffi_encode_base64 = C.ngx_stream_lua_ffi_encode_base64
ngx_lua_ffi_decode_base64 = C.ngx_stream_lua_ffi_decode_base64
end
local function base64_encoded_length(len, no_padding)
return no_padding and floor((len * 8 + 5) / 6) or
floor((len + 2) / 3) * 4
end
ngx.encode_base64 = function (s, no_padding)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local no_padding_bool = false;
local no_padding_int = 0;
if no_padding then
if no_padding ~= true then
local typ = type(no_padding)
error("bad no_padding: boolean expected, got " .. typ, 2)
end
no_padding_bool = true
no_padding_int = 1;
end
local dlen = base64_encoded_length(slen, no_padding_bool)
local dst = get_string_buf(dlen)
local r_dlen = ngx_lua_ffi_encode_base64(s, slen, dst, no_padding_int)
-- if dlen ~= r_dlen then error("discrepancy in len") end
return ffi_string(dst, r_dlen)
end
local function base64_decoded_length(len)
return floor((len + 3) / 4) * 3
end
ngx.decode_base64 = function (s)
if type(s) ~= 'string' then
error("string argument only", 2)
end
local slen = #s
local dlen = base64_decoded_length(slen)
-- print("dlen: ", tonumber(dlen))
local dst = get_string_buf(dlen)
local pdlen = get_size_ptr()
local ok = ngx_lua_ffi_decode_base64(s, slen, dst, pdlen)
if ok == 0 then
return nil
end
return ffi_string(dst, pdlen[0])
end
return {
version = base.version
}

101
resty/core/ctx.lua Normal file
View file

@ -0,0 +1,101 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local debug = require "debug"
local base = require "resty.core.base"
local misc = require "resty.core.misc"
local C = ffi.C
local register_getter = misc.register_ngx_magic_key_getter
local register_setter = misc.register_ngx_magic_key_setter
local registry = debug.getregistry()
local new_tab = base.new_tab
local ref_in_table = base.ref_in_table
local get_request = base.get_request
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_OK = base.FFI_OK
local error = error
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_get_ctx_ref
local ngx_lua_ffi_set_ctx_ref
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r);
int ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref);
]]
ngx_lua_ffi_get_ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref
ngx_lua_ffi_set_ctx_ref = C.ngx_http_lua_ffi_set_ctx_ref
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_get_ctx_ref(ngx_stream_lua_request_t *r);
int ngx_stream_lua_ffi_set_ctx_ref(ngx_stream_lua_request_t *r, int ref);
]]
ngx_lua_ffi_get_ctx_ref = C.ngx_stream_lua_ffi_get_ctx_ref
ngx_lua_ffi_set_ctx_ref = C.ngx_stream_lua_ffi_set_ctx_ref
end
local _M = {
_VERSION = base.version
}
local function get_ctx_table()
local r = get_request()
if not r then
error("no request found")
end
local ctx_ref = ngx_lua_ffi_get_ctx_ref(r)
if ctx_ref == FFI_NO_REQ_CTX then
error("no request ctx found")
end
local ctxs = registry.ngx_lua_ctx_tables
if ctx_ref < 0 then
local ctx = new_tab(0, 4)
ctx_ref = ref_in_table(ctxs, ctx)
if ngx_lua_ffi_set_ctx_ref(r, ctx_ref) ~= FFI_OK then
return nil
end
return ctx
end
return ctxs[ctx_ref]
end
register_getter("ctx", get_ctx_table)
local function set_ctx_table(ctx)
local r = get_request()
if not r then
error("no request found")
end
local ctx_ref = ngx_lua_ffi_get_ctx_ref(r)
if ctx_ref == FFI_NO_REQ_CTX then
error("no request ctx found")
end
local ctxs = registry.ngx_lua_ctx_tables
if ctx_ref < 0 then
ctx_ref = ref_in_table(ctxs, ctx)
ngx_lua_ffi_set_ctx_ref(r, ctx_ref)
return
end
ctxs[ctx_ref] = ctx
end
register_setter("ctx", set_ctx_table)
return _M

66
resty/core/exit.lua Normal file
View file

@ -0,0 +1,66 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local error = error
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local get_request = base.get_request
local co_yield = coroutine._yield
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_exit
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status,
unsigned char *err, size_t *errlen);
]]
ngx_lua_ffi_exit = C.ngx_http_lua_ffi_exit
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status,
unsigned char *err, size_t *errlen);
]]
ngx_lua_ffi_exit = C.ngx_stream_lua_ffi_exit
end
local ERR_BUF_SIZE = 128
local FFI_DONE = base.FFI_DONE
ngx.exit = function (rc)
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
local r = get_request()
if r == nil then
error("no request found")
end
errlen[0] = ERR_BUF_SIZE
rc = ngx_lua_ffi_exit(r, rc, err, errlen)
if rc == 0 then
-- print("yielding...")
return co_yield()
end
if rc == FFI_DONE then
return
end
error(ffi_string(err, errlen[0]), 2)
end
return {
version = base.version
}

110
resty/core/hash.lua Normal file
View file

@ -0,0 +1,110 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_new = ffi.new
local ffi_string = ffi.string
local ngx = ngx
local type = type
local error = error
local tostring = tostring
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_md5
local ngx_lua_ffi_md5_bin
local ngx_lua_ffi_sha1_bin
if subsystem == "http" then
ffi.cdef[[
void ngx_http_lua_ffi_md5_bin(const unsigned char *src, size_t len,
unsigned char *dst);
void ngx_http_lua_ffi_md5(const unsigned char *src, size_t len,
unsigned char *dst);
int ngx_http_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
unsigned char *dst);
]]
ngx_lua_ffi_md5 = C.ngx_http_lua_ffi_md5
ngx_lua_ffi_md5_bin = C.ngx_http_lua_ffi_md5_bin
ngx_lua_ffi_sha1_bin = C.ngx_http_lua_ffi_sha1_bin
elseif subsystem == "stream" then
ffi.cdef[[
void ngx_stream_lua_ffi_md5_bin(const unsigned char *src, size_t len,
unsigned char *dst);
void ngx_stream_lua_ffi_md5(const unsigned char *src, size_t len,
unsigned char *dst);
int ngx_stream_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
unsigned char *dst);
]]
ngx_lua_ffi_md5 = C.ngx_stream_lua_ffi_md5
ngx_lua_ffi_md5_bin = C.ngx_stream_lua_ffi_md5_bin
ngx_lua_ffi_sha1_bin = C.ngx_stream_lua_ffi_sha1_bin
end
local MD5_DIGEST_LEN = 16
local md5_buf = ffi_new("unsigned char[?]", MD5_DIGEST_LEN)
ngx.md5_bin = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
ngx_lua_ffi_md5_bin(s, #s, md5_buf)
return ffi_string(md5_buf, MD5_DIGEST_LEN)
end
local MD5_HEX_DIGEST_LEN = MD5_DIGEST_LEN * 2
local md5_hex_buf = ffi_new("unsigned char[?]", MD5_HEX_DIGEST_LEN)
ngx.md5 = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
ngx_lua_ffi_md5(s, #s, md5_hex_buf)
return ffi_string(md5_hex_buf, MD5_HEX_DIGEST_LEN)
end
local SHA_DIGEST_LEN = 20
local sha_buf = ffi_new("unsigned char[?]", SHA_DIGEST_LEN)
ngx.sha1_bin = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local ok = ngx_lua_ffi_sha1_bin(s, #s, sha_buf)
if ok == 0 then
error("SHA-1 support missing in Nginx")
end
return ffi_string(sha_buf, SHA_DIGEST_LEN)
end
return {
version = base.version
}

240
resty/core/misc.lua Normal file
View file

@ -0,0 +1,240 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
local ffi = require "ffi"
local os = require "os"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ngx = ngx
local type = type
local error = error
local rawget = rawget
local rawset = rawset
local tonumber = tonumber
local setmetatable = setmetatable
local FFI_OK = base.FFI_OK
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local new_tab = base.new_tab
local get_request = base.get_request
local get_size_ptr = base.get_size_ptr
local get_string_buf = base.get_string_buf
local get_string_buf_size = base.get_string_buf_size
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_get_resp_status
local ngx_lua_ffi_get_conf_env
local ngx_magic_key_getters
local ngx_magic_key_setters
local _M = new_tab(0, 3)
local ngx_mt = new_tab(0, 2)
if subsystem == "http" then
ngx_magic_key_getters = new_tab(0, 4)
ngx_magic_key_setters = new_tab(0, 2)
elseif subsystem == "stream" then
ngx_magic_key_getters = new_tab(0, 2)
ngx_magic_key_setters = new_tab(0, 1)
end
local function register_getter(key, func)
ngx_magic_key_getters[key] = func
end
_M.register_ngx_magic_key_getter = register_getter
local function register_setter(key, func)
ngx_magic_key_setters[key] = func
end
_M.register_ngx_magic_key_setter = register_setter
ngx_mt.__index = function (tb, key)
local f = ngx_magic_key_getters[key]
if f then
return f()
end
return rawget(tb, key)
end
ngx_mt.__newindex = function (tb, key, ctx)
local f = ngx_magic_key_setters[key]
if f then
return f(ctx)
end
return rawset(tb, key, ctx)
end
setmetatable(ngx, ngx_mt)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r);
int ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int r);
int ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r);
int ngx_http_lua_ffi_headers_sent(ngx_http_request_t *r);
int ngx_http_lua_ffi_get_conf_env(const unsigned char *name,
unsigned char **env_buf,
size_t *name_len);
]]
ngx_lua_ffi_get_resp_status = C.ngx_http_lua_ffi_get_resp_status
ngx_lua_ffi_get_conf_env = C.ngx_http_lua_ffi_get_conf_env
-- ngx.status
local function set_status(status)
local r = get_request()
if not r then
error("no request found")
end
if type(status) ~= 'number' then
status = tonumber(status)
end
local rc = C.ngx_http_lua_ffi_set_resp_status(r, status)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return
end
register_setter("status", set_status)
-- ngx.is_subrequest
local function is_subreq()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_is_subrequest(r)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc == 1
end
register_getter("is_subrequest", is_subreq)
-- ngx.headers_sent
local function headers_sent()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_headers_sent(r)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc == 1
end
register_getter("headers_sent", headers_sent)
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_get_resp_status(ngx_stream_lua_request_t *r);
int ngx_stream_lua_ffi_get_conf_env(const unsigned char *name,
unsigned char **env_buf,
size_t *name_len);
]]
ngx_lua_ffi_get_resp_status = C.ngx_stream_lua_ffi_get_resp_status
ngx_lua_ffi_get_conf_env = C.ngx_stream_lua_ffi_get_conf_env
end
-- ngx.status
local function get_status()
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_get_resp_status(r)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc
end
register_getter("status", get_status)
do
local _getenv = os.getenv
local env_ptr = ffi_new("unsigned char *[1]")
os.getenv = function (name)
local r = get_request()
if r then
-- past init_by_lua* phase now
os.getenv = _getenv
env_ptr = nil
return os.getenv(name)
end
local size = get_string_buf_size()
env_ptr[0] = get_string_buf(size)
local name_len_ptr = get_size_ptr()
local rc = ngx_lua_ffi_get_conf_env(name, env_ptr, name_len_ptr)
if rc == FFI_OK then
return ffi_str(env_ptr[0] + name_len_ptr[0] + 1)
end
-- FFI_DECLINED
local value = _getenv(name)
if value ~= nil then
return value
end
return nil
end
end
_M._VERSION = base.version
return _M

92
resty/core/ndk.lua Normal file
View file

@ -0,0 +1,92 @@
-- Copyright (C) by OpenResty Inc.
local ffi = require 'ffi'
local base = require "resty.core.base"
base.allows_subsystem('http')
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_new = ffi.new
local ffi_str = ffi.string
local FFI_OK = base.FFI_OK
local new_tab = base.new_tab
local get_string_buf = base.get_string_buf
local get_request = base.get_request
local setmetatable = setmetatable
local type = type
local tostring = tostring
local error = error
local _M = {
version = base.version
}
ffi.cdef[[
typedef void * ndk_set_var_value_pt;
int ngx_http_lua_ffi_ndk_lookup_directive(const unsigned char *var_data,
size_t var_len, ndk_set_var_value_pt *func);
int ngx_http_lua_ffi_ndk_set_var_get(ngx_http_request_t *r,
ndk_set_var_value_pt func, const unsigned char *arg_data, size_t arg_len,
ngx_http_lua_ffi_str_t *value);
]]
local func_p = ffi_new("void*[1]")
local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
local function ndk_set_var_get(self, var)
if type(var) ~= "string" then
var = tostring(var)
end
if C.ngx_http_lua_ffi_ndk_lookup_directive(var, #var, func_p) ~= FFI_OK then
error('ndk.set_var: directive "' .. var
.. '" not found or does not use ndk_set_var_value', 2)
end
local func = func_p[0]
return function (arg)
local r = get_request()
if not r then
error("no request found")
end
if type(arg) ~= "string" then
arg = tostring(arg)
end
local buf = get_string_buf(ffi_str_size)
local value = ffi_cast(ffi_str_type, buf)
local rc = C.ngx_http_lua_ffi_ndk_set_var_get(r, func, arg, #arg, value)
if rc ~= FFI_OK then
error("calling directive " .. var .. " failed with code " .. rc, 2)
end
return ffi_str(value.data, value.len)
end
end
local function ndk_set_var_set()
error("not allowed", 2)
end
if ndk then
local mt = new_tab(0, 2)
mt.__newindex = ndk_set_var_set
mt.__index = ndk_set_var_get
ndk.set_var = setmetatable(new_tab(0, 0), mt)
end
return _M

58
resty/core/phase.lua Normal file
View file

@ -0,0 +1,58 @@
local ffi = require 'ffi'
local base = require "resty.core.base"
local C = ffi.C
local FFI_ERROR = base.FFI_ERROR
local get_request = base.get_request
local error = error
local tostring = tostring
ffi.cdef[[
int ngx_http_lua_ffi_get_phase(ngx_http_request_t *r, char **err)
]]
local errmsg = base.get_errmsg_ptr()
local context_names = {
[0x0001] = "set",
[0x0002] = "rewrite",
[0x0004] = "access",
[0x0008] = "content",
[0x0010] = "log",
[0x0020] = "header_filter",
[0x0040] = "body_filter",
[0x0080] = "timer",
[0x0100] = "init_worker",
[0x0200] = "balancer",
[0x0400] = "ssl_cert",
[0x0800] = "ssl_session_store",
[0x1000] = "ssl_session_fetch",
}
function ngx.get_phase()
local r = get_request()
-- if we have no request object, assume we are called from the "init" phase
if not r then
return "init"
end
local context = C.ngx_http_lua_ffi_get_phase(r, errmsg)
if context == FFI_ERROR then -- NGX_ERROR
error(errmsg, 2)
end
local phase = context_names[context]
if not phase then
error("unknown phase: " .. tostring(context))
end
return phase
end
return {
version = base.version
}

1195
resty/core/regex.lua Normal file

File diff suppressed because it is too large Load diff

367
resty/core/request.lua Normal file
View file

@ -0,0 +1,367 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
base.allows_subsystem("http")
local utils = require "resty.core.utils"
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local FFI_DECLINED = base.FFI_DECLINED
local FFI_OK = base.FFI_OK
local new_tab = base.new_tab
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_new = ffi.new
local ffi_str = ffi.string
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local setmetatable = setmetatable
local lower = string.lower
local rawget = rawget
local ngx = ngx
local get_request = base.get_request
local type = type
local error = error
local tostring = tostring
local tonumber = tonumber
local str_replace_char = utils.str_replace_char
ffi.cdef[[
typedef struct {
ngx_http_lua_ffi_str_t key;
ngx_http_lua_ffi_str_t value;
} ngx_http_lua_ffi_table_elt_t;
int ngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r,
int max, int *truncated);
int ngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r,
ngx_http_lua_ffi_table_elt_t *out, int count, int raw);
int ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r,
int max, int *truncated);
size_t ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r,
unsigned char *buf, ngx_http_lua_ffi_table_elt_t *out, int count);
double ngx_http_lua_ffi_req_start_time(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_method(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_method_name(ngx_http_request_t *r,
unsigned char **name, size_t *len);
int ngx_http_lua_ffi_req_set_method(ngx_http_request_t *r, int method);
int ngx_http_lua_ffi_req_header_set_single_value(ngx_http_request_t *r,
const unsigned char *key, size_t key_len, const unsigned char *value,
size_t value_len);
]]
local table_elt_type = ffi.typeof("ngx_http_lua_ffi_table_elt_t*")
local table_elt_size = ffi.sizeof("ngx_http_lua_ffi_table_elt_t")
local truncated = ffi.new("int[1]")
local req_headers_mt = {
__index = function (tb, key)
return rawget(tb, (str_replace_char(lower(key), '_', '-')))
end
}
function ngx.req.get_headers(max_headers, raw)
local r = get_request()
if not r then
error("no request found")
end
if not max_headers then
max_headers = -1
end
if not raw then
raw = 0
else
raw = 1
end
local n = C.ngx_http_lua_ffi_req_get_headers_count(r, max_headers,
truncated)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return {}
end
local raw_buf = get_string_buf(n * table_elt_size)
local buf = ffi_cast(table_elt_type, raw_buf)
local rc = C.ngx_http_lua_ffi_req_get_headers(r, buf, n, raw)
if rc == 0 then
local headers = new_tab(0, n)
for i = 0, n - 1 do
local h = buf[i]
local key = h.key
key = ffi_str(key.data, key.len)
local value = h.value
value = ffi_str(value.data, value.len)
local existing = headers[key]
if existing then
if type(existing) == "table" then
existing[#existing + 1] = value
else
headers[key] = {existing, value}
end
else
headers[key] = value
end
end
if raw == 0 then
headers = setmetatable(headers, req_headers_mt)
end
if truncated[0] ~= 0 then
return headers, "truncated"
end
return headers
end
return nil
end
function ngx.req.get_uri_args(max_args)
local r = get_request()
if not r then
error("no request found")
end
if not max_args then
max_args = -1
end
local n = C.ngx_http_lua_ffi_req_get_uri_args_count(r, max_args, truncated)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return {}
end
local args_len = C.ngx_http_lua_ffi_req_get_querystring_len(r)
local strbuf = get_string_buf(args_len + n * table_elt_size)
local kvbuf = ffi_cast(table_elt_type, strbuf + args_len)
local nargs = C.ngx_http_lua_ffi_req_get_uri_args(r, strbuf, kvbuf, n)
local args = new_tab(0, nargs)
for i = 0, nargs - 1 do
local arg = kvbuf[i]
local key = arg.key
key = ffi_str(key.data, key.len)
local value = arg.value
local len = value.len
if len == -1 then
value = true
else
value = ffi_str(value.data, len)
end
local existing = args[key]
if existing then
if type(existing) == "table" then
existing[#existing + 1] = value
else
args[key] = {existing, value}
end
else
args[key] = value
end
end
if truncated[0] ~= 0 then
return args, "truncated"
end
return args
end
function ngx.req.start_time()
local r = get_request()
if not r then
error("no request found")
end
return tonumber(C.ngx_http_lua_ffi_req_start_time(r))
end
do
local methods = {
[0x0002] = "GET",
[0x0004] = "HEAD",
[0x0008] = "POST",
[0x0010] = "PUT",
[0x0020] = "DELETE",
[0x0040] = "MKCOL",
[0x0080] = "COPY",
[0x0100] = "MOVE",
[0x0200] = "OPTIONS",
[0x0400] = "PROPFIND",
[0x0800] = "PROPPATCH",
[0x1000] = "LOCK",
[0x2000] = "UNLOCK",
[0x4000] = "PATCH",
[0x8000] = "TRACE",
}
local namep = ffi_new("unsigned char *[1]")
function ngx.req.get_method()
local r = get_request()
if not r then
error("no request found")
end
do
local id = C.ngx_http_lua_ffi_req_get_method(r)
if id == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
local method = methods[id]
if method then
return method
end
end
local sizep = get_size_ptr()
local rc = C.ngx_http_lua_ffi_req_get_method_name(r, namep, sizep)
if rc ~= 0 then
return nil
end
return ffi_str(namep[0], sizep[0])
end
end -- do
function ngx.req.set_method(method)
local r = get_request()
if not r then
error("no request found")
end
if type(method) ~= "number" then
error("bad method number", 2)
end
local rc = C.ngx_http_lua_ffi_req_set_method(r, method)
if rc == FFI_OK then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if rc == FFI_DECLINED then
error("unsupported HTTP method: " .. method, 2)
end
error("unknown error: " .. rc)
end
do
local orig_func = ngx.req.set_header
function ngx.req.set_header(name, value)
if type(value) == "table" then
return orig_func(name, value)
end
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
name = tostring(name)
end
local rc
if not value then
rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name,
#name, nil, 0)
else
if type(value) ~= "string" then
value = tostring(value)
end
rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name,
#name, value, #value)
end
if rc == FFI_OK or rc == FFI_DECLINED then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
error("error")
end
end -- do
function ngx.req.clear_header(name)
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
name = tostring(name)
end
local rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name, #name,
nil, 0)
if rc == FFI_OK or rc == FFI_DECLINED then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
error("error")
end
return {
version = base.version
}

183
resty/core/response.lua Normal file
View file

@ -0,0 +1,183 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_str = ffi.string
local new_tab = base.new_tab
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_DECLINED = base.FFI_DECLINED
local get_string_buf = base.get_string_buf
local setmetatable = setmetatable
local type = type
local tostring = tostring
local get_request = base.get_request
local error = error
local ngx = ngx
local _M = {
version = base.version
}
local MAX_HEADER_VALUES = 100
local errmsg = base.get_errmsg_ptr()
local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
ffi.cdef[[
int ngx_http_lua_ffi_set_resp_header(ngx_http_request_t *r,
const char *key_data, size_t key_len, int is_nil,
const char *sval, size_t sval_len, ngx_http_lua_ffi_str_t *mvals,
size_t mvals_len, int override, char **errmsg);
int ngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r,
const unsigned char *key, size_t key_len,
unsigned char *key_buf, ngx_http_lua_ffi_str_t *values,
int max_nvalues, char **errmsg);
]]
local function set_resp_header(tb, key, value, no_override)
local r = get_request()
if not r then
error("no request found")
end
if type(key) ~= "string" then
key = tostring(key)
end
local rc
if value == nil then
if no_override then
error("invalid header value", 3)
end
rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, true, nil, 0, nil,
0, 1, errmsg)
else
local sval, sval_len, mvals, mvals_len, buf
if type(value) == "table" then
mvals_len = #value
if mvals_len == 0 and no_override then
return
end
buf = get_string_buf(ffi_str_size * mvals_len)
mvals = ffi_cast(ffi_str_type, buf)
for i = 1, mvals_len do
local s = value[i]
if type(s) ~= "string" then
s = tostring(s)
value[i] = s
end
local str = mvals[i - 1]
str.data = s
str.len = #s
end
sval_len = 0
else
if type(value) ~= "string" then
sval = tostring(value)
else
sval = value
end
sval_len = #sval
mvals_len = 0
end
local override_int = no_override and 0 or 1
rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, false, sval,
sval_len, mvals, mvals_len,
override_int, errmsg)
end
if rc == 0 or rc == FFI_DECLINED then
return
end
if rc == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
-- rc == FFI_ERROR
error(ffi_str(errmsg[0]), 2)
end
_M.set_resp_header = set_resp_header
local function get_resp_header(tb, key)
local r = get_request()
if not r then
error("no request found")
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
local key_buf = get_string_buf(key_len + ffi_str_size * MAX_HEADER_VALUES)
local values = ffi_cast(ffi_str_type, key_buf + key_len)
local n = C.ngx_http_lua_ffi_get_resp_header(r, key, key_len, key_buf,
values, MAX_HEADER_VALUES,
errmsg)
-- print("retval: ", n)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return nil
end
if n == 1 then
local v = values[0]
return ffi_str(v.data, v.len)
end
if n > 0 then
local ret = new_tab(n, 0)
for i = 1, n do
local v = values[i - 1]
ret[i] = ffi_str(v.data, v.len)
end
return ret
end
-- n == FFI_ERROR
error(ffi_str(errmsg[0]), 2)
end
do
local mt = new_tab(0, 2)
mt.__newindex = set_resp_header
mt.__index = get_resp_header
ngx.header = setmetatable(new_tab(0, 0), mt)
end
return _M

638
resty/core/shdict.lua Normal file
View file

@ -0,0 +1,638 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local _M = {
version = base.version
}
local ngx_shared = ngx.shared
if not ngx_shared then
return _M
end
local ffi_new = ffi.new
local ffi_str = ffi.string
local C = ffi.C
local get_string_buf = base.get_string_buf
local get_string_buf_size = base.get_string_buf_size
local get_size_ptr = base.get_size_ptr
local tonumber = tonumber
local tostring = tostring
local next = next
local type = type
local error = error
local getmetatable = getmetatable
local FFI_DECLINED = base.FFI_DECLINED
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_shdict_get
local ngx_lua_ffi_shdict_incr
local ngx_lua_ffi_shdict_store
local ngx_lua_ffi_shdict_flush_all
local ngx_lua_ffi_shdict_get_ttl
local ngx_lua_ffi_shdict_set_expire
local ngx_lua_ffi_shdict_capacity
local ngx_lua_ffi_shdict_free_space
local ngx_lua_ffi_shdict_udata_to_zone
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_shdict_get(void *zone, const unsigned char *key,
size_t key_len, int *value_type, unsigned char **str_value_buf,
size_t *str_value_len, double *num_value, int *user_flags,
int get_stale, int *is_stale, char **errmsg);
int ngx_http_lua_ffi_shdict_incr(void *zone, const unsigned char *key,
size_t key_len, double *value, char **err, int has_init,
double init, long init_ttl, int *forcible);
int ngx_http_lua_ffi_shdict_store(void *zone, int op,
const unsigned char *key, size_t key_len, int value_type,
const unsigned char *str_value_buf, size_t str_value_len,
double num_value, long exptime, int user_flags, char **errmsg,
int *forcible);
int ngx_http_lua_ffi_shdict_flush_all(void *zone);
long ngx_http_lua_ffi_shdict_get_ttl(void *zone,
const unsigned char *key, size_t key_len);
int ngx_http_lua_ffi_shdict_set_expire(void *zone,
const unsigned char *key, size_t key_len, long exptime);
size_t ngx_http_lua_ffi_shdict_capacity(void *zone);
void *ngx_http_lua_ffi_shdict_udata_to_zone(void *zone_udata);
]]
ngx_lua_ffi_shdict_get = C.ngx_http_lua_ffi_shdict_get
ngx_lua_ffi_shdict_incr = C.ngx_http_lua_ffi_shdict_incr
ngx_lua_ffi_shdict_store = C.ngx_http_lua_ffi_shdict_store
ngx_lua_ffi_shdict_flush_all = C.ngx_http_lua_ffi_shdict_flush_all
ngx_lua_ffi_shdict_get_ttl = C.ngx_http_lua_ffi_shdict_get_ttl
ngx_lua_ffi_shdict_set_expire = C.ngx_http_lua_ffi_shdict_set_expire
ngx_lua_ffi_shdict_capacity = C.ngx_http_lua_ffi_shdict_capacity
ngx_lua_ffi_shdict_udata_to_zone =
C.ngx_http_lua_ffi_shdict_udata_to_zone
if not pcall(function ()
return C.ngx_http_lua_ffi_shdict_free_space
end)
then
ffi.cdef[[
size_t ngx_http_lua_ffi_shdict_free_space(void *zone);
]]
end
pcall(function ()
ngx_lua_ffi_shdict_free_space = C.ngx_http_lua_ffi_shdict_free_space
end)
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_shdict_get(void *zone, const unsigned char *key,
size_t key_len, int *value_type, unsigned char **str_value_buf,
size_t *str_value_len, double *num_value, int *user_flags,
int get_stale, int *is_stale, char **errmsg);
int ngx_stream_lua_ffi_shdict_incr(void *zone, const unsigned char *key,
size_t key_len, double *value, char **err, int has_init,
double init, long init_ttl, int *forcible);
int ngx_stream_lua_ffi_shdict_store(void *zone, int op,
const unsigned char *key, size_t key_len, int value_type,
const unsigned char *str_value_buf, size_t str_value_len,
double num_value, long exptime, int user_flags, char **errmsg,
int *forcible);
int ngx_stream_lua_ffi_shdict_flush_all(void *zone);
long ngx_stream_lua_ffi_shdict_get_ttl(void *zone,
const unsigned char *key, size_t key_len);
int ngx_stream_lua_ffi_shdict_set_expire(void *zone,
const unsigned char *key, size_t key_len, long exptime);
size_t ngx_stream_lua_ffi_shdict_capacity(void *zone);
void *ngx_stream_lua_ffi_shdict_udata_to_zone(void *zone_udata);
]]
ngx_lua_ffi_shdict_get = C.ngx_stream_lua_ffi_shdict_get
ngx_lua_ffi_shdict_incr = C.ngx_stream_lua_ffi_shdict_incr
ngx_lua_ffi_shdict_store = C.ngx_stream_lua_ffi_shdict_store
ngx_lua_ffi_shdict_flush_all = C.ngx_stream_lua_ffi_shdict_flush_all
ngx_lua_ffi_shdict_get_ttl = C.ngx_stream_lua_ffi_shdict_get_ttl
ngx_lua_ffi_shdict_set_expire = C.ngx_stream_lua_ffi_shdict_set_expire
ngx_lua_ffi_shdict_capacity = C.ngx_stream_lua_ffi_shdict_capacity
ngx_lua_ffi_shdict_udata_to_zone =
C.ngx_stream_lua_ffi_shdict_udata_to_zone
if not pcall(function ()
return C.ngx_stream_lua_ffi_shdict_free_space
end)
then
ffi.cdef[[
size_t ngx_stream_lua_ffi_shdict_free_space(void *zone);
]]
end
-- ngx_stream_lua is only compatible with NGINX >= 1.13.6, meaning it
-- cannot lack support for ngx_stream_lua_ffi_shdict_free_space.
ngx_lua_ffi_shdict_free_space = C.ngx_stream_lua_ffi_shdict_free_space
else
error("unknown subsystem: " .. subsystem)
end
if not pcall(function () return C.free end) then
ffi.cdef[[
void free(void *ptr);
]]
end
local value_type = ffi_new("int[1]")
local user_flags = ffi_new("int[1]")
local num_value = ffi_new("double[1]")
local is_stale = ffi_new("int[1]")
local forcible = ffi_new("int[1]")
local str_value_buf = ffi_new("unsigned char *[1]")
local errmsg = base.get_errmsg_ptr()
local function check_zone(zone)
if not zone or type(zone) ~= "table" then
error("bad \"zone\" argument", 3)
end
zone = zone[1]
if type(zone) ~= "userdata" then
error("bad \"zone\" argument", 3)
end
zone = ngx_lua_ffi_shdict_udata_to_zone(zone)
if zone == nil then
error("bad \"zone\" argument", 3)
end
return zone
end
local function shdict_store(zone, op, key, value, exptime, flags)
zone = check_zone(zone)
if not exptime then
exptime = 0
elseif exptime < 0 then
error('bad "exptime" argument', 2)
end
if not flags then
flags = 0
end
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local str_val_buf
local str_val_len = 0
local num_val = 0
local valtyp = type(value)
-- print("value type: ", valtyp)
-- print("exptime: ", exptime)
if valtyp == "string" then
valtyp = 4 -- LUA_TSTRING
str_val_buf = value
str_val_len = #value
elseif valtyp == "number" then
valtyp = 3 -- LUA_TNUMBER
num_val = value
elseif value == nil then
valtyp = 0 -- LUA_TNIL
elseif valtyp == "boolean" then
valtyp = 1 -- LUA_TBOOLEAN
num_val = value and 1 or 0
else
return nil, "bad value type"
end
local rc = ngx_lua_ffi_shdict_store(zone, op, key, key_len,
valtyp, str_val_buf,
str_val_len, num_val,
exptime * 1000, flags, errmsg,
forcible)
-- print("rc == ", rc)
if rc == 0 then -- NGX_OK
return true, nil, forcible[0] == 1
end
-- NGX_DECLINED or NGX_ERROR
return false, ffi_str(errmsg[0]), forcible[0] == 1
end
local function shdict_set(zone, key, value, exptime, flags)
return shdict_store(zone, 0, key, value, exptime, flags)
end
local function shdict_safe_set(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0004, key, value, exptime, flags)
end
local function shdict_add(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0001, key, value, exptime, flags)
end
local function shdict_safe_add(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0005, key, value, exptime, flags)
end
local function shdict_replace(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0002, key, value, exptime, flags)
end
local function shdict_delete(zone, key)
return shdict_set(zone, key, nil)
end
local function shdict_get(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local size = get_string_buf_size()
local buf = get_string_buf(size)
str_value_buf[0] = buf
local value_len = get_size_ptr()
value_len[0] = size
local rc = ngx_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags, 0,
is_stale, errmsg)
if rc ~= 0 then
if errmsg[0] then
return nil, ffi_str(errmsg[0])
end
error("failed to get the key")
end
local typ = value_type[0]
if typ == 0 then -- LUA_TNIL
return nil
end
local flags = tonumber(user_flags[0])
local val
if typ == 4 then -- LUA_TSTRING
if str_value_buf[0] ~= buf then
-- ngx.say("len: ", tonumber(value_len[0]))
buf = str_value_buf[0]
val = ffi_str(buf, value_len[0])
C.free(buf)
else
val = ffi_str(buf, value_len[0])
end
elseif typ == 3 then -- LUA_TNUMBER
val = tonumber(num_value[0])
elseif typ == 1 then -- LUA_TBOOLEAN
val = (tonumber(buf[0]) ~= 0)
else
error("unknown value type: " .. typ)
end
if flags ~= 0 then
return val, flags
end
return val
end
local function shdict_get_stale(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local size = get_string_buf_size()
local buf = get_string_buf(size)
str_value_buf[0] = buf
local value_len = get_size_ptr()
value_len[0] = size
local rc = ngx_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags, 1,
is_stale, errmsg)
if rc ~= 0 then
if errmsg[0] then
return nil, ffi_str(errmsg[0])
end
error("failed to get the key")
end
local typ = value_type[0]
if typ == 0 then -- LUA_TNIL
return nil
end
local flags = tonumber(user_flags[0])
local val
if typ == 4 then -- LUA_TSTRING
if str_value_buf[0] ~= buf then
-- ngx.say("len: ", tonumber(value_len[0]))
buf = str_value_buf[0]
val = ffi_str(buf, value_len[0])
C.free(buf)
else
val = ffi_str(buf, value_len[0])
end
elseif typ == 3 then -- LUA_TNUMBER
val = tonumber(num_value[0])
elseif typ == 1 then -- LUA_TBOOLEAN
val = (tonumber(buf[0]) ~= 0)
else
error("unknown value type: " .. typ)
end
if flags ~= 0 then
return val, flags, is_stale[0] == 1
end
return val, nil, is_stale[0] == 1
end
local function shdict_incr(zone, key, value, init, init_ttl)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
if type(value) ~= "number" then
value = tonumber(value)
end
num_value[0] = value
if init then
local typ = type(init)
if typ ~= "number" then
init = tonumber(init)
if not init then
error("bad init arg: number expected, got " .. typ, 2)
end
end
end
if init_ttl ~= nil then
local typ = type(init_ttl)
if typ ~= "number" then
init_ttl = tonumber(init_ttl)
if not init_ttl then
error("bad init_ttl arg: number expected, got " .. typ, 2)
end
end
if init_ttl < 0 then
error('bad "init_ttl" argument', 2)
end
if not init then
error('must provide "init" when providing "init_ttl"', 2)
end
else
init_ttl = 0
end
local rc = ngx_lua_ffi_shdict_incr(zone, key, key_len, num_value,
errmsg, init and 1 or 0,
init or 0, init_ttl * 1000,
forcible)
if rc ~= 0 then -- ~= NGX_OK
return nil, ffi_str(errmsg[0])
end
if not init then
return tonumber(num_value[0])
end
return tonumber(num_value[0]), nil, forcible[0] == 1
end
local function shdict_flush_all(zone)
zone = check_zone(zone)
ngx_lua_ffi_shdict_flush_all(zone)
end
local function shdict_ttl(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local rc = ngx_lua_ffi_shdict_get_ttl(zone, key, key_len)
if rc == FFI_DECLINED then
return nil, "not found"
end
return tonumber(rc) / 1000
end
local function shdict_expire(zone, key, exptime)
zone = check_zone(zone)
if not exptime then
error('bad "exptime" argument', 2)
end
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local rc = ngx_lua_ffi_shdict_set_expire(zone, key, key_len,
exptime * 1000)
if rc == FFI_DECLINED then
return nil, "not found"
end
-- NGINX_OK/FFI_OK
return true
end
local function shdict_capacity(zone)
zone = check_zone(zone)
return tonumber(ngx_lua_ffi_shdict_capacity(zone))
end
local shdict_free_space
if ngx_lua_ffi_shdict_free_space then
shdict_free_space = function (zone)
zone = check_zone(zone)
return tonumber(ngx_lua_ffi_shdict_free_space(zone))
end
else
shdict_free_space = function ()
error("'shm:free_space()' not supported in NGINX < 1.11.7", 2)
end
end
local _, dict = next(ngx_shared, nil)
if dict then
local mt = getmetatable(dict)
if mt then
mt = mt.__index
if mt then
mt.get = shdict_get
mt.get_stale = shdict_get_stale
mt.incr = shdict_incr
mt.set = shdict_set
mt.safe_set = shdict_safe_set
mt.add = shdict_add
mt.safe_add = shdict_safe_add
mt.replace = shdict_replace
mt.delete = shdict_delete
mt.flush_all = shdict_flush_all
mt.ttl = shdict_ttl
mt.expire = shdict_expire
mt.capacity = shdict_capacity
mt.free_space = shdict_free_space
end
end
end
return _M

159
resty/core/time.lua Normal file
View file

@ -0,0 +1,159 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local error = error
local tonumber = tonumber
local type = type
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local time_val = ffi_new("long[1]")
local get_string_buf = base.get_string_buf
local ngx = ngx
local FFI_ERROR = base.FFI_ERROR
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_now
local ngx_lua_ffi_time
local ngx_lua_ffi_today
local ngx_lua_ffi_localtime
local ngx_lua_ffi_utctime
local ngx_lua_ffi_update_time
if subsystem == 'http' then
ffi.cdef[[
double ngx_http_lua_ffi_now(void);
long ngx_http_lua_ffi_time(void);
void ngx_http_lua_ffi_today(unsigned char *buf);
void ngx_http_lua_ffi_localtime(unsigned char *buf);
void ngx_http_lua_ffi_utctime(unsigned char *buf);
void ngx_http_lua_ffi_update_time(void);
int ngx_http_lua_ffi_cookie_time(unsigned char *buf, long t);
void ngx_http_lua_ffi_http_time(unsigned char *buf, long t);
void ngx_http_lua_ffi_parse_http_time(const unsigned char *str, size_t len,
long *time);
]]
ngx_lua_ffi_now = C.ngx_http_lua_ffi_now
ngx_lua_ffi_time = C.ngx_http_lua_ffi_time
ngx_lua_ffi_today = C.ngx_http_lua_ffi_today
ngx_lua_ffi_localtime = C.ngx_http_lua_ffi_localtime
ngx_lua_ffi_utctime = C.ngx_http_lua_ffi_utctime
ngx_lua_ffi_update_time = C.ngx_http_lua_ffi_update_time
elseif subsystem == 'stream' then
ffi.cdef[[
double ngx_stream_lua_ffi_now(void);
long ngx_stream_lua_ffi_time(void);
void ngx_stream_lua_ffi_today(unsigned char *buf);
void ngx_stream_lua_ffi_localtime(unsigned char *buf);
void ngx_stream_lua_ffi_utctime(unsigned char *buf);
void ngx_stream_lua_ffi_update_time(void);
]]
ngx_lua_ffi_now = C.ngx_stream_lua_ffi_now
ngx_lua_ffi_time = C.ngx_stream_lua_ffi_time
ngx_lua_ffi_today = C.ngx_stream_lua_ffi_today
ngx_lua_ffi_localtime = C.ngx_stream_lua_ffi_localtime
ngx_lua_ffi_utctime = C.ngx_stream_lua_ffi_utctime
ngx_lua_ffi_update_time = C.ngx_stream_lua_ffi_update_time
end
function ngx.now()
return tonumber(ngx_lua_ffi_now())
end
function ngx.time()
return tonumber(ngx_lua_ffi_time())
end
function ngx.update_time()
ngx_lua_ffi_update_time()
end
function ngx.today()
-- the format of today is 2010-11-19
local today_buf_size = 10
local buf = get_string_buf(today_buf_size)
ngx_lua_ffi_today(buf)
return ffi_str(buf, today_buf_size)
end
function ngx.localtime()
-- the format of localtime is 2010-11-19 20:56:31
local localtime_buf_size = 19
local buf = get_string_buf(localtime_buf_size)
ngx_lua_ffi_localtime(buf)
return ffi_str(buf, localtime_buf_size)
end
function ngx.utctime()
-- the format of utctime is 2010-11-19 20:56:31
local utctime_buf_size = 19
local buf = get_string_buf(utctime_buf_size)
ngx_lua_ffi_utctime(buf)
return ffi_str(buf, utctime_buf_size)
end
if subsystem == 'http' then
function ngx.cookie_time(sec)
if type(sec) ~= "number" then
error("number argument only", 2)
end
-- the format of cookie time is Mon, 28-Sep-2038 06:00:00 GMT
-- or Mon, 28-Sep-18 06:00:00 GMT
local cookie_time_buf_size = 29
local buf = get_string_buf(cookie_time_buf_size)
local used_size = C.ngx_http_lua_ffi_cookie_time(buf, sec)
return ffi_str(buf, used_size)
end
function ngx.http_time(sec)
if type(sec) ~= "number" then
error("number argument only", 2)
end
-- the format of http time is Mon, 28 Sep 1970 06:00:00 GMT
local http_time_buf_size = 29
local buf = get_string_buf(http_time_buf_size)
C.ngx_http_lua_ffi_http_time(buf, sec)
return ffi_str(buf, http_time_buf_size)
end
function ngx.parse_http_time(time_str)
if type(time_str) ~= "string" then
error("string argument only", 2)
end
C.ngx_http_lua_ffi_parse_http_time(time_str, #time_str, time_val)
local res = time_val[0]
if res == FFI_ERROR then
return nil
end
return tonumber(res)
end
end
return {
version = base.version
}

94
resty/core/uri.lua Normal file
View file

@ -0,0 +1,94 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local type = type
local tostring = tostring
local get_string_buf = base.get_string_buf
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_escape_uri
local ngx_lua_ffi_unescape_uri
local ngx_lua_ffi_uri_escaped_length
if subsystem == "http" then
ffi.cdef[[
size_t ngx_http_lua_ffi_uri_escaped_length(const unsigned char *src,
size_t len);
void ngx_http_lua_ffi_escape_uri(const unsigned char *src, size_t len,
unsigned char *dst);
size_t ngx_http_lua_ffi_unescape_uri(const unsigned char *src,
size_t len, unsigned char *dst);
]]
ngx_lua_ffi_escape_uri = C.ngx_http_lua_ffi_escape_uri
ngx_lua_ffi_unescape_uri = C.ngx_http_lua_ffi_unescape_uri
ngx_lua_ffi_uri_escaped_length = C.ngx_http_lua_ffi_uri_escaped_length
elseif subsystem == "stream" then
ffi.cdef[[
size_t ngx_stream_lua_ffi_uri_escaped_length(const unsigned char *src,
size_t len);
void ngx_stream_lua_ffi_escape_uri(const unsigned char *src, size_t len,
unsigned char *dst);
size_t ngx_stream_lua_ffi_unescape_uri(const unsigned char *src,
size_t len, unsigned char *dst);
]]
ngx_lua_ffi_escape_uri = C.ngx_stream_lua_ffi_escape_uri
ngx_lua_ffi_unescape_uri = C.ngx_stream_lua_ffi_unescape_uri
ngx_lua_ffi_uri_escaped_length = C.ngx_stream_lua_ffi_uri_escaped_length
end
ngx.escape_uri = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local dlen = ngx_lua_ffi_uri_escaped_length(s, slen)
-- print("dlen: ", tonumber(dlen))
if dlen == slen then
return s
end
local dst = get_string_buf(dlen)
ngx_lua_ffi_escape_uri(s, slen, dst)
return ffi_string(dst, dlen)
end
ngx.unescape_uri = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local dlen = slen
local dst = get_string_buf(dlen)
dlen = ngx_lua_ffi_unescape_uri(s, slen, dst)
return ffi_string(dst, dlen)
end
return {
version = base.version,
}

44
resty/core/utils.lua Normal file
View file

@ -0,0 +1,44 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
base.allows_subsystem("http")
local C = ffi.C
local ffi_str = ffi.string
local ffi_copy = ffi.copy
local byte = string.byte
local str_find = string.find
local get_string_buf = base.get_string_buf
ffi.cdef[[
void ngx_http_lua_ffi_str_replace_char(unsigned char *buf, size_t len,
const unsigned char find, const unsigned char replace);
]]
local _M = {
version = base.version
}
function _M.str_replace_char(str, find, replace)
if not str_find(str, find, nil, true) then
return str
end
local len = #str
local buf = get_string_buf(len)
ffi_copy(buf, str)
C.ngx_http_lua_ffi_str_replace_char(buf, len, byte(find),
byte(replace))
return ffi_str(buf, len)
end
return _M

160
resty/core/var.lua Normal file
View file

@ -0,0 +1,160 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local type = type
local error = error
local tostring = tostring
local setmetatable = setmetatable
local get_request = base.get_request
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local new_tab = base.new_tab
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_var_get
local ngx_lua_ffi_var_set
local ERR_BUF_SIZE = 256
ngx.var = new_tab(0, 0)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_var_get(ngx_http_request_t *r,
const char *name_data, size_t name_len, char *lowcase_buf,
int capture_id, char **value, size_t *value_len, char **err);
int ngx_http_lua_ffi_var_set(ngx_http_request_t *r,
const unsigned char *name_data, size_t name_len,
unsigned char *lowcase_buf, const unsigned char *value,
size_t value_len, unsigned char *errbuf, size_t *errlen);
]]
ngx_lua_ffi_var_get = C.ngx_http_lua_ffi_var_get
ngx_lua_ffi_var_set = C.ngx_http_lua_ffi_var_set
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r,
const char *name_data, size_t name_len, char *lowcase_buf,
int capture_id, char **value, size_t *value_len, char **err);
int ngx_stream_lua_ffi_var_set(ngx_stream_lua_request_t *r,
const unsigned char *name_data, size_t name_len,
unsigned char *lowcase_buf, const unsigned char *value,
size_t value_len, unsigned char *errbuf, size_t *errlen);
]]
ngx_lua_ffi_var_get = C.ngx_stream_lua_ffi_var_get
ngx_lua_ffi_var_set = C.ngx_stream_lua_ffi_var_set
end
local value_ptr = ffi_new("unsigned char *[1]")
local errmsg = base.get_errmsg_ptr()
local function var_get(self, name)
local r = get_request()
if not r then
error("no request found")
end
local value_len = get_size_ptr()
local rc
if type(name) == "number" then
rc = ngx_lua_ffi_var_get(r, nil, 0, nil, name, value_ptr, value_len,
errmsg)
else
if type(name) ~= "string" then
error("bad variable name", 2)
end
local name_len = #name
local lowcase_buf = get_string_buf(name_len)
rc = ngx_lua_ffi_var_get(r, name, name_len, lowcase_buf, 0, value_ptr,
value_len, errmsg)
end
-- ngx.log(ngx.WARN, "rc = ", rc)
if rc == 0 then -- NGX_OK
return ffi_str(value_ptr[0], value_len[0])
end
if rc == -5 then -- NGX_DECLINED
return nil
end
if rc == -1 then -- NGX_ERROR
error(ffi_str(errmsg[0]), 2)
end
end
local function var_set(self, name, value)
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
error("bad variable name", 2)
end
local name_len = #name
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local lowcase_buf = get_string_buf(name_len + ERR_BUF_SIZE)
local value_len
if value == nil then
value_len = 0
else
if type(value) ~= 'string' then
value = tostring(value)
end
value_len = #value
end
local errbuf = lowcase_buf + name_len
local rc = ngx_lua_ffi_var_set(r, name, name_len, lowcase_buf, value,
value_len, errbuf, errlen)
-- ngx.log(ngx.WARN, "rc = ", rc)
if rc == 0 then -- NGX_OK
return
end
if rc == -1 then -- NGX_ERROR
error(ffi_str(errbuf, errlen[0]), 2)
end
end
do
local mt = new_tab(0, 2)
mt.__index = var_get
mt.__newindex = var_set
setmetatable(ngx.var, mt)
end
return {
version = base.version
}

77
resty/core/worker.lua Normal file
View file

@ -0,0 +1,77 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local new_tab = base.new_tab
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_worker_id
local ngx_lua_ffi_worker_pid
local ngx_lua_ffi_worker_count
local ngx_lua_ffi_worker_exiting
ngx.worker = new_tab(0, 4)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_worker_id(void);
int ngx_http_lua_ffi_worker_pid(void);
int ngx_http_lua_ffi_worker_count(void);
int ngx_http_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_http_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_http_lua_ffi_worker_pid
ngx_lua_ffi_worker_count = C.ngx_http_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_http_lua_ffi_worker_exiting
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_worker_id(void);
int ngx_stream_lua_ffi_worker_pid(void);
int ngx_stream_lua_ffi_worker_count(void);
int ngx_stream_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_stream_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_stream_lua_ffi_worker_pid
ngx_lua_ffi_worker_count = C.ngx_stream_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_stream_lua_ffi_worker_exiting
end
function ngx.worker.exiting()
return ngx_lua_ffi_worker_exiting() ~= 0
end
function ngx.worker.pid()
return ngx_lua_ffi_worker_pid()
end
function ngx.worker.id()
local id = ngx_lua_ffi_worker_id()
if id < 0 then
return nil
end
return id
end
function ngx.worker.count()
return ngx_lua_ffi_worker_count()
end
return {
_VERSION = base.version
}