diff --git a/apisix/plugins/limit-req.lua b/apisix/plugins/limit-req.lua index 8b87634600f2..2577f7cee86e 100644 --- a/apisix/plugins/limit-req.lua +++ b/apisix/plugins/limit-req.lua @@ -36,6 +36,9 @@ local schema = { rejected_code = { type = "integer", minimum = 200, maximum = 599, default = 503 }, + nodelay = { + type = "boolean", default = false + }, }, required = {"rate", "burst", "key"} } @@ -96,7 +99,7 @@ function _M.access(conf, ctx) return 500 end - if delay >= 0.001 then + if delay >= 0.001 and not conf.nodelay then sleep(delay) end end diff --git a/docs/en/latest/plugins/limit-req.md b/docs/en/latest/plugins/limit-req.md index f160eaa4176c..977dfb4cd17f 100644 --- a/docs/en/latest/plugins/limit-req.md +++ b/docs/en/latest/plugins/limit-req.md @@ -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. | +| nodelay | boolean | optional | false | | If nodelay flag is true, bursted requests will not get delayed | **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.** diff --git a/docs/zh/latest/plugins/limit-req.md b/docs/zh/latest/plugins/limit-req.md index 2aa6bd365d7e..891cec6e22c1 100644 --- a/docs/zh/latest/plugins/limit-req.md +++ b/docs/zh/latest/plugins/limit-req.md @@ -42,6 +42,7 @@ title: limit-req | burst | integer | 必须 | | burst >= 0 | t请求速率超过 (`rate` + `brust`)的请求会被直接拒绝。 | | key | string | 必须 | | ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"] | 用来做请求计数的依据,当前接受的 key 有:"remote_addr"(客户端IP地址), "server_addr"(服务端 IP 地址), 请求头中的"X-Forwarded-For" 或 "X-Real-IP","consumer_name"(consumer 的 username)。 | | rejected_code | integer | 可选 | 503 | [200,...,599] | 当请求超过阈值被拒绝时,返回的 HTTP 状态码。 | +| nodelay | boolean | 可选 | false | | 如果 nodelay 为 true, 请求速率超过 `rate` 但没有超过 (`rate` + `brust`)的请求不会加上延迟, 如果是 false,则会加上延迟。 | **key 是可以被用户自定义的,只需要修改插件的一行代码即可完成。并没有在插件中放开是处于安全的考虑。** diff --git a/t/plugin/limit-req2.t b/t/plugin/limit-req2.t new file mode 100644 index 000000000000..9ed83e763c31 --- /dev/null +++ b/t/plugin/limit-req2.t @@ -0,0 +1,121 @@ +# +# 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'; + +log_level('info'); +repeat_each(1); +no_long_string(); +no_shuffle(); +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: add plugin for delay test +--- 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-req": { + "rate": 0.1, + "burst": 4, + "rejected_code": 503, + "key": "remote_addr" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "desc": "upstream_node", + "uri": "/hello*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 2: the second request will timeout because of delay, error code will be '' +--- abort +--- timeout: 500ms +--- pipelined_requests eval +["GET /hello", "GET /hello"] +--- error_code eval +[200, ''] + + + +=== TEST 3: add nodelay flag +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1/plugins', + ngx.HTTP_PATCH, + [[{ + "limit-req": { + "rate": 0.1, + "burst": 4, + "rejected_code": 503, + "key": "remote_addr", + "nodelay": true + } + }]] + ) + + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 4: the second request will not timeout because of nodelay +--- abort +--- timeout: 500ms +--- pipelined_requests eval +["GET /hello", "GET /hello"] +--- error_code eval +[200, 200]