mirror of
https://github.com/truewhile/MeBox.git
synced 2026-11-12 19:34:24 +08:00
98 lines
3.6 KiB
Go
98 lines
3.6 KiB
Go
package service
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/truewhile/MeBox/internal/config"
|
|
)
|
|
|
|
func TestHLSPlaybackScopesIsolateSegmentReadsAndStop(t *testing.T) {
|
|
cfg := &config.Config{}
|
|
cfg.Cache.CacheDir = t.TempDir()
|
|
transcoder := NewTranscoderService(cfg, zap.NewNop(), nil, nil)
|
|
svc := NewStreamService(cfg, zap.NewNop(), nil, transcoder)
|
|
a := HLSPlaybackScope{UserID: "user-a", SessionID: "tab-a"}
|
|
b := HLSPlaybackScope{UserID: "user-b", SessionID: "tab-a"}
|
|
c := HLSPlaybackScope{UserID: "user-a", SessionID: "tab-c"}
|
|
for _, scope := range []HLSPlaybackScope{a, b, c} {
|
|
key := scope.jobKey("movie")
|
|
dir := filepath.Join(cfg.Cache.CacheDir, key, "run-current")
|
|
if err := os.MkdirAll(dir, 0o750); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "seg_00000.ts"), []byte(scope.UserID+"/"+scope.SessionID), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
transcoder.jobs[key] = &hlsJob{mediaID: "movie", jobKey: key, outputDir: dir, seekGen: 100, cancel: func() {}}
|
|
}
|
|
for _, scope := range []HLSPlaybackScope{a, b, c} {
|
|
req := httptest.NewRequest(http.MethodGet, "/api/hls/movie/seg_00000.ts?_seek=100", nil)
|
|
rec := httptest.NewRecorder()
|
|
if err := svc.ServeHLSSegment(rec, req, "movie", "seg_00000.ts", scope); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, want := rec.Body.String(), scope.UserID+"/"+scope.SessionID; got != want {
|
|
t.Fatalf("segment=%q, want %q", got, want)
|
|
}
|
|
}
|
|
transcoder.StopPlayback("movie", a)
|
|
if transcoder.jobs[a.jobKey("movie")] != nil {
|
|
t.Fatal("stopped scope is still active")
|
|
}
|
|
if transcoder.jobs[b.jobKey("movie")] == nil || transcoder.jobs[c.jobKey("movie")] == nil {
|
|
t.Fatal("stopping one player affected another user/tab")
|
|
}
|
|
// A delayed stop from a previous local-playback session must not affect a replacement.
|
|
transcoder.StopPlayback("movie", a)
|
|
if transcoder.jobs[c.jobKey("movie")] == nil {
|
|
t.Fatal("old stop cancelled a new session")
|
|
}
|
|
}
|
|
|
|
func TestHLSPlaybackRejectsStaleSegmentGeneration(t *testing.T) {
|
|
cfg := &config.Config{}
|
|
cfg.Cache.CacheDir = t.TempDir()
|
|
transcoder := NewTranscoderService(cfg, zap.NewNop(), nil, nil)
|
|
svc := NewStreamService(cfg, zap.NewNop(), nil, transcoder)
|
|
scope := HLSPlaybackScope{UserID: "viewer", SessionID: "tab"}
|
|
key := scope.jobKey("movie")
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, "seg_00000.ts"), []byte("new-generation"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
transcoder.jobs[key] = &hlsJob{mediaID: "movie", jobKey: key, outputDir: dir, seekGen: 200, cancel: func() {}}
|
|
req := httptest.NewRequest(http.MethodGet, "/api/hls/movie/seg_00000.ts?_seek=100", nil)
|
|
rec := httptest.NewRecorder()
|
|
if err := svc.ServeHLSSegment(rec, req, "movie", "seg_00000.ts", scope); err == nil {
|
|
t.Fatal("stale segment request read a replacement generation")
|
|
}
|
|
if rec.Body.Len() != 0 {
|
|
t.Fatal("stale request exposed replacement bytes")
|
|
}
|
|
}
|
|
|
|
func TestHLSPlaybackScopeRequiresServerOwner(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/?session_id=tab&user_id=attacker-controlled", nil)
|
|
if _, err := HLSPlaybackScopeForRequest(req, ""); err == nil {
|
|
t.Fatal("missing authenticated owner was accepted")
|
|
}
|
|
a, err := HLSPlaybackScopeForRequest(req, "real-owner")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if a.UserID != "real-owner" {
|
|
t.Fatal("client parameter supplied the owner")
|
|
}
|
|
req = httptest.NewRequest(http.MethodGet, "/?PlaySessionId=emby-session", nil)
|
|
b, err := HLSPlaybackScopeForRequest(req, "real-owner")
|
|
if err != nil || b.SessionID == "" {
|
|
t.Fatalf("Emby session must stay scoped: %#v %v", b, err)
|
|
}
|
|
}
|