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

feat(limit-*): add custom reject-message for traffic control #4808

Merged
merged 10 commits into from
Aug 13, 2021
3 changes: 3 additions & 0 deletions apisix/plugins/limit-conn.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ local schema = {
rejected_code = {
type = "integer", minimum = 200, maximum = 599, default = 503
},
rejected_msg = {
type = "string"
},
allow_degradation = {type = "boolean", default = false}
},
required = {"conn", "burst", "default_conn_delay", "key"}
Expand Down
4 changes: 4 additions & 0 deletions apisix/plugins/limit-conn/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
--
local limit_conn_new = require("resty.limit.conn").new
local core = require("apisix.core")
local str_len = string.len
local sleep = core.sleep
local shdict_name = "plugin-limit-conn"
if ngx.config.subsystem == "stream" then
Expand Down Expand Up @@ -53,6 +54,9 @@ function _M.increase(conf, ctx)
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
if conf.rejected_msg and str_len(conf.rejected_msg) > 0 then
okaybase marked this conversation as resolved.
Show resolved Hide resolved
return conf.rejected_code, { error_msg = conf.rejected_msg }
end
return conf.rejected_code or 503
end

Expand Down
7 changes: 7 additions & 0 deletions apisix/plugins/limit-count.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
--
local limit_local_new = require("resty.limit.count").new
local core = require("apisix.core")
local str_len = string.len
local plugin_name = "limit-count"
local limit_redis_cluster_new
local limit_redis_new
Expand Down Expand Up @@ -45,6 +46,9 @@ local schema = {
rejected_code = {
type = "integer", minimum = 200, maximum = 599, default = 503
},
rejected_msg = {
type = "string"
},
policy = {
type = "string",
enum = {"local", "redis", "redis-cluster"},
Expand Down Expand Up @@ -175,6 +179,9 @@ function _M.access(conf, ctx)
if not delay then
local err = remaining
if err == "rejected" then
if conf.rejected_msg and str_len(conf.rejected_msg) > 0 then
return conf.rejected_code, { error_msg = conf.rejected_msg }
end
return conf.rejected_code
end

Expand Down
7 changes: 7 additions & 0 deletions apisix/plugins/limit-req.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
--
local limit_req_new = require("resty.limit.req").new
local core = require("apisix.core")
local str_len = string.len
local plugin_name = "limit-req"
local sleep = core.sleep

Expand All @@ -36,6 +37,9 @@ local schema = {
rejected_code = {
type = "integer", minimum = 200, maximum = 599, default = 503
},
rejected_msg = {
type = "string"
},
nodelay = {
type = "boolean", default = false
},
Expand Down Expand Up @@ -96,6 +100,9 @@ function _M.access(conf, ctx)
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
if conf.rejected_msg and str_len(conf.rejected_msg) > 0 then
return conf.rejected_code, { error_msg = conf.rejected_msg }
end
return conf.rejected_code
end

Expand Down
1 change: 1 addition & 0 deletions docs/en/latest/plugins/limit-conn.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Limiting request concurrency plugin.
| only_use_default_delay | boolean | optional | false | [true,false] | enable the strict mode of the latency seconds. If you set this option to `true`, it will run strictly according to the latency seconds you set without additional calculation logic. |
| key | object | required | | ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"] | to limit the concurrency level. <br />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. <br /> Now accept those as key: "remote_addr"(client's IP), "server_addr"(server's IP), "X-Forwarded-For/X-Real-IP" in request header, "consumer_name"(consumer's username). |
| rejected_code | string | optional | 503 | [200,...,599] | returned when the request exceeds `conn` + `burst` will be rejected. |
| rejected_msg | string | optional | | non-empty | The `error_msg` returned when the request exceeds `conn` + `burst` will be rejected. |
| allow_degradation | boolean | optional | false | | Whether to enable plugin degradation when the limit-conn function is temporarily unavailable. Allow requests to continue when the value is set to true, default false. |

**Key can be customized by the user, only need to modify a line of code of the plug-in to complete. It is a security consideration that is not open in the plugin.**
Expand Down
13 changes: 13 additions & 0 deletions docs/en/latest/plugins/limit-count.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Limit request rate by a fixed number of requests in a given time window.
| time_window | integer | required | | time_window > 0 | the time window in seconds before the request count is reset. |
| key | string | optional | "remote_addr" | ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name", "service_id"] | The user specified key to limit the count. <br /> Now accept those as key: "remote_addr"(client's IP), "server_addr"(server's IP), "X-Forwarded-For/X-Real-IP" in request header, "consumer_name"(consumer's username) and "service_id". |
| rejected_code | integer | optional | 503 | [200,...,599] | The HTTP status code returned when the request exceeds the threshold is rejected, default 503. |
| rejected_msg | string | optional | | non-empty | The `error_msg` returned when the request exceeds the threshold is rejected. |
| policy | string | optional | "local" | ["local", "redis", "redis-cluster"] | The rate-limiting policies to use for retrieving and incrementing the limits. Available values are `local`(the counters will be stored locally in-memory on the node), `redis`(counters are stored on a Redis server and will be shared across the nodes, usually use it to do the global speed limit), and `redis-cluster` which works the same as `redis` but with redis cluster. |
| allow_degradation | boolean | optional | false | | Whether to enable plugin degradation when the limit-count function is temporarily unavailable(e.g. redis timeout). Allow requests to continue when the value is set to true, default false. |
| show_limit_quota_header | boolean | optional | true | | Whether show `X-RateLimit-Limit` and `X-RateLimit-Remaining` (which mean the total number of requests and the remaining number of requests that can be sent) in the response header, default true. |
Expand Down Expand Up @@ -185,6 +186,18 @@ Server: APISIX web server
</html>
```

At the same time, if you set the property `error_msg` to `"Requests are too frequent, please try again later."` , when you visit for the third time, you will receive a response body like below:

```shell
HTTP/1.1 503 Service Temporarily Unavailable
Content-Type: text/html
Content-Length: 194
Connection: keep-alive
Server: APISIX web server

{"error_msg":"Requests are too frequent, please try again later."}
```

This means that the `limit count` plugin is in effect.

## Disable Plugin
Expand Down
13 changes: 13 additions & 0 deletions docs/en/latest/plugins/limit-req.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ limit request rate using the "leaky bucket" method.
| burst | integer | required | | burst >= 0 | the number of excessive requests per second allowed to be delayed. Requests exceeding this hard limit will get rejected immediately. |
| key | string | required | | ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"] | 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, "consumer_name"(consumer's username). |
| rejected_code | integer | optional | 503 | [200,...,599] | The HTTP status code returned when the request exceeds the threshold is rejected. |
| rejected_msg | string | optional | | non-empty | The `error_msg` returned when the request exceeds the threshold is rejected. |
okaybase marked this conversation as resolved.
Show resolved Hide resolved
| nodelay | boolean | optional | false | | If nodelay flag is true, bursted requests will not get delayed |
| allow_degradation | boolean | optional | false | | Whether to enable plugin degradation when the limit-req function is temporarily unavailable. Allow requests to continue when the value is set to true, default false. |

Expand Down Expand Up @@ -109,6 +110,18 @@ Server: APISIX web server
</html>
```

At the same time, you set the property `error_msg` to `"Requests are too frequent, please try again later."` , when you exceed, you will receive a response body like below:

```shell
HTTP/1.1 503 Service Temporarily Unavailable
Content-Type: text/html
Content-Length: 194
Connection: keep-alive
Server: APISIX web server

{"error_msg":"Requests are too frequent, please try again later."}
```

This means that the limit req plugin is in effect.

### How to enable on the `consumer`
Expand Down
1 change: 1 addition & 0 deletions docs/zh/latest/plugins/limit-conn.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ title: limit-conn
| only_use_default_delay | boolean | optional | false | [true,false] | 延迟时间的严格模式。 如果设置为`true`的话,将会严格按照设置的时间来进行延迟 |
| key | object | required | | ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"] | 用户指定的限制并发级别的关键字,可以是客户端 IP 或服务端 IP。<br />例如,可以使用主机名(或服务器区域)作为关键字,以便限制每个主机名的并发性。 否则,我们也可以使用客户端地址作为关键字,这样我们就可以避免单个客户端用太多的并行连接或请求淹没我们的服务。 <br />当前接受的 key 有:"remote_addr"(客户端 IP 地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For" 或 "X-Real-IP", "consumer_name"(consumer 的 username)。 |
| rejected_code | string | optional | 503 | [200,...,599] | 当请求超过 `conn` + `burst` 这个阈值时,返回的 HTTP 状态码 |
| rejected_msg | string | 可选 | | 非空 | 当请求超过 `conn` + `burst` 这个阈值时,返回的 `error_msg` 值。 |
okaybase marked this conversation as resolved.
Show resolved Hide resolved
| allow_degradation | boolean | 可选 | false | | 当插件功能临时不可用时是否允许请求继续。当值设置为 true 时则自动允许请求继续,默认值是 false。|

**注:key 是可以被用户自定义的,只需要修改插件的一行代码即可完成。并没有在插件中放开是处于安全的考虑。**
Expand Down
13 changes: 13 additions & 0 deletions docs/zh/latest/plugins/limit-count.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ title: limit-count
| time_window | integer | 必须 | | time_window > 0 | 时间窗口的大小(以秒为单位),超过这个时间就会重置 |
| key | string | 可选 | "remote_addr" | ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name", "service_id"] | 用来做请求计数的有效值。<br />例如,可以使用主机名(或服务器区域)作为关键字,以便限制每个主机名规定时间内的请求次数。我们也可以使用客户端地址作为关键字,这样我们就可以避免单个客户端规定时间内多次的连接我们的服务。<br />当前接受的 key 有:"remote_addr"(客户端 IP 地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For" 或 "X-Real-IP", "consumer_name"(consumer 的 username), "service_id" 。 |
| rejected_code | integer | 可选 | 503 | [200,...,599] | 当请求超过阈值被拒绝时,返回的 HTTP 状态码 |
| rejected_msg | string | 可选 | | 非空 | 当请求超过阈值被拒绝时,返回的 `error_msg` 值。 |
| policy | string | 可选 | "local" | ["local", "redis", "redis-cluster"] | 用于检索和增加限制的速率限制策略。可选的值有:`local`(计数器被以内存方式保存在节点本地,默认选项) 和 `redis`(计数器保存在 Redis 服务节点上,从而可以跨节点共享结果,通常用它来完成全局限速);以及`redis-cluster`,跟 redis 功能一样,只是使用 redis 集群方式。 |
| allow_degradation | boolean | 可选 | false | | 当限流插件功能临时不可用时(例如,Redis 超时)是否允许请求继续。当值设置为 true 时则自动允许请求继续,默认值是 false。|
| show_limit_quota_header | boolean | 可选 | true | | 是否在响应头中显示 `X-RateLimit-Limit` 和 `X-RateLimit-Remaining` (限制的总请求数和剩余还可以发送的请求数),默认值是 true。 |
Expand Down Expand Up @@ -189,6 +190,18 @@ Server: APISIX web server
</html>
```

同时,如果你设置了属性 `error_msg` 的值为 `"Requests are too frequent, please try again later."` ,当你第三次访问的时候,就会收到如下的响应体:

```shell
HTTP/1.1 503 Service Temporarily Unavailable
Content-Type: text/html
Content-Length: 194
Connection: keep-alive
Server: APISIX web server

{"error_msg":"Requests are too frequent, please try again later."}
```

这就表示 `limit count` 插件生效了。

## 移除插件
Expand Down
Loading