Fix panic on GET /chunked from integer overflow

HttpServerTimeout is a time.Duration (nanoseconds); the default-delay
branch scaled it by time.Second again, overflowing int64 to a negative
value and passing it to rand.Intn, which panics. The bare /chunked route
(no wait param) hit this on every request.

Convert the timeout to whole seconds in a guarded helper that keeps
rand.Intn's argument positive, and add a regression test.
This commit is contained in:
Stefan Prodan
2026-06-08 23:58:36 +03:00
parent 349544d100
commit 302c092cce
2 changed files with 30 additions and 1 deletions
+13 -1
View File
@@ -26,7 +26,7 @@ func (s *Server) chunkedHandler(w http.ResponseWriter, r *http.Request) {
delay, err := strconv.Atoi(vars["wait"])
if err != nil {
delay = rand.Intn(int(s.config.HttpServerTimeout*time.Second)-10) + 10
delay = randomDelaySeconds(s.config.HttpServerTimeout)
}
flusher, ok := w.(http.Flusher)
@@ -46,3 +46,15 @@ func (s *Server) chunkedHandler(w http.ResponseWriter, r *http.Request) {
flusher.Flush()
}
// randomDelaySeconds returns a random delay in seconds within [10, timeout),
// used when no explicit wait is provided. timeout is a time.Duration, so it is
// converted to whole seconds; the upper bound is clamped to keep rand.Intn's
// argument positive (it panics on a non-positive argument).
func randomDelaySeconds(timeout time.Duration) int {
maxDelay := int(timeout / time.Second)
if maxDelay <= 11 {
maxDelay = 12
}
return rand.Intn(maxDelay-10) + 10
}
+17
View File
@@ -5,6 +5,7 @@ import (
"net/http/httptest"
"regexp"
"testing"
"time"
)
func TestChunkedHandler(t *testing.T) {
@@ -33,3 +34,19 @@ func TestChunkedHandler(t *testing.T) {
rr.Body.String(), expected)
}
}
// TestRandomDelaySeconds covers the default-delay branch taken by the bare
// /chunked route (no {wait} value). This used to panic because the
// duration-to-seconds math overflowed int64 and handed rand.Intn a negative
// argument. Every timeout must yield a valid delay in [10, max] without panicking.
func TestRandomDelaySeconds(t *testing.T) {
timeouts := []time.Duration{30 * time.Second, 12 * time.Second, time.Second, 0, -1}
for _, timeout := range timeouts {
for range 100 {
d := randomDelaySeconds(timeout)
if d < 10 {
t.Fatalf("timeout %s: delay %d below floor of 10", timeout, d)
}
}
}
}