想在api服务器里实现一个acl的功能,对某些请求(需要登录,需要检查appkey,需要限制频次等)做限制,对某些起始状态(比如登陆)或者终结状态(比如报错)的请求放行。
因为lua里木有switch case,因此通过一个acl_list的table去查找规则,因为需要限制的请求种类比较多,就把rule_check_token当成默认值了,一看是是这么写的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
local _M = { ACL_PASS = 0, ACL_DENIED = 1, } function _M.rule_always_pass(self) return _M.ACL_PASS end function _M.rule_check_token(query) if query == nil then return _M.ACL_DENIED end local token = query['token'] or ngx.req.get_headers()['Authorization'] local uid = query['uid'] -- ... end _M.acl_list = setmetatable({ ['error'] = _M.rule_always_pass, ['user/login'] = _M.rule_always_pass, }, { __index = _M.rule_check_token }) return _M |
然而却会在local token= xxxx那一行报stack overflow,想了半天也发现哪里有无限递归,因为query传进来的是http请求的query string解析出的键值对表。
把query打印出来一看,发现竟然是这个模块本身……
仔细看了文档才知道,原来__index后面的值是一个function时,lua会调用这个function去获得不存在的键,并且第一个参数是模块本身(即_M,一个table)。在这个例子里:
- 调用一个非默认规则的api
- lua调用了rule_check_token
- 参数query被传入了_M本身
- 运行到local token= xxxx这一行
- 这个table里(_M)又没有token这个键
- 回到2
所以就死循环了
所以要好好看文档
解决方法是可以套一个function
1 |
__index = function() return _M.rule_check_token end |
贴一个打印table的工具,方便调试:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function print_r(tbl, pref) for k,v in pairs(tbl) do if type(v) == 'table' then print((pref or "|-"), k, "\t", "<table>") _print_r(v, " "..(pref or "|-")) else if type(v) == 'function' then v = " <function>" end print((pref or "|-"), k, "\t", v) end end end |
可以打印出如下形式:
1 2 3 4 5 6 7 8 |
|-_VERSION 0.01 |-rule_always_pass <function> |-ACL_DENIED 1 |-acl_list <table> |-error <function> |-user/login <function> |-ACL_PASS 0 |-rule_check_token <function> |
使用openresty请自行改写成local function和ngx.say的形式