Search code examples
nginxluaquotingopenresty

Understanding quoting of strings in inline Lua code in Nginx+OpenResty


For testing, I needed to create some fake data in an Nginx server with OpenResty. I needed to create an array where one IP address is replicated, for example:

 ["1.2.3.4", "1.2.3.4", ...]

In Ruby, an implementation could look like this:

 "[" + (["\"1.2.3.4\""] * 4096).join(", ") + "]"

I'm a beginner in Lua, and I struggled quite a lot to get it working in the OpenResty environment. This is what finally seems to work:

   content_by_lua '
        -- ... some code ...
        local ips = {}
        for i=1,4096 do ips[i] = "\\"1.2.3.4\\"" end
        local fakeData = table.concat(ips, ", ")
        local fakeResponse = "[" .. fakeData .. "]"
        --- ...
   '

I had some problems with the string quoting. First, I tried '"1.2.3.4"', which did not work in the inline Lua section. That is understandable because the ' character is already the separator for the content_by_lua section.

Next, I tried "\"1.2.3.4\"", which I would have expected to work, but it resulted in this (confusing) error:

failed to load inlined Lua code:
content_by_lua(nginx.conf:235):17: malformed number near '1.2.3.4'

By trial and error, I found out that this error goes away if you use double quoting ("\\"1.2.3.4\\""). This works in OpenResty, although the same code is rejected if I try it out in the Lua REPL:

$ rep.lua 
Lua REPL 0.8
> "\\"1.2.3.4\\""
[string "REPL"]:1: unexpected symbol near '"\"'

To help me understand and become a better Lua/OpenResty programmer, I have some questions about quoting:

  1. Why do you need double-quoting (in OpenResty)? (Why "\\"1.2.3.4\\"" instead of "\"1.2.3.4\""?)
  2. Why does OpenResty behave differently than the Lua REPL?
  3. Is it possible to use single quotes in content_by_lua sections? (To simplify it to '"1.2.3.4"')

(Also, an unrelated question that bothers me. Is there there a simpler way to express the code? When I compare it with my Ruby snippet, I'm quite sure that it can also be expressed much more elegantly in Lua.)


Solution

  • As Egor says, this is because the backslash character \ is stripped out by the nginx config file parser before it is parsed by Lua. In your case, "\\"1.2.3.4\\"" is correct, because it is parsed to "\"1.2.3.4\"" by the nginx config parser, and then to the string "1.2.3.4" by Lua.

    If you don't want to worry about how many backslashes to use, there are a couple of alternatives. The easiest is to use content_by_lua_block, which is available from OpenResty v0.9.17 onwards.

    content_by_lua_block {
        -- ... some code ...
        local ips = {}
        for i=1,4096 do ips[i] = '"1.2.3.4"' end
        local fakeData = table.concat(ips, ", ")
        local fakeResponse = "[" .. fakeData .. "]"
        --- ...
    }
    

    If you can't use Lua blocks in your version of OpenResty, instead you can use Lua's multiline string syntax.

    content_by_lua '
        -- ... some code ...
        local ips = {}
        for i=1,4096 do ips[i] = [["1.2.3.4"]] end
        local fakeData = table.concat(ips, ", ")
        local fakeResponse = "[" .. fakeData .. "]"
        --- ...
    '
    

    For more detail, see the OpenResty docs.

    Also, if you want to avoid working with tables at all, you can make the same test data by using string.rep.

    content_by_lua '
        -- ... some code ...
        local ip = [["1.2.3.4"]]
        local fakeResponse = "[" .. string.rep(ip .. ", ", 4095) .. ip .. "]"
        --- ...
    '