Files
SpotiFLAC-Mobile/go_backend/extension_runtime_stall_test.go
T
zarzet 2fa4aa5b70 perf(network): mobile stability and data-usage fixes from network audit
Stability:
- transient timeouts no longer classified as ISP blocking; they fall
  through to retry backoff (hard blocks - DNS/RST/cert - still abort)
- connectivity change now closes idle Go connections in every network
  mode (debounced), so pooled sockets from the old interface are not
  reused after a wifi/cellular handoff
- download body reads get a 60s stall watchdog that cancels and
  surfaces a retryable network error instead of hanging; distinct
  from user cancellation
- ResponseHeaderTimeout 45s on all transports; IdleConnTimeout 90->60s;
  dial timeout 30->10s; retry backoff gains full jitter; Retry-After
  honored on 5xx and Deezer 429

Data usage / speed:
- cover downloads deduplicated with singleflight plus a 24MB/15min
  in-memory cache keyed by final URL (album batches fetched the same
  1800px cover once per track before)
- song.link availability cached (30min positive / 5min negative)
  in front of the 9-req/min rate limiter
- uTLS Cloudflare path now pools one HTTP/2 connection per host with
  a shared TLS session cache instead of a full handshake per request

Deezer artist track-count N+1 kept: counts feed the discography
download UI and are already amortized by the artist TTL cache.
2026-07-13 08:39:05 +07:00

55 lines
1.3 KiB
Go

package gobackend
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
)
// TestStallWatchdogCancelsOnNoData verifies the watchdog aborts a transfer that
// stops sending bytes, marks itself stalled (not user-cancelled), and does so
// after resetting on the initial byte.
func TestStallWatchdogCancelsOnNoData(t *testing.T) {
block := make(chan struct{})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", "1000")
w.WriteHeader(http.StatusOK)
w.Write([]byte("x"))
w.(http.Flusher).Flush()
<-block // simulate a dead radio mid-transfer: stop sending
}))
defer srv.Close()
defer close(block)
req, _ := http.NewRequestWithContext(context.Background(), "GET", srv.URL, nil)
req, wd := bindStallWatchdog(req, 150*time.Millisecond)
defer wd.stop()
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("do: %v", err)
}
defer resp.Body.Close()
buf := make([]byte, 32)
var readErr error
for {
n, er := resp.Body.Read(buf)
if n > 0 {
wd.reset()
}
if er != nil {
readErr = er
break
}
}
if readErr == nil {
t.Fatal("expected read error from stall cancel")
}
if !wd.stalled.Load() {
t.Fatalf("watchdog did not mark stalled; err=%v", readErr)
}
}