diff --git a/.travis.yml b/.travis.yml index 3f5cf76bf..22e7496c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/lib/resty/core.lua b/lib/resty/core.lua index 71bb94642..53953e3cb 100644 --- a/lib/resty/core.lua +++ b/lib/resty/core.lua @@ -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" diff --git a/lib/resty/core/string.lua b/lib/resty/core/string.lua new file mode 100644 index 000000000..0735a2327 --- /dev/null +++ b/lib/resty/core/string.lua @@ -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 +} diff --git a/t/string.t b/t/string.t new file mode 100644 index 000000000..428341693 --- /dev/null +++ b/t/string.t @@ -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]