Lua - 空值判断的几种情况

举报
HuangJacky 发表于 2018/07/18 09:32:08 2018/07/18
【摘要】 在安全领域,lua编程语言因为其小巧在众多工具上都作为插件开发语言,常见的有openresty,nmap等。因此笔者将会开辟一个Lua相关的系列文章,主要记录工作过程中一些领悟或者是一些踩过的坑,希望能够借此平台帮助到读者们。

0x00 背景

最近在写一段nginx+redis的代码,主要基于openresty,其中使用到了lua-resty-redis库。我平时写代码都比较小心,针对外部输入的值一般都会进行异常判断,大概的代码如下:

local redis = require "redis"
local cjson = require "cjson"
--[[省略部分代码]]
local ok, err = redis:get("key")
if not ok then
    ngx.log(ngx.ERR, '[ERROR]:', err)
    return
end
local data = cjson.decode(ok)

在decode这里出现了错误提示,但是ok并没有为空或者nil,不然代码是走不到这里来。
发现问题后,我们就在前面打印一下ok数据的类型吧,大概的代码如下:

ngx.log(ngx.ERR, 'ok type: ', type(ok))
if not ok then
    -- TODO
end

这个时候我们得到的结果是userdata,这个东西算是一种复杂结构体,一般都是跨语言产生的,比如ffi.C这些。当时我的思路大概也是这样,肯定redis存放的数据是二进制的,但是呀,存放什么数据都是我自己控制的,不可能有什么畸形数据,因此这一点也排除了。最后在自己查看中发现,其实就是这个key不存在。

0x01 分析

既然原因找到了,我们就去看看为什么会这样,主要通过阅读lua-resty-redis的源码:

local function _read_reply(self, sock)
    local line, err = sock:receive()
    if not line then
        if err == "timeout" and not rawget(self, "_subscribed") then
            sock:close()
        end
        return nil, err
    end

    local prefix = byte(line)

    if prefix == 36 then    -- char '$'
        -- print("bulk reply")

        local size = tonumber(sub(line, 2))
        if size < 0 then
            return null
        end

        local data, err = sock:receive(size)
        if not data then
            if err == "timeout" then
                sock:close()
            end
            return nil, err
        end

        local dummy, err = sock:receive(2) -- ignore CRLF
        if not dummy then
            return nil, err
        end

        return data

    elseif prefix == 43 then    -- char '+'
        -- print("status reply")

        return sub(line, 2)

    elseif prefix == 42 then -- char '*'
        local n = tonumber(sub(line, 2))

        -- print("multi-bulk reply: ", n)
        if n < 0 then
            return null
        end

        local vals = new_tab(n, 0)
        local nvals = 0
        for i = 1, n do
            local res, err = _read_reply(self, sock)
            if res then
                nvals = nvals + 1
                vals[nvals] = res

            elseif res == nil then
                return nil, err

            else
                -- be a valid redis error value
                nvals = nvals + 1
                vals[nvals] = {false, err}
            end
        end

        return vals

    elseif prefix == 58 then    -- char ':'
        -- print("integer reply")
        return tonumber(sub(line, 2))

    elseif prefix == 45 then    -- char '-'
        -- print("error reply: ", n)

        return false, sub(line, 2)

    else
        -- when `line` is an empty string, `prefix` will be equal to nil.
        return nil, "unknown prefix: \"" .. tostring(prefix) .. "\""
    end
end

从上面的源码可以看到,在读取redis服务器返回数据的时候,如果某些格式不正确,比如数据长度的字节小于0这样的异常情况,函数就会返回null,注意是null不是nil
这个null的定义来自ngx.null,这个东西可以追溯到其官方文档lua-nginx-module. 

The ngx.null constant is a NULL light userdata usually used to represent nil values in Lua tables etc and is similar to the lua-cjson library’s cjson.null constant.

从上面描述看,ngx.null就是一个代表null的userdata结构,类似一个自定义的类,但是没有什么具体含义,同时文档里面也提到了类似的值还有cjson.null,以后小心被坑。

0x02 扩展

同时文档中还提到了,使用ngx.log对几个空值进行字符串打印的时候

  • nil会显示成“nil”,

  • 逻辑值会显示成“true”或者“false”,

  • ngx.null会被显示成“null”。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。