Skip to content
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
5 changes: 3 additions & 2 deletions apisix/plugins/limit-count.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ local _M = {
priority = 1002,
name = plugin_name,
schema = limit_count.schema,
metadata_schema = limit_count.metadata_schema,
}


function _M.check_schema(conf)
return limit_count.check_schema(conf)
function _M.check_schema(conf, schema_type)
return limit_count.check_schema(conf, schema_type)
end


Expand Down
54 changes: 46 additions & 8 deletions apisix/plugins/limit-count/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,30 @@ local group_conf_lru = core.lrucache.new({
type = 'plugin',
})

local metadata_defaults = {
limit_header = "X-RateLimit-Limit",
remaining_header = "X-RateLimit-Remaining",
reset_header = "X-RateLimit-Reset",
}

local metadata_schema = {
type = "object",
properties = {
limit_header = {
type = "string",
default = metadata_defaults.limit_header,
},
remaining_header = {
type = "string",
default = metadata_defaults.remaining_header,
},
reset_header = {
type = "string",
default = metadata_defaults.reset_header,
},
},
}

local schema = {
type = "object",
properties = {
Expand Down Expand Up @@ -91,7 +115,8 @@ local schema = {
local schema_copy = core.table.deepcopy(schema)

local _M = {
schema = schema
schema = schema,
metadata_schema = metadata_schema,
}


Expand All @@ -100,7 +125,12 @@ local function group_conf(conf)
end


function _M.check_schema(conf)

function _M.check_schema(conf, schema_type)
if schema_type == core.schema.TYPE_METADATA then
return core.schema.check(metadata_schema, conf)
end

local ok, err = core.schema.check(schema, conf)
if not ok then
return false, err
Expand Down Expand Up @@ -250,14 +280,22 @@ function _M.rate_limit(conf, ctx, name, cost)
delay, remaining, reset = lim:incoming(key, cost)
end

local metadata = apisix_plugin.plugin_metadata("limit-count")
if metadata then
metadata = metadata.value
else
metadata = metadata_defaults
end
core.log.info("limit-count plugin-metadata: ", core.json.delay_encode(metadata))

if not delay then
local err = remaining
if err == "rejected" then
-- show count limit header when rejected
if conf.show_limit_quota_header then
core.response.set_header("X-RateLimit-Limit", conf.count,
"X-RateLimit-Remaining", 0,
"X-RateLimit-Reset", reset)
core.response.set_header(metadata.limit_header, conf.count,
metadata.remaining_header, 0,
metadata.reset_header, reset)
end

if conf.rejected_msg then
Expand All @@ -274,9 +312,9 @@ function _M.rate_limit(conf, ctx, name, cost)
end

if conf.show_limit_quota_header then
core.response.set_header("X-RateLimit-Limit", conf.count,
"X-RateLimit-Remaining", remaining,
"X-RateLimit-Reset", reset)
core.response.set_header(metadata.limit_header, conf.count,
metadata.remaining_header, remaining,
metadata.reset_header, reset)
end
end

Expand Down
59 changes: 59 additions & 0 deletions docs/en/latest/plugins/limit-count.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,65 @@ Server: APISIX web server
{"error_msg":"Requests are too frequent, please try again later."}
```

### Customize Rate Limiting Headers

The following example demonstrates how you can use plugin metadata to customize the rate limiting response header names, which are by default `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`.

Configure the plugin metadata for this plugin and update the headers:

```shell
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/limit-count" -X PUT -d '
{
"log_format": {
"limit_header": "X-Custom-RateLimit-Limit",
"remaining_header": "X-Custom-RateLimit-Remaining",
"reset_header": "X-Custom-RateLimit-Reset"
}
}'
```

Create a route with `limit-count` plugin that allows for a quota of 1 within a 30-second window per remote address:

```shell
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "limit-count-route",
"uri": "/get",
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key_type": "var",
"key": "remote_addr",
"window_type": "sliding"
}
# highlight-end
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
```

Send a request to verify:

```shell
curl -i "http://127.0.0.1:9080/get"
```

You should receive an `HTTP/1.1 200 OK` response and see the following headers:

```text
X-Custom-RateLimit-Limit: 1
X-Custom-RateLimit-Remaining: 0
X-Custom-RateLimit-Reset: 28
```

## Delete Plugin

To remove the `limit-count` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect.
Expand Down
63 changes: 63 additions & 0 deletions t/plugin/limit-count5.t
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,66 @@ passed
["GET /hello", "GET /hello", "GET /hello", "GET /hello"]
--- error_code eval
[200, 200, 503, 503]



=== TEST 4: customize rate limit headers by plugin metadata
--- 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": 10,
"time_window": 60,
"rejected_code": 503,
"key_type": "var",
"key": "remote_addr"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say("fail")
return
end
local code, meta_body = t('/apisix/admin/plugin_metadata/limit-count',
ngx.HTTP_PUT,
[[{
"limit_header":"APISIX-RATELIMIT-QUOTA",
"remaining_header":"APISIX-RATELIMIT-REMAINING",
"reset_header":"APISIX-RATELIMIT-RESET"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say("fail")
return
end
ngx.say("passed")
}
}
--- response_body
passed



=== TEST 5: check rate limit headers
--- request
GET /hello
--- response_headers_like
APISIX-RATELIMIT-QUOTA: 10
APISIX-RATELIMIT-REMAINING: 9
APISIX-RATELIMIT-RESET: \d+
Loading