check / check (push) Successful in 58s
POST /api/v1/reports stays unauthenticated but is bounded. Each client address, as the trusted-proxy logic resolves it, may send REPORTS_PER_MINUTE reports a minute (default 60, all at once if it likes), using golang.org/x/time/rate; past that it gets 429 with Retry-After. Buckets that have refilled are dropped once a minute, so idle addresses do not pile up. reportbuf refuses a report that would take the report files past DATA_DIR_MAX_BYTES (default 1 GiB) with ErrFull, answered with 507; the count starts from the files already in DATA_DIR, and reports not yet written count at their uncompressed size. CORS adds nothing unless CORS_ALLOWED_ORIGINS lists origins. A limit that is not a positive number stops the server from starting. Model: opus-5-5
47 lines
1.1 KiB
Go
47 lines
1.1 KiB
Go
package middleware
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/time/rate"
|
|
)
|
|
|
|
// TestAddressLimiterDropsOnlyFullBuckets checks the sweep in allow:
|
|
// a minute after the last one, it drops an address whose bucket has
|
|
// filled up again, and keeps one still short of tokens, whose limit
|
|
// would otherwise start over.
|
|
func TestAddressLimiterDropsOnlyFullBuckets(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const (
|
|
refilled = "198.51.100.1"
|
|
drained = "198.51.100.2"
|
|
)
|
|
|
|
// Two a minute: one token back every 30 seconds.
|
|
limiter := &addressLimiter{
|
|
burst: 2,
|
|
byAddr: make(map[string]*rate.Limiter),
|
|
limit: rate.Every(30 * time.Second),
|
|
}
|
|
|
|
start := time.Now()
|
|
|
|
// The first call sweeps the empty map and takes one of two tokens.
|
|
limiter.allow(refilled, start)
|
|
limiter.allow(drained, start.Add(59*time.Second))
|
|
limiter.allow(drained, start.Add(59*time.Second))
|
|
|
|
// A minute after the first sweep, this call sweeps again.
|
|
limiter.allow("198.51.100.3", start.Add(time.Minute))
|
|
|
|
if _, ok := limiter.byAddr[refilled]; ok {
|
|
t.Error("address with a full bucket was kept")
|
|
}
|
|
|
|
if _, ok := limiter.byAddr[drained]; !ok {
|
|
t.Error("address short of tokens was dropped")
|
|
}
|
|
}
|