Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Re-implement ngx_lua's ngx.decode_args API function with FFI #130

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ install:
- git clone https://github.com/openresty/openresty.git ../openresty
- git clone https://github.com/openresty/openresty-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 -b decode_args https://github.com/hongliang5316/lua-nginx-module.git ../lua-nginx-module
- git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
- git clone https://github.com/openresty/lua-resty-lrucache.git
Expand Down
1 change: 1 addition & 0 deletions lib/resty/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require "resty.core.request"
require "resty.core.response"
require "resty.core.time"
require "resty.core.worker"
require "resty.core.string"


local base = require "resty.core.base"
Expand Down
89 changes: 89 additions & 0 deletions lib/resty/core/string.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
-- Copyright (C) Yichun Zhang (agentzh)


local ffi = require 'ffi'
local base = require "resty.core.base"


local new_tab = base.new_tab
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_str = ffi.string
local get_string_buf = base.get_string_buf
local ngx = ngx
local type = type
local tonumber = tonumber


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_get_args_count(const char *args,
size_t buf_len, int max);

int ngx_http_lua_ffi_decode_args(char *buf, const char *args,
size_t len, ngx_http_lua_ffi_table_elt_t *out, int count);
]]


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")


function ngx.decode_args(str, max_args)
max_args = tonumber(max_args)
if not max_args or max_args < 0 then
max_args = -1
end

local args_len = #str

local n = C.ngx_http_lua_ffi_get_args_count(str, args_len, max_args)
if n == 0 then
return {}
end

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_decode_args(strbuf, str,
args_len, 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

return args
end


return {
version = base.version
}
256 changes: 256 additions & 0 deletions t/string.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
# vim:set ft=nginx ts=4 sw=4 et fdm=marker:
use lib 'lib';
use Test::Nginx::Socket::Lua 'no_plan';
use Cwd qw(cwd);

#worker_connections(1014);
#master_process_enabled(1);
#log_level('warn');

repeat_each(2);

my $pwd = cwd();

our $HttpConfig = <<_EOC_;
lua_shared_dict dogs 1m;
lua_package_path "$pwd/lib/?.lua;../lua-resty-lrucache/lib/?.lua;;";
init_by_lua_block {
local verbose = false
if verbose then
local dump = require "jit.dump"
dump.on("b", "$Test::Nginx::Util::ErrLogFile")
else
local v = require "jit.v"
v.on("$Test::Nginx::Util::ErrLogFile")
end

require "resty.core"
-- jit.off()
}
_EOC_

#no_diff();
#no_long_string();
check_accum_error_log();
run_tests();

__DATA__

=== TEST 1 ngx.decode_args (sanity)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local args = "a=bar&b=foo"
args = ngx.decode_args(args)
ngx.say("a = ", args.a)
ngx.say("b = ", args.b)
';
}
--- request
GET /lua
--- response_body
a = bar
b = foo



=== TEST 2: ngx.decode_args (multi-value)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local args = "a=bar&b=foo&a=baz"
args = ngx.decode_args(args)
ngx.say("a = ", table.concat(args.a, ", "))
ngx.say("b = ", args.b)
';
}
--- request
GET /lua
--- response_body
a = bar, baz
b = foo



=== TEST 3: ngx.decode_args (empty string)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local args = ""
args = ngx.decode_args(args)
ngx.say("n = ", #args)
';
}
--- request
GET /lua
--- response_body
n = 0



=== TEST 4: ngx.decode_args (boolean args)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local args = "a&b"
args = ngx.decode_args(args)
ngx.say("a = ", args.a)
ngx.say("b = ", args.b)
';
}
--- request
GET /lua
--- response_body
a = true
b = true



=== TEST 5: ngx.decode_args (empty value args)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local args = "a=&b="
args = ngx.decode_args(args)
ngx.say("a = ", args.a)
ngx.say("b = ", args.b)
';
}
--- request
GET /lua
--- response_body
a =
b =



=== TEST 6: ngx.decode_args (max_args = 1)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local args = "a=bar&b=foo"
args = ngx.decode_args(args, 1)
ngx.say("a = ", args.a)
ngx.say("b = ", args.b)
';
}
--- request
GET /lua
--- response_body
a = bar
b = nil



=== TEST 7: ngx.decode_args (max_args = -1)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local args = "a=bar&b=foo"
args = ngx.decode_args(args, -1)
ngx.say("a = ", args.a)
ngx.say("b = ", args.b)
';
}
--- request
GET /lua
--- response_body
a = bar
b = foo



=== TEST 8: ngx.decode_args should not modify lua strings in place
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local s = "f+f=bar&B=foo"
args = ngx.decode_args(s)
local arr = {}
for k, v in pairs(args) do
table.insert(arr, k)
end
table.sort(arr)
for i, k in ipairs(arr) do
ngx.say("key: ", k)
end
ngx.say("s = ", s)
';
}
--- request
GET /lua
--- response_body
key: B
key: f f
s = f+f=bar&B=foo
--- no_error_log
[error]



=== TEST 9: ngx.decode_args should not modify lua strings in place (sample from Xu Jian)
--- http_config eval: $::HttpConfig
--- config
lua_need_request_body on;
location /t {
content_by_lua '
function split(s, delimiter)
local result = {}
local from = 1
local delim_from, delim_to = string.find(s, delimiter, from)
while delim_from do
table.insert(result, string.sub(s, from, delim_from - 1))
from = delim_to + 1
delim_from, delim_to = string.find(s, delimiter, from)
end
table.insert(result, string.sub(s, from))
return result
end

local post_data = ngx.req.get_body_data()

local commands = split(post_data, "||")
for _, command in pairs(commands) do
--command = ngx.unescape_uri(command)
local request_args = ngx.decode_args(command, 0)
local arr = {}
for k, v in pairs(request_args) do
table.insert(arr, k)
end
table.sort(arr)
for i, k in ipairs(arr) do
ngx.say(k, ": ", request_args[k])
end
ngx.say(" ===============")
end
';
}
--- request
POST /t
method=zadd&key=User%3A1227713%3Alikes%3Atwitters&arg1=1356514698&arg2=780984852||method=zadd&key=User%3A1227713%3Alikes%3Atwitters&arg1=1356514698&arg2=780984852||method=zadd&key=User%3A1227713%3Alikes%3Atwitters&arg1=1356514698&arg2=780984852
--- response_body
arg1: 1356514698
arg2: 780984852
key: User:1227713:likes:twitters
method: zadd
===============
arg1: 1356514698
arg2: 780984852
key: User:1227713:likes:twitters
method: zadd
===============
arg1: 1356514698
arg2: 780984852
key: User:1227713:likes:twitters
method: zadd
===============
--- no_error_log
[error]