mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-27 21:30:23 +02:00
188 lines
5.0 KiB
Go
188 lines
5.0 KiB
Go
//go:build !ios
|
|
|
|
package gobackend
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/net/http2"
|
|
)
|
|
|
|
type fakePooledHTTP2Conn struct {
|
|
healthy bool
|
|
streamsActive int
|
|
blockShutdown bool
|
|
ignoreShutdownCtx bool
|
|
closeCount atomic.Int32
|
|
shutdownCount atomic.Int32
|
|
shutdownOnce sync.Once
|
|
shutdownDone chan struct{}
|
|
forceCloseUnblocked chan struct{}
|
|
forceCloseOnce sync.Once
|
|
}
|
|
|
|
func newFakePooledHTTP2Conn(healthy bool) *fakePooledHTTP2Conn {
|
|
return &fakePooledHTTP2Conn{
|
|
healthy: healthy,
|
|
shutdownDone: make(chan struct{}),
|
|
forceCloseUnblocked: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
func (c *fakePooledHTTP2Conn) RoundTrip(*http.Request) (*http.Response, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *fakePooledHTTP2Conn) ReserveNewRequest() bool { return c.healthy }
|
|
|
|
func (c *fakePooledHTTP2Conn) State() http2.ClientConnState {
|
|
if c.healthy {
|
|
return http2.ClientConnState{
|
|
StreamsActive: c.streamsActive,
|
|
MaxConcurrentStreams: 100,
|
|
}
|
|
}
|
|
return http2.ClientConnState{Closing: true, StreamsActive: c.streamsActive}
|
|
}
|
|
|
|
func (c *fakePooledHTTP2Conn) Close() error {
|
|
c.closeCount.Add(1)
|
|
c.forceCloseOnce.Do(func() { close(c.forceCloseUnblocked) })
|
|
return nil
|
|
}
|
|
|
|
func (c *fakePooledHTTP2Conn) Shutdown(ctx context.Context) error {
|
|
c.shutdownCount.Add(1)
|
|
c.shutdownOnce.Do(func() { close(c.shutdownDone) })
|
|
if c.ignoreShutdownCtx {
|
|
<-c.forceCloseUnblocked
|
|
return context.DeadlineExceeded
|
|
}
|
|
if c.blockShutdown {
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func waitForShutdown(t *testing.T, conn *fakePooledHTTP2Conn) {
|
|
t.Helper()
|
|
select {
|
|
case <-conn.shutdownDone:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("connection did not begin graceful shutdown")
|
|
}
|
|
}
|
|
|
|
func TestUTLSPoolRetiresStaleCachedConnection(t *testing.T) {
|
|
transport := newUTLSTransport()
|
|
stale := newFakePooledHTTP2Conn(false)
|
|
transport.conns["example:443"] = stale
|
|
|
|
if got := transport.cachedConn("example:443"); got != nil {
|
|
t.Fatalf("cachedConn returned stale connection: %#v", got)
|
|
}
|
|
waitForShutdown(t, stale)
|
|
if stale.closeCount.Load() != 0 {
|
|
t.Fatalf("gracefully retired connection was force closed")
|
|
}
|
|
if _, exists := transport.conns["example:443"]; exists {
|
|
t.Fatal("stale connection was not removed")
|
|
}
|
|
}
|
|
|
|
func TestUTLSPoolStoreClosesDiscardedAndRetiresReplacedConnection(t *testing.T) {
|
|
transport := newUTLSTransport()
|
|
healthy := newFakePooledHTTP2Conn(true)
|
|
transport.conns["example:443"] = healthy
|
|
fresh := newFakePooledHTTP2Conn(true)
|
|
|
|
if got := transport.storeConn("example:443", fresh); got != healthy {
|
|
t.Fatal("healthy pooled connection was not reused")
|
|
}
|
|
if fresh.closeCount.Load() != 1 {
|
|
t.Fatalf("discarded fresh close count = %d", fresh.closeCount.Load())
|
|
}
|
|
|
|
stale := newFakePooledHTTP2Conn(false)
|
|
transport.conns["example:443"] = stale
|
|
replacement := newFakePooledHTTP2Conn(true)
|
|
if got := transport.storeConn("example:443", replacement); got != replacement {
|
|
t.Fatal("stale connection was not replaced")
|
|
}
|
|
waitForShutdown(t, stale)
|
|
if stale.closeCount.Load() != 0 {
|
|
t.Fatalf("replaced connection was force closed")
|
|
}
|
|
}
|
|
|
|
func TestUTLSPoolInvalidateRetiresOnlyRequestedConnection(t *testing.T) {
|
|
transport := newUTLSTransport()
|
|
current := newFakePooledHTTP2Conn(true)
|
|
old := newFakePooledHTTP2Conn(false)
|
|
transport.conns["example:443"] = current
|
|
|
|
transport.invalidate("example:443", old)
|
|
if transport.conns["example:443"] != current {
|
|
t.Fatal("invalidating an old connection removed the replacement")
|
|
}
|
|
if old.shutdownCount.Load() != 0 {
|
|
t.Fatal("connection already removed from the pool was retired again")
|
|
}
|
|
|
|
transport.invalidate("example:443", current)
|
|
waitForShutdown(t, current)
|
|
if _, exists := transport.conns["example:443"]; exists {
|
|
t.Fatal("invalidated current connection remained in the pool")
|
|
}
|
|
}
|
|
|
|
func TestUTLSPoolCloseIdleUsesBoundedShutdown(t *testing.T) {
|
|
transport := newUTLSTransport()
|
|
conn := newFakePooledHTTP2Conn(true)
|
|
transport.conns["example:443"] = conn
|
|
|
|
transport.closeIdleConnections()
|
|
if len(transport.conns) != 0 {
|
|
t.Fatal("pool was not cleared synchronously")
|
|
}
|
|
select {
|
|
case <-conn.shutdownDone:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("pooled connection was not shut down")
|
|
}
|
|
if conn.shutdownCount.Load() != 1 {
|
|
t.Fatalf("shutdown count = %d", conn.shutdownCount.Load())
|
|
}
|
|
}
|
|
|
|
func TestUTLSPoolForcesCloseWhenGracefulShutdownTimesOut(t *testing.T) {
|
|
conn := newFakePooledHTTP2Conn(true)
|
|
conn.ignoreShutdownCtx = true
|
|
|
|
retirePooledHTTP2ConnWithTimeout(conn, 20*time.Millisecond)
|
|
waitForShutdown(t, conn)
|
|
|
|
deadline := time.After(time.Second)
|
|
for conn.closeCount.Load() == 0 {
|
|
select {
|
|
case <-deadline:
|
|
t.Fatal("timed-out graceful shutdown did not force close")
|
|
case <-time.After(10 * time.Millisecond):
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUTLSPoolDoesNotPutADeadlineOnActiveStreams(t *testing.T) {
|
|
conn := newFakePooledHTTP2Conn(false)
|
|
conn.streamsActive = 1
|
|
if timeout := pooledHTTP2RetirementTimeout(conn.State()); timeout != 0 {
|
|
t.Fatalf("active connection retirement timeout = %v", timeout)
|
|
}
|
|
}
|