# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib '.';
use t::TestCore;

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

repeat_each(2);

plan tests => repeat_each() * (blocks() * 4 + 19);

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

__DATA__

=== TEST 1: ngx.req.get_headers
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local ffi = require "ffi"
            local headers
            for i = 1, 500 do
                headers = ngx.req.get_headers()
            end
            local keys = {}
            for k, _ in pairs(headers) do
                keys[#keys + 1] = k
            end
            table.sort(keys)
            for _, k in ipairs(keys) do
                ngx.say(k, ": ", headers[k])
            end
        }
    }
--- request
GET /t
--- response_body
bar: bar
baz: baz
connection: close
foo: foo
host: localhost
--- more_headers
Foo: foo
Bar: bar
Baz: baz
--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ .*? -> \d+\]/
--- no_error_log eval
[
"[error]",
qr/ -- NYI: (?!return to lower frame)/,
]



=== TEST 2: ngx.req.get_headers (raw)
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local ffi = require "ffi"
            local headers
            for i = 1, 500 do
                headers = ngx.req.get_headers(100, true)
            end
            local keys = {}
            for k, _ in pairs(headers) do
                keys[#keys + 1] = k
            end
            table.sort(keys)
            for _, k in ipairs(keys) do
                ngx.say(k, ": ", headers[k])
            end
        }
    }
--- request
GET /t
--- response_body
Bar: bar
Baz: baz
Connection: close
Foo: foo
Host: localhost
--- more_headers
Foo: foo
Bar: bar
Baz: baz
--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ .*? -> \d+\]/
--- no_error_log
[error]
 -- NYI:



=== TEST 3: ngx.req.get_headers (count is 2)
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local ffi = require "ffi"
            local headers
            for i = 1, 500 do
                headers = ngx.req.get_headers(2, true)
            end
            local keys = {}
            for k, _ in pairs(headers) do
                keys[#keys + 1] = k
            end
            table.sort(keys)
            for _, k in ipairs(keys) do
                ngx.say(k, ": ", headers[k])
            end
        }
    }
--- request
GET /t
--- response_body
Connection: close
Host: localhost
--- more_headers
Foo: foo
Bar: bar
Baz: baz
--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
--- no_error_log
[error]
 -- NYI:



=== TEST 4: ngx.req.get_headers (metatable)
--- http_config eval
"
$::HttpConfig
underscores_in_headers on;
"
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local ffi = require "ffi"
            local headers, header
            for i = 1, 500 do
                headers = ngx.req.get_headers()
                header = headers["foo_BAR"]
            end
            ngx.say("foo_BAR: ", header)
            local keys = {}
            for k, _ in pairs(headers) do
                keys[#keys + 1] = k
            end
            table.sort(keys)
            for _, k in ipairs(keys) do
                ngx.say(k, ": ", headers[k])
            end

            ngx.say("X_Bar_Header: ", headers["X_Bar_Header"])
            ngx.say("x_Bar_Header: ", headers["x_Bar_Header"])
            ngx.say("x_bar_header: ", headers["x_bar_header"])
        }
    }
--- request
GET /t
--- response_body
foo_BAR: foo
baz: baz
connection: close
foo-bar: foo
host: localhost
x_bar_header: bar
X_Bar_Header: bar
x_Bar_Header: bar
x_bar_header: bar
--- more_headers
Foo-Bar: foo
Baz: baz
X_Bar_Header: bar
--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ .*? -> \d+\]/
--- no_error_log eval
["[error]",
qr/ -- NYI: (?!return to lower frame at)(?!C function 0x[0-9a-f]+ at content_by_lua\(nginx.conf:\d+\):15)/,
]



=== TEST 5: ngx.req.get_uri_args
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local ffi = require "ffi"
            local args
            for i = 1, 500 do
                args = ngx.req.get_uri_args()
            end
            if type(args) ~= "table" then
                ngx.say("bad args type found: ", args)
                return
            end
            local keys = {}
            for k, _ in pairs(args) do
                keys[#keys + 1] = k
            end
            table.sort(keys)
            for _, k in ipairs(keys) do
                local v = args[k]
                if type(v) == "table" then
                    ngx.say(k, ": ", table.concat(v, ", "))
                else
                    ngx.say(k, ": ", v)
                end
            end
        }
    }
--- request
GET /t?a=3%200&foo%20bar=&a=hello&blah
--- response_body
a: 3 0, hello
blah: true
foo bar: 
--- error_log eval
qr/\[TRACE\s+\d+ .*? -> \d+\]/
--- no_error_log
[error]
 -- NYI:
--- wait: 0.2



=== TEST 6: ngx.req.get_uri_args (empty)
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local ffi = require "ffi"
            local args
            for i = 1, 500 do
                args = ngx.req.get_uri_args()
            end
            if type(args) ~= "table" then
                ngx.say("bad args type found: ", args)
                return
            end
            local keys = {}
            for k, _ in pairs(args) do
                keys[#keys + 1] = k
            end
            table.sort(keys)
            for _, k in ipairs(keys) do
                local v = args[k]
                if type(v) == "table" then
                    ngx.say(k, ": ", table.concat(v, ", "))
                else
                    ngx.say(k, ": ", v)
                end
            end
        }
    }
--- request
GET /t?
--- response_body
--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
--- no_error_log
[error]
 -- NYI:



=== TEST 7: ngx.req.start_time()
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                t = ngx.req.start_time()
            end
            ngx.sleep(0.10)
            local elapsed = ngx.now() - t
            ngx.say(t > 1399867351)
            ngx.say(">= 0.099: ", elapsed >= 0.099)
            ngx.say("< 0.11: ", elapsed < 0.11)
            -- ngx.say(t, " ", elapsed)
        }
    }
--- request
GET /t
--- response_body
true
>= 0.099: true
< 0.11: true

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 8: ngx.req.get_method (GET)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                t = ngx.req.get_method()
            end
            ngx.say("method: ", t)
        }
    }
--- request
GET /t
--- response_body
method: GET

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 9: ngx.req.get_method (OPTIONS)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                t = ngx.req.get_method()
            end
            ngx.say("method: ", t)
        }
    }
--- request
OPTIONS /t
--- response_body
method: OPTIONS

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 10: ngx.req.get_method (POST)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                t = ngx.req.get_method()
            end
            ngx.say("method: ", t)
            ngx.req.discard_body()
        }
    }
--- request
POST /t
hello
--- response_body
method: POST

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 11: ngx.req.get_method (unknown method)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                t = ngx.req.get_method()
            end
            ngx.say("method: ", t)
            ngx.req.discard_body()
        }
    }
--- request
BLAH /t
hello
--- response_body
method: BLAH

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 12: ngx.req.get_method (CONNECT)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                t = ngx.req.get_method()
            end
            ngx.say("method: ", t)
            ngx.req.discard_body()
        }
    }
--- request
CONNECT /t
hello
--- response_body
method: CONNECT

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch
--- skip_nginx
6: >= 1.21.1



=== TEST 13: ngx.req.set_method (GET -> PUT)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                ngx.req.set_method(ngx.HTTP_PUT)
            end
            ngx.say("method: ", ngx.req.get_method())
        }
    }
--- request
GET /t
--- response_body
method: PUT

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 14: ngx.req.set_header (single number value)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                ngx.req.set_header("foo", i)
            end
            ngx.say("header foo: ", ngx.var.http_foo)
        }
    }
--- request
GET /t
--- response_body
header foo: 500

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 15: ngx.req.set_header (nil value)
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                ngx.req.set_header("foo", nil)
            end
            ngx.say("header foo: ", type(ngx.var.http_foo))
        }
    }
--- request
GET /t
--- response_body
header foo: nil

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):3 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 16: ngx.req.clear_header
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            ngx.req.set_header("foo", "hello")
            local t
            for i = 1, 500 do
                t = ngx.req.clear_header("foo")
            end
            ngx.say("header foo: ", type(ngx.var.http_foo))
        }
    }
--- request
GET /t
--- response_body
header foo: nil

--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
--- no_error_log
[error]
bad argument type
stitch



=== TEST 17: ngx.req.set_header (multiple values)
--- config
    location = /t {
        content_by_lua_block {
            ngx.req.set_header("Foo", { "baz", 123 })

            ngx.say("Foo: ", table.concat(ngx.req.get_headers()["Foo"], ", "))
        }
    }
--- request
GET /t
--- more_headers
Foo: bar
--- response_body
Foo: baz, 123
--- no_error_log
[error]



=== TEST 18: ngx.req.get_header (metatable is nil)
--- config
    location = /t {
        content_by_lua_block {
            local headers = ngx.req.get_headers()

            ngx.say(string.format("%s,%s",type(headers), type(getmetatable(headers))))
        }
    }

--- raw_request eval
"GET /t \r\n"
--- http09
--- response_body
table,table



=== TEST 19: CONNECT method is considered invalid since nginx 1.21.1
--- config
    location = /t {
        access_log off;
        content_by_lua_block {
            local t
            for i = 1, 500 do
                t = ngx.req.get_method()
            end
            ngx.say("method: ", t)
            ngx.req.discard_body()
        }
    }
--- request
CONNECT /t
hello
--- error_code: 400
--- no_error_log
[error]
--- skip_nginx
2: < 1.21.1



=== TEST 20: get_uri_args allows to reuse table
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local base = require "resty.core.base"
            local args = base.new_tab(0, 3)
            local id = tostring(args)
            for i = 1, 5 do
                base.clear_tab(args)
                args = ngx.req.get_uri_args(-1, args)
                assert(tostring(args) == id)
            end
            local keys = {}
            for k, _ in pairs(args) do
                keys[#keys + 1] = k
            end
            table.sort(keys)
            for _, k in ipairs(keys) do
                local v = args[k]
                if type(v) == "table" then
                    ngx.say(k, ": ", table.concat(v, ", "))
                else
                    ngx.say(k, ": ", v)
                end
            end
        }
    }
--- request
GET /t?a=3%200&foo%20bar=&a=hello&blah
--- response_body
a: 3 0, hello
blah: true
foo bar: 
--- no_error_log
[error]



=== TEST 21: get_uri_args allows to reuse table (empty)
--- config
    location = /t {
        set $foo hello;
        content_by_lua_block {
            local base = require "resty.core.base"
            local args = base.new_tab(0, 3)
            local id = tostring(args)
            for i = 1, 5 do
                args = ngx.req.get_uri_args(-1, args)
                assert(tostring(args) == id)
            end
            local n_key = 0
            for k, _ in pairs(args) do
                n_key = n_key + 1
            end
            ngx.say(n_key)
        }
    }
--- request
GET /t
--- response_body
0
--- no_error_log
[error]
