Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
808245e7d5 |
@@ -412,8 +412,9 @@ refusal comes with `SWWAF_ALLOW_NETS` in milestone 3 or later.
|
|||||||
the checks, passes the request to the app and the answer back with the
|
the checks, passes the request to the app and the answer back with the
|
||||||
standard library's `httputil.ReverseProxy` within the timeouts and size
|
standard library's `httputil.ReverseProxy` within the timeouts and size
|
||||||
limits, and writes the request's log line. Its `check` method is where a
|
limits, and writes the request's log line. Its `check` method is where a
|
||||||
request is refused before anything reaches the app: for a rate limit, and,
|
request is refused before anything reaches the app: for a rate limit, for an
|
||||||
with the rest of milestone 2, for the country lists.
|
announced body over the size limit, and, with the rest of milestone 2, for the
|
||||||
|
country lists.
|
||||||
- `internal/ratelimit`: counts each client's requests and tells when one takes
|
- `internal/ratelimit`: counts each client's requests and tells when one takes
|
||||||
it over a rate limit.
|
it over a rate limit.
|
||||||
- `internal/requestlog`: the lines on stdout: the request log line and the
|
- `internal/requestlog`: the lines on stdout: the request log line and the
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func TestRateLimitRefusesWith429BeforeTheApp(t *testing.T) {
|
|||||||
{client, http.StatusTooManyRequests},
|
{client, http.StatusTooManyRequests},
|
||||||
{"203.0.113.10", http.StatusOK},
|
{"203.0.113.10", http.StatusOK},
|
||||||
{"2001:db8::1", http.StatusOK},
|
{"2001:db8::1", http.StatusOK},
|
||||||
{"2001:db8::ffff:2", http.StatusTooManyRequests},
|
{"2001:db8::8000:0:0:1", http.StatusTooManyRequests},
|
||||||
{"2001:db8:0:1::1", http.StatusOK},
|
{"2001:db8:0:1::1", http.StatusOK},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,9 +101,16 @@ type buckets struct {
|
|||||||
// under way, and those in the bucket before it weighted by how much of
|
// under way, and those in the bucket before it weighted by how much of
|
||||||
// that bucket the window still covers.
|
// that bucket the window still covers.
|
||||||
//
|
//
|
||||||
// Concurrent requests can be counted out of order, so now can be before
|
// Concurrent requests can be counted out of order, so now can be a moment
|
||||||
// the bucket under way began; such a request is counted in that bucket.
|
// before the bucket under way began; such a request is counted in that
|
||||||
|
// bucket. A request dated more than a second before it means the clock
|
||||||
|
// was set back, and the buckets start afresh: otherwise the bucket before
|
||||||
|
// would keep its full weight until the clock caught up.
|
||||||
func (b *buckets) add(now time.Time, length time.Duration) float64 {
|
func (b *buckets) add(now time.Time, length time.Duration) float64 {
|
||||||
|
if now.Before(b.start.Add(-time.Second)) {
|
||||||
|
*b = buckets{}
|
||||||
|
}
|
||||||
|
|
||||||
start := now.Truncate(length)
|
start := now.Truncate(length)
|
||||||
if start.After(b.start) {
|
if start.After(b.start) {
|
||||||
if start.Equal(b.start.Add(length)) {
|
if start.Equal(b.start.Add(length)) {
|
||||||
|
|||||||
@@ -87,6 +87,48 @@ func TestRefusedRequestsCount(t *testing.T) {
|
|||||||
wantCount(t, limiter, within, later, "")
|
wantCount(t, limiter, within, later, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequestCountedLateGoesInTheBucketUnderWay(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
limiter := ratelimit.New(ratelimit.Limits{PerMinute: limit})
|
||||||
|
client := netip.MustParsePrefix("203.0.113.9/32")
|
||||||
|
start := midnight()
|
||||||
|
|
||||||
|
for range limit {
|
||||||
|
wantCount(t, limiter, client, start, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A concurrent request dated a moment before the bucket under way, but
|
||||||
|
// counted after it began, is counted in it: 3 + 1 is over the limit.
|
||||||
|
wantCount(t, limiter, client, start.Add(-time.Millisecond), minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClockSetBackStartsTheBucketsAfresh(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
limiter := ratelimit.New(ratelimit.Limits{PerHour: limit})
|
||||||
|
client := netip.MustParsePrefix("203.0.113.9/32")
|
||||||
|
start := midnight()
|
||||||
|
|
||||||
|
for range limit {
|
||||||
|
wantCount(t, limiter, client, start, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Half an hour into the next bucket: 3 / 2 + 1 is within the limit.
|
||||||
|
wantCount(t, limiter, client, start.Add(time.Hour+time.Hour/2), "")
|
||||||
|
|
||||||
|
// The clock is set back an hour. Counted in the bucket under way, the
|
||||||
|
// next request would find the bucket before it at full weight, 3 + 2,
|
||||||
|
// over the limit until the clock caught up. The buckets start afresh
|
||||||
|
// instead, and the client is refused only past the limit again.
|
||||||
|
setBack := start.Add(time.Hour / 2)
|
||||||
|
for range limit {
|
||||||
|
wantCount(t, limiter, client, setBack, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
wantCount(t, limiter, client, setBack, hour)
|
||||||
|
}
|
||||||
|
|
||||||
func TestKeepsAtMost20000ClientsDroppingTheLeastRecentlySeen(t *testing.T) {
|
func TestKeepsAtMost20000ClientsDroppingTheLeastRecentlySeen(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user