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: support more key for limit-rate, limit-count, limit-conn #444

Merged
merged 3 commits into from
Aug 27, 2019
Merged
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: 2 additions & 0 deletions doc/plugins/limit-conn-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Apisix 的限制并发请求(或并发连接)插件。

For example, one can use the host name (or server zone) as the key so that we limit concurrency per host name. Otherwise, we can also use the client address as the key so that we can avoid a single client from flooding our service with too many parallel connections or requests.

Now accept those as key: "remote_addr"(client's IP), "server_addr"(server's IP), "X-Forwarded-For/X-Real-IP" in request header.

* `rejected_code`: The HTTP status code returned when the request exceeds the threshold is rejected. The default is 503.

#### enable plugin
Expand Down
2 changes: 2 additions & 0 deletions doc/plugins/limit-conn.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Limiting request concurrency (or concurrent connections) plugin for Apisix.

For example, one can use the host name (or server zone) as the key so that we limit concurrency per host name. Otherwise, we can also use the client address as the key so that we can avoid a single client from flooding our service with too many parallel connections or requests.

Now accept those as key: "remote_addr"(client's IP), "server_addr"(server's IP), "X-Forwarded-For/X-Real-IP" in request header.

* `rejected_code`: The HTTP status code returned when the request exceeds the threshold is rejected. The default is 503.

#### enable plugin
Expand Down
2 changes: 1 addition & 1 deletion doc/plugins/limit-count-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* `count`:指定时间窗口内的请求数量阈值
* `time_window`:时间窗口的大小(以秒为单位),超过这个时间就会重置
* `rejected_code`:当请求超过阈值被拒绝时,返回的 HTTP 状态码,默认是 503
* `key`:是用来做请求计数的依据,当前只接受终端 IP 做为 key,即 "remote_addr"
* `key`:是用来做请求计数的依据,当前接受的 key 有:"remote_addr"(客户端IP地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For" 或 "X-Real-IP"。

### 示例

Expand Down
2 changes: 1 addition & 1 deletion doc/plugins/limit-count.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* `count`: is the specified number of requests threshold.
* `time_window`: is the time window in seconds before the request count is reset.
* `rejected_code`: The HTTP status code returned when the request exceeds the threshold is rejected. The default is 503.
* `key`: is the user specified key to limit the rate, now only accept "remote_addr"(client's IP) as key
* `key`: is the user specified key to limit the rate, now accept those as key: "remote_addr"(client's IP), "server_addr"(server's IP), "X-Forwarded-For/X-Real-IP" in request header.

### example

Expand Down
2 changes: 1 addition & 1 deletion doc/plugins/limit-req-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* `rate`:指定的请求速率(以秒为单位),请求速率超过 `rate` 但没有超过 (`rate` + `brust`)的请求会被加上延时
* `burst`:请求速率超过 (`rate` + `brust`)的请求会被直接拒绝
* `rejected_code`:当请求超过阈值被拒绝时,返回的 HTTP 状态码
* `key`:是用来做请求计数的依据,当前只接受终端 IP 做为 key,即 "remote_addr"
* `key`:是用来做请求计数的依据,当前接受的 key 有:"remote_addr"(客户端IP地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For" 或 "X-Real-IP"。

### 示例

Expand Down
2 changes: 1 addition & 1 deletion doc/plugins/limit-req.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ limit request rate using the "leaky bucket" method.
* `rate`: is the specified request rate (number per second) threshold.Requests exceeding this rate (and below `burst`) will get delayed to conform to the rate.
* `burst`: is the number of excessive requests per second allowed to be delayed. Requests exceeding this hard limit will get rejected immediately.
* `rejected_code`: The HTTP status code returned when the request exceeds the threshold is rejected. The default is 503.
* `key`: is the user specified key to limit the rate, now only accept "remote_addr"(client's IP) as key
* `key`: is the user specified key to limit the rate, now accept those as key: "remote_addr"(client's IP), "server_addr"(server's IP), "X-Forwarded-For/X-Real-IP" in request header.

### example

Expand Down
9 changes: 6 additions & 3 deletions lua/apisix/plugins/limit-conn.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ local schema = {
conn = {type = "integer", minimum = 0},
burst = {type = "integer", minimum = 0},
default_conn_delay = {type = "number", minimum = 0},
key = {type = "string", enum = {"remote_addr", "server_addr"}},
key = {type = "string",
enum = {"remote_addr", "server_addr", "http_x_real_ip",
"http_x_forwarded_for"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not hard code the key of headers or args

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user can specify the value of key, which's the point.

},
rejected_code = {type = "integer", minimum = 200},
},
required = {"conn", "burst", "default_conn_delay", "key", "rejected_code"}
Expand Down Expand Up @@ -50,12 +53,12 @@ function _M.access(conf, ctx)
end

local key = (ctx.var[conf.key] or "") .. ctx.conf_type .. ctx.conf_version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctx.var can also get value from headers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use core.request.header

local rejected_code = conf.rejected_code
core.log.info("limit key: ", key)

local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
return rejected_code
return conf.rejected_code
end

core.log.error("failed to limit req: ", err)
Expand Down
7 changes: 5 additions & 2 deletions lua/apisix/plugins/limit-count.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ local schema = {
properties = {
count = {type = "integer", minimum = 0},
time_window = {type = "integer", minimum = 0},
key = {type = "string", enum = {"remote_addr", "server_addr"}},
key = {type = "string",
enum = {"remote_addr", "server_addr", "http_x_real_ip",
"http_x_forwarded_for"},
},
rejected_code = {type = "integer", minimum = 200, maximum = 600},
},
additionalProperties = false,
Expand Down Expand Up @@ -45,7 +48,7 @@ function _M.access(conf, ctx)
end

local key = (ctx.var[conf.key] or "") .. ctx.conf_type .. ctx.conf_version
core.log.info("key: ", key)
core.log.info("limit key: ", key)

local delay, remaining = lim:incoming(key, true)
if not delay then
Expand Down
7 changes: 6 additions & 1 deletion lua/apisix/plugins/limit-req.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ local schema = {
properties = {
rate = {type = "number", minimum = 0},
burst = {type = "number", minimum = 0},
key = {type = "string", enum = {"remote_addr", "server_addr"}},
key = {type = "string",
enum = {"remote_addr", "server_addr", "http_x_real_ip",
"http_x_forwarded_for"},
},
rejected_code = {type = "integer", minimum = 200},
},
required = {"rate", "burst", "key", "rejected_code"}
Expand Down Expand Up @@ -49,6 +52,8 @@ function _M.access(conf, ctx)
end

local key = (ctx.var[conf.key] or "") .. ctx.conf_type .. ctx.conf_version
core.log.info("limit key: ", key)

local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
Expand Down
190 changes: 187 additions & 3 deletions t/plugin/limit-conn.t
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ add_block_preprocessor(sub {
my ($block) = @_;
my $port = $ENV{TEST_NGINX_SERVER_PORT};

my $config = $block->config // "";
$config .= <<_EOC_;

my $config = $block->config // <<_EOC_;
location /access_root_dir {
content_by_lua_block {
local httpc = require "resty.http"
Expand Down Expand Up @@ -597,3 +595,189 @@ GET /t
passed
--- no_error_log
[error]



=== TEST 16: key: http_x_real_ip
--- 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,
[[{
"plugins": {
"limit-conn": {
"conn": 5,
"burst": 1,
"default_conn_delay": 0.1,
"rejected_code": 503,
"key": "http_x_real_ip"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/limit_conn"
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 17: exceeding the burst (X-Real-IP)
--- config
location /access_root_dir {
content_by_lua_block {
local port = ngx.var.server_port
local httpc = require "resty.http"
local hc = httpc:new()

local res, err = hc:request_uri('http://127.0.0.1:' .. port .. '/limit_conn', {
keepalive = false,
headers = {["X-Real-IP"] = "10.10.10.1"}
})
if res then
ngx.exit(res.status)
end
}
}

location /test_concurrency {
content_by_lua_block {
local reqs = {}
for i = 1, 10 do
reqs[i] = { "/access_root_dir" }
end
local resps = { ngx.location.capture_multi(reqs) }
for i, resp in ipairs(resps) do
ngx.say(resp.status)
end
}
}
--- more_headers
X-Real-IP: 10.0.0.1
--- request
GET /test_concurrency
--- timeout: 10s
--- response_body
200
200
200
200
200
200
503
503
503
503
--- error_log
limit key: 10.10.10.1route



=== TEST 18: key: http_x_forwarded_for
--- 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,
[[{
"plugins": {
"limit-conn": {
"conn": 5,
"burst": 1,
"default_conn_delay": 0.1,
"rejected_code": 503,
"key": "http_x_forwarded_for"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/limit_conn"
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 19: exceeding the burst(X-Forwarded-For)
--- config
location /access_root_dir {
content_by_lua_block {
local port = ngx.var.server_port
local httpc = require "resty.http"
local hc = httpc:new()

local res, err = hc:request_uri('http://127.0.0.1:' .. port .. '/limit_conn', {
keepalive = false,
headers = {["X-Forwarded-For"] = "10.10.10.2"}
})
if res then
ngx.exit(res.status)
end
}
}

location /test_concurrency {
content_by_lua_block {
local reqs = {}
for i = 1, 10 do
reqs[i] = { "/access_root_dir" }
end
local resps = { ngx.location.capture_multi(reqs) }
for i, resp in ipairs(resps) do
ngx.say(resp.status)
end
}
}
--- more_headers
X-Real-IP: 10.0.0.1
--- request
GET /test_concurrency
--- timeout: 10s
--- response_body
200
200
200
200
200
200
503
503
503
503
--- error_log
limit key: 10.10.10.2route