Per-client request rate limits over a minute, an hour and a day (closes #43)
check / check (push) Successful in 2m20s
check / check (push) Successful in 2m20s
Each client, one IPv4 address or one IPv6 /64, is counted in two buckets per window, the earlier weighted by how much of it the window covers; at most 20,000 clients are kept, least recently seen dropped first. A request over SWWAF_RATE_LIMIT_PER_MINUTE, _HOUR or _DAY (1000, 10000, 50000, or off) gets 429 before reaching the app. Refused requests count, 413s included. A clock set back over a second behind a bucket's start restarts that window. The log line gains limit_hit and the action rate_limited. Deviation from SPEC.md, per the issue: the 20,000 bound and /64 are fixed. Judgement call: golang-lru/v2 holds the table; httprate does not count refused requests. Deviation: go.mod and go.sum hand-written; no make target tidies them. Model: opus-5-5
This commit is contained in:
@@ -23,6 +23,9 @@ const (
|
||||
// ActionUpstreamError is a request the app could not be reached
|
||||
// for, or whose answer could not be passed on.
|
||||
ActionUpstreamError = "upstream_error"
|
||||
// ActionRateLimited is a request refused because it took its client
|
||||
// over a rate limit, or came while the client was over one.
|
||||
ActionRateLimited = "rate_limited"
|
||||
)
|
||||
|
||||
// timeLayout is RFC 3339 with milliseconds.
|
||||
@@ -49,6 +52,9 @@ type Line struct {
|
||||
Referer string `json:"referer"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
Action string `json:"action"`
|
||||
// LimitHit is the window whose rate limit the request went over:
|
||||
// minute, hour or day.
|
||||
LimitHit string `json:"limit_hit,omitempty"`
|
||||
// Aborted is true when the client went away early.
|
||||
Aborted bool `json:"aborted,omitempty"`
|
||||
// DurationTotal and DurationUpstreamTotal are in milliseconds.
|
||||
|
||||
Reference in New Issue
Block a user