Files
ggjuku/internal/app/media_transport_parity_test.go
2026-09-30 10:20:46 +08:00

154 lines
6.5 KiB
Go

package app
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
)
func TestParityMediaCredentialsStayOnExactOrigin(t *testing.T) {
var requests int
d := rankingTestDownloader(t, func(request *http.Request) (*http.Response, error) {
requests++
if request.Header.Get("Sec-Fetch-Mode") != "cors" || request.Header.Get("Sec-Fetch-Dest") != "empty" || request.Header.Get("Referer") != "https://site.example.test/episode/1" {
t.Error("media request lost required headers")
}
response := rankingHTTPResponse(request, 200, "fixture")
if request.URL.Host == "media.example.test" {
if request.Header.Get("Cookie") != "Signed=fixture" {
t.Error("credential missing on its origin")
}
response.StatusCode = 302
response.Header.Set("Location", "https://other.example.test/segment")
} else if request.Header.Get("Cookie") != "" {
t.Error("signed credential leaked on redirect")
}
return response, nil
})
credentials := &providerMediaCredentials{origin: "https://media.example.test", cookie: "Signed=fixture", referer: "https://site.example.test/episode/1", expires: time.Now().Add(time.Minute)}
request, _ := http.NewRequestWithContext(providerMediaContext(context.Background(), credentials), http.MethodGet, "https://media.example.test/segment", nil)
response, err := d.doMediaRequest(request)
if err != nil {
t.Fatal(err)
}
response.Body.Close()
if requests != 2 || request.Header.Get("Cookie") != "" {
t.Fatal("redirect not followed or caller request mutated")
}
credentials.expires = time.Now().Add(-time.Second)
if _, err := d.doMediaRequest(request); err == nil || requests != 2 {
t.Fatal("expired credentials reached the network")
}
for _, address := range []string{"http://media.example.test/segment", "https://media.example.test:444/segment", "https://child.media.example.test/segment"} {
other, _ := http.NewRequest(http.MethodGet, address, nil)
other.Header.Set("Cookie", "Signed=fixture")
if err := credentials.apply(other); err != nil || other.Header.Get("Cookie") != "" {
t.Fatal("origin scope includes another scheme, port or subdomain")
}
}
}
func TestParityMediaProxiesKeepCredentialsForListsKeysAndSegments(t *testing.T) {
for _, gateway := range []bool{false, true} {
t.Run(fmt.Sprint(gateway), func(t *testing.T) {
seen := map[string]int{}
d := rankingTestDownloader(t, func(request *http.Request) (*http.Response, error) {
seen[request.URL.Path]++
if request.Header.Get("Cookie") != "Signed=fixture" || request.Header.Get("Sec-Fetch-Mode") != "cors" {
t.Error("proxy dropped credentials or media headers")
}
switch request.URL.Path {
case "/start.m3u8":
response := rankingHTTPResponse(request, 302, "")
response.Header.Set("Location", "/final/index.m3u8?sig=a%2Fb")
return response, nil
case "/final/index.m3u8":
return rankingHTTPResponse(request, 200, "#EXTM3U\n#EXT-X-KEY:METHOD=AES-128,URI=\"key\"\n#EXTINF:4,\nsegment.ts\n#EXT-X-ENDLIST\n"), nil
case "/final/key":
return rankingHTTPResponse(request, 200, "0123456789abcdef"), nil
case "/final/segment.ts":
return rankingHTTPResponse(request, 200, "synthetic-segment"), nil
}
return nil, fmt.Errorf("unexpected request: %s", request.URL.Path)
})
credentials := &providerMediaCredentials{origin: "https://media.example.test", cookie: "Signed=fixture", referer: "https://site.example.test/episode/1"}
ctx := providerMediaContext(context.Background(), credentials)
playlist, final, err := d.fetchMediaPlaylist(ctx, "https://media.example.test/start.m3u8", credentials.referer)
if err != nil || final != "https://media.example.test/final/index.m3u8?sig=a%2Fb" {
t.Fatal("redirect lost its final playlist base", final, err)
}
media := providerMedia{URL: final, Playlist: playlist, Referer: credentials.referer, credentials: credentials}
proxy, err := d.newHLSProxy(context.Background(), media, nil)
if err != nil {
t.Fatal(err)
}
defer proxy.Close()
root := proxy.root
if gateway {
proxy.Close()
session := &playbackMediaSession{media: media, plan: playbackMediaPlan{Player: "hls"}}
app := &UIApp{downloader: d}
app.attachMediaGateway(session, "/media/", "")
proxy = session.proxy
root = session.plan.URL
}
read := func(path string) string {
t.Helper()
writer := httptest.NewRecorder()
proxy.ServeHTTP(writer, httptest.NewRequest(http.MethodGet, path, nil))
if writer.Code != 200 {
t.Fatalf("proxy response %d: %s", writer.Code, writer.Body.String())
}
return writer.Body.String()
}
body := read(root)
key := hlsURIAttribute.FindStringSubmatch(body)
if len(key) != 2 || read(key[1]) != "0123456789abcdef" {
t.Fatal("rewritten key not readable")
}
segment := ""
for _, line := range strings.Split(body, "\n") {
if line != "" && !strings.HasPrefix(line, "#") {
segment = line
break
}
}
if segment == "" || read(segment) != "synthetic-segment" || seen["/final/key"] != 1 || seen["/final/segment.ts"] != 1 {
t.Fatal("rewritten segment not readable")
}
})
}
}
func TestParityPlaybackQualityInheritsMediaCredentials(t *testing.T) {
d := rankingTestDownloader(t, func(request *http.Request) (*http.Response, error) {
if request.Header.Get("Cookie") != "Signed=fixture" {
t.Error("variant lost credentials")
}
return rankingHTTPResponse(request, 200, "#EXTM3U\n#EXTINF:4,\nsegment.ts\n#EXT-X-ENDLIST\n"), nil
})
credentials := &providerMediaCredentials{origin: "https://media.example.test", cookie: "Signed=fixture"}
media := providerMedia{URL: "https://media.example.test/high.mp4", Referer: "https://site.example.test/", Quality: 1080, credentials: credentials,
Variants: []providerMedia{{URL: "https://media.example.test/low.m3u8", Quality: 720}}}
selected, err := d.selectPlaybackQuality(context.WithValue(context.Background(), playbackQualityKey{}, 720), media)
if err != nil || selected.credentials != credentials || selected.Referer != media.Referer || selected.Playlist == "" {
t.Fatal("selected variant lost session or playlist", err)
}
request, _ := http.NewRequest(http.MethodGet, "https://media.example.test:443/part", nil)
if providerMediaOrigin(request.URL) != credentials.origin {
t.Fatal("default port changed origin")
}
_, _, err = d.fetchMediaPlaylist(context.Background(), "file:///tmp/fixture", "")
if err == nil {
t.Fatal("non-HTTP playlist accepted")
}
_, _ = io.Discard.Write(nil)
_, _ = url.Parse("https://example.test")
}