servewaf always answers rproxy well within its time limit (closes #182)
check / check (push) Successful in 6m23s
check / check (push) Successful in 6m23s
A question waits on GeoJS at most 800ms, the new default of SWWAF_LOOKUP_TIMEOUT. It does not wait at all when the checks will refuse it whatever GeoJS answers. Just before the Core Rule Set, servewaf refuses more than 50 cookies or 100 header fields with 431. Shared default changed: SWWAF_LOOKUP_TIMEOUT also applies to smallwebwaf with no command. Judgement call: SWWAF_REPUTATION_TIMEOUT stays 2s, since no request waits on the DNS blocklists or AbuseIPDB. Model: opus-5-5
This commit is contained in:
@@ -228,6 +228,30 @@ func (l *Limiter) Count(
|
||||
return l.count(client, now, 1, 0, percent)
|
||||
}
|
||||
|
||||
// OverARateLimit reports whether one more request from client at now would
|
||||
// take it over a whole rate limit. A client's percentage can only lower a
|
||||
// limit, so that request is over the limit at any percentage. It counts
|
||||
// nothing, and does not make the client the most recently seen.
|
||||
func (l *Limiter) OverARateLimit(client netip.Prefix, now time.Time) bool {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
c, seen := l.clients.Peek(client)
|
||||
if !seen {
|
||||
return false
|
||||
}
|
||||
|
||||
buckets := c.buckets()
|
||||
|
||||
for i, w := range l.windows {
|
||||
if w.limit > 0 && buckets[i].Add(now, w.length, 0)+1 > float64(w.limit) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CountBytes counts bytes, those of a request from client that has ended,
|
||||
// at now, in every window, and returns the client's counts in each window.
|
||||
// It reports whether the bytes take the client over a byte limit, of which
|
||||
|
||||
@@ -26,6 +26,50 @@ const (
|
||||
hour = "hour"
|
||||
)
|
||||
|
||||
func TestOverARateLimitSaysWhatTheNextRequestWouldDoAndCountsNothing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := netip.MustParsePrefix("203.0.113.9/32")
|
||||
now := midnight()
|
||||
limiter := ratelimit.New(ratelimit.Limits{PerMinute: limit, PerDay: limit + 1},
|
||||
tableSize)
|
||||
|
||||
if limiter.OverARateLimit(client, now) {
|
||||
t.Error("a client never seen is over a rate limit")
|
||||
}
|
||||
|
||||
if limiter.Len() != 0 {
|
||||
t.Error("asking about a client never seen added it to the table")
|
||||
}
|
||||
|
||||
for range limit {
|
||||
if limiter.OverARateLimit(client, now) {
|
||||
t.Error("a client with room for another request is over a rate limit")
|
||||
}
|
||||
|
||||
wantCount(t, limiter, client, now, "")
|
||||
}
|
||||
|
||||
if !limiter.OverARateLimit(client, now) {
|
||||
t.Error("a client at its limit is not over a rate limit")
|
||||
}
|
||||
|
||||
held, _ := limiter.Client(client)
|
||||
if held.Minute.Current != limit {
|
||||
t.Errorf("the client's minute holds %d requests, want %d", held.Minute.Current, limit)
|
||||
}
|
||||
|
||||
wantCount(t, limiter, client, now, minute)
|
||||
|
||||
// A limit that is off is never gone over.
|
||||
off := ratelimit.New(ratelimit.Limits{}, tableSize)
|
||||
off.Count(client, now, whole)
|
||||
|
||||
if off.OverARateLimit(client, now) {
|
||||
t.Error("a client is over a rate limit that is off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEachWindowRefusesAtItsLimitAndLetsTheClientBack(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user