fix(ytmusic): reject stale cookie cache writes
This commit is contained in:
Vendored
+9
@@ -36,6 +36,7 @@ type cookieBase struct {
|
||||
mu sync.Mutex
|
||||
playlists []playlist.PlaylistInfo
|
||||
trackCache map[string][]playlist.Track
|
||||
generation uint64
|
||||
nextLoad uint64
|
||||
loadCancels map[uint64]context.CancelFunc
|
||||
}
|
||||
@@ -61,6 +62,7 @@ func (b *cookieBase) fetchPlaylists() ([]playlist.PlaylistInfo, error) {
|
||||
b.mu.Unlock()
|
||||
return res, nil
|
||||
}
|
||||
generation := b.generation
|
||||
|
||||
b.mu.Unlock()
|
||||
|
||||
@@ -77,7 +79,9 @@ func (b *cookieBase) fetchPlaylists() ([]playlist.PlaylistInfo, error) {
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
if b.generation == generation {
|
||||
b.playlists = pls
|
||||
}
|
||||
b.mu.Unlock()
|
||||
return pls, nil
|
||||
}
|
||||
@@ -88,6 +92,7 @@ func (b *cookieBase) fetchTracks(target string) ([]playlist.Track, error) {
|
||||
b.mu.Unlock()
|
||||
return cached, nil
|
||||
}
|
||||
generation := b.generation
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), cookiePlaylistLoadTimeout)
|
||||
b.nextLoad++
|
||||
@@ -122,13 +127,16 @@ func (b *cookieBase) fetchTracks(target string) ([]playlist.Track, error) {
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
if b.generation == generation {
|
||||
b.trackCache[target] = tracks
|
||||
}
|
||||
b.mu.Unlock()
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
func (b *cookieBase) refresh() {
|
||||
b.mu.Lock()
|
||||
b.generation++
|
||||
for _, cancel := range b.loadCancels {
|
||||
cancel()
|
||||
}
|
||||
@@ -140,6 +148,7 @@ func (b *cookieBase) refresh() {
|
||||
|
||||
func (b *cookieBase) close() {
|
||||
b.mu.Lock()
|
||||
b.generation++
|
||||
for _, cancel := range b.loadCancels {
|
||||
cancel()
|
||||
}
|
||||
|
||||
+54
@@ -335,6 +335,60 @@ func TestCookieProviderStopsTrackLoad(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCookieProviderRefreshRejectsStaleResults(t *testing.T) {
|
||||
t.Run("playlists", func(t *testing.T) {
|
||||
base := newCookieBase("firefox")
|
||||
started := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
base.fetchFn = func(string) ([]playlist.PlaylistInfo, error) {
|
||||
close(started)
|
||||
<-release
|
||||
return []playlist.PlaylistInfo{{ID: "private"}}, nil
|
||||
}
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
_, _ = base.fetchPlaylists()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
<-started
|
||||
base.refresh()
|
||||
close(release)
|
||||
<-done
|
||||
base.mu.Lock()
|
||||
defer base.mu.Unlock()
|
||||
if base.playlists != nil {
|
||||
t.Fatalf("stale playlists cached after refresh: %v", base.playlists)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("tracks", func(t *testing.T) {
|
||||
base := newCookieBase("firefox")
|
||||
started := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
base.resolveFn = func(context.Context, string, int, int, ...string) ([]playlist.Track, int, error) {
|
||||
close(started)
|
||||
<-release
|
||||
return []playlist.Track{{Title: "Private"}}, 1, nil
|
||||
}
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
_, _ = base.fetchTracks("private")
|
||||
close(done)
|
||||
}()
|
||||
|
||||
<-started
|
||||
base.refresh()
|
||||
close(release)
|
||||
<-done
|
||||
base.mu.Lock()
|
||||
defer base.mu.Unlock()
|
||||
if _, ok := base.trackCache["private"]; ok {
|
||||
t.Fatal("stale tracks cached after refresh")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCookieProviderSearchTracksHonorsCancellation(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("skipping Unix shell script test on Windows")
|
||||
|
||||
Reference in New Issue
Block a user