blob: a358ac6aa16efe59235aabcb1ab6da474c24e3fb [file]
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
no_long_string();
no_shuffle();
no_root_location();
log_level('info');
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->error_log && !$block->no_error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: configure count/time_window and rules at same time
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"count": 2,
"time_window": 5,
"rejected_code": 503,
"key_type": "var",
"key": "remote_addr",
"rules": [
{
"count": 1,
"time_window": 10,
"key": "${http_company}"
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"failed to check the configuration of plugin limit-count err: value should match only one schema, but matches both schemas 1 and 2"}
=== TEST 2: configure multiple rules with same key
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"rejected_code": 503,
"rules": [
{
"count": 5,
"time_window": 10,
"key": "${http_company}"
},
{
"count": 8,
"time_window": 20,
"key": "${http_company}"
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"failed to check the configuration of plugin limit-count err: duplicate key '${http_company}' in rules"}
=== TEST 3: setup route with rules
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"rejected_code": 503,
"rejected_msg" : "rejected",
"rules": [
{
"key": "${http_user}",
"count": "${http_jack_count}",
"time_window": 60
},
{
"key": "${http_project}",
"count": "${http_apisix_count}",
"time_window": 60
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: no any rule matched
--- request
GET /hello
--- error_code: 500
--- error_log
failed to get rate limit rules
=== TEST 5: match user rule
--- pipelined_requests eval
["GET /hello", "GET /hello", "GET /hello"]
--- more_headers
user: jack
jack-count: 2
--- error_code eval
[200, 200, 503]
--- response_body eval
["hello world\n", "hello world\n", "{\"error_msg\":\"rejected\"}\n"]
=== TEST 6: match project rule
--- pipelined_requests eval
["GET /hello", "GET /hello"]
--- more_headers
project: apisix
apisix-count: 3
--- error_code eval
[200, 200, 200, 503]
--- response_body eval
["hello world\n", "hello world\n", "hello world\n", "{\"error_msg\":\"rejected\"}\n"]
=== TEST 7: setup route with rules with variables with default values
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"rejected_code": 503,
"rejected_msg" : "rejected",
"rules": [
{
"count": "${http_count ?? 2}",
"time_window": "${http_tw ?? 5}",
"key": "${remote_addr}"
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: rules with variables in count - default value
--- pipelined_requests eval
["GET /hello", "GET /hello", "GET /hello"]
--- error_code eval
[200, 200, 503]
--- response_body eval
["hello world\n", "hello world\n", "{\"error_msg\":\"rejected\"}\n"]
=== TEST 9: rules with variables in count - with header
--- setup
ngx.sleep(5)
--- pipelined_requests eval
["GET /hello", "GET /hello"]
--- more_headers
count: 1
--- error_code eval
[200, 503]
--- response_body eval
["hello world\n", "{\"error_msg\":\"rejected\"}\n"]
=== TEST 10: rules with same key and custom headers
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"rejected_code": 503,
"rejected_msg" : "rejected",
"show_limit_quota_header": true,
"rules": [
{
"count": 2,
"time_window": 2,
"key": "${remote_addr}_2s"
},
{
"count": 3,
"time_window": 5,
"key": "${remote_addr}_5s"
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: test rules with same key
--- config
location /t {
content_by_lua_block {
local http = require("resty.http")
local httpc = http.new()
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"
for i = 1, 2, 1 do
local res = httpc:request_uri(uri)
if res.status ~= 200 then
ngx.say("first two requests failed, status: " .. res.status)
return
end
end
-- req 3, rejected by rule 1
res = httpc:request_uri(uri)
if res.status ~= 503 then
ngx.say("req 3 should be rejected by rule 1, but got status: ", res.status)
return
end
ngx.sleep(2)
-- req 4, after sleep
res = httpc:request_uri(uri)
if res.status ~= 200 then
ngx.say("req 4 failed, status: ", res.status)
return
end
-- req 5, rejected by rule 2
res = httpc:request_uri(uri)
if res.status ~= 503 then
ngx.say("req 5 should be rejected by rule 2, but got status: ", res.status)
return
end
ngx.say("passed")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 12: setup route with header_prefix
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"rejected_code": 503,
"rejected_msg" : "rejected",
"rules": [
{
"key": "${http_user}",
"count": "${http_jack_count}",
"time_window": 60,
"header_prefix": "jack"
},
{
"key": "${http_project}",
"count": "${http_apisix_count}",
"time_window": 60,
"header_prefix": "bar"
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 13: match jack
--- request
GET /hello
--- more_headers
user: jack
jack-count: 2
--- error_code: 200
--- response_headers
X-Jack-RateLimit-Limit: 2
X-Jack-RateLimit-Remaining: 1
X-Jack-RateLimit-Reset: 60
=== TEST 14: match bar
--- request
GET /hello
--- more_headers
project: apisix
apisix-count: 3
--- error_code: 200
--- response_headers
X-Bar-RateLimit-Limit: 3
X-Bar-RateLimit-Remaining: 2
X-Bar-RateLimit-Reset: 60
=== TEST 15: setup route without header_prefix
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"rejected_code": 503,
"rejected_msg" : "rejected",
"rules": [
{
"key": "${http_user}",
"count": "${http_jack_count}",
"time_window": 60
},
{
"key": "${http_project}",
"count": "${http_apisix_count}",
"time_window": 60
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 16: match jack
--- request
GET /hello
--- more_headers
user: jack
jack-count: 2
--- error_code: 200
--- response_headers
X-1-RateLimit-Limit: 2
X-1-RateLimit-Remaining: 1
X-1-RateLimit-Reset: 60
=== TEST 17: match bar
--- request
GET /hello
--- more_headers
project: apisix
apisix-count: 3
--- error_code: 200
--- response_headers
X-2-RateLimit-Limit: 3
X-2-RateLimit-Remaining: 2
X-2-RateLimit-Reset: 60
=== TEST 18: use variable with default value in rules.key
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"limit-count": {
"rejected_code": 503,
"rules": [
{
"count": 1,
"time_window": 10,
"key": "${http_project ?? apisix}"
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 19: with project header
--- request
GET /hello
--- more_headers
project: kubernetes
--- error_log eval
qr/limit key: \/apisix\/routes\/1:[^:]+:kubernetes/
=== TEST 20: without project header
--- request
GET /hello
--- error_log eval
qr/limit key: \/apisix\/routes\/1:[^:]+:apisix/