-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
rate-limit.lua
98 lines (84 loc) · 2.92 KB
/
rate-limit.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
--
-- Copyright (c) 2024 Alexander Todorov <atodorov@otb.bg>
--
-- Licensed under GNU Affero General Public License v3 or later (AGPLv3+)
-- https://www.gnu.org/licenses/agpl-3.0.html
--
local function getenv(var_name, default)
local value = os.getenv(var_name)
if value == nil then
return default
end
-- WARNING: explicitly convert to a number
return tonumber(value)
end
local function startswith(self, start)
return self:sub(1, #start) == start
end
-- REMEMBER: these are per IP address, not total
-- Configuration values
-- key = zone-name, rate/s, burst/s
local LIMITS = {
authenticated = {
"limit_storage_authenticated",
getenv("NGX_AUTHENTICATED_RATE", 300),
getenv("NGX_AUTHENTICATED_BURST", 100),
},
errors = {
"limit_storage_errors",
getenv("NGX_ERRORS_RATE", 0.02), -- 1r/m
getenv("NGX_ERRORS_BURST", 1),
},
-- WARNING: limits for static files should be much higher than /acounts/login/
-- and other authenticated requests otherwise may block the client and
-- result in the entire page being rate limited
static = {
"limit_storage_static",
getenv("NGX_STATIC_RATE", 300),
getenv("NGX_STATIC_BURST", 100),
},
uploads = {
"limit_storage_uploads",
getenv("NGX_UPLOADS_RATE", 10),
getenv("NGX_UPLOADS_BURST", 10),
},
}
-- WARNING: must be the same as limit_req_status in Nginx
-- otherwise our testing tools will count wrong
local NGX_LIMIT_REQ_STATUS = 429
-- WARNING: should always be set but error check anyway
local config_key = ngx.var.rate_limit_config_key
if LIMITS[config_key] == nil then
ngx.log(ngx.ERR, "Cannot find rate limit configuration for: ", ngx.var.request_uri)
-- don't crash everything in case we have an error in the rate-limit config
return
end
local ZONE_NAME, RATE, BURST = table.unpack(LIMITS[config_key])
local limit_module = require "resty.limit.req"
local bucket, err = limit_module.new(ZONE_NAME, RATE, BURST)
if not bucket then
ngx.log(ngx.ERR,
"Failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
-- the following call must be per-request.
local delay, err = bucket:incoming(ngx.var.binary_remote_addr, true)
if not delay then
if err == "rejected" then
return ngx.exit(NGX_LIMIT_REQ_STATUS)
end
ngx.log(ngx.ERR, "Failed to limit request: ", err)
return ngx.exit(500)
end
-- NOTE: we operate in a no-delay fashion for now
-- if delay >= 0.001 then
-- the 2nd return value holds the number of excess requests
-- per second for the specified key. for example, number 31
-- means the current request rate is at 231 req/sec for the
-- specified key.
-- local excess = err
-- the request exceeding the 200 req/sec but below 300 req/sec,
-- so we intentionally delay it here a bit to conform to the
-- 200 req/sec rate.
-- ngx.sleep(delay)
-- end