fix(stream): self-heal host→container path skew in HLS + sidecar handlers
On a docker agent the web DB holds host paths (e.g. /mnt/nas/peliculas/…) while the container mounts that media at /downloads, so the runtime allowed root (cfg.Download.Dir=/downloads) rejects the host path. The raw /stream handler already self-heals via relocateUnreachable, but the HLS/remux session handler did not — it logged "path outside allowed dirs" and returned, so the web silently fell back to the raw /stream path (no transcode, slow funnel start) and HLS/remux never ran. The path-scoped sidecar handlers (/thumbnail, /trickplay, /sub) had the same skew → 404 for every scrubber frame, trickplay sprite and external subtitle. - HLS handler (OnStreamSession): apply the same relocateUnreachable remap as the raw handler before the dir-resolve. - StreamServer: add SetPathResolver/healMediaPath, applied in /thumbnail, /trickplay, /sub AFTER token verification (the token still binds the original web path; the resolver is a pure function of that path and re-validates containment, so it can't be abused to serve a different file). - Hoist the allowed-roots list into streamAllowedRoots(cfg) so the raw, HLS and sidecar handlers can't drift apart. Note: relocateUnreachable needs a ≥3-segment path tail, so flat media layouts are not self-healed (same limitation as /stream; a re-scan rewrites the DB path). The HLS handler replicates only the lexical remap, not the raw handler's transient-NFS os.Stat retry.
This commit is contained in:
parent
d44f16cae2
commit
d97ca11fa5
3 changed files with 112 additions and 7 deletions
|
|
@ -35,6 +35,33 @@ func (f *fakeFileProviderSeekable) NewFileReader(_ context.Context) io.ReadSeekC
|
|||
return &readSeekNopCloser{strings.NewReader(string(f.content))}
|
||||
}
|
||||
|
||||
// TestStreamServer_healMediaPath covers the host→container base-path self-heal
|
||||
// used by the path-scoped handlers (/thumbnail, /trickplay, /sub).
|
||||
func TestStreamServer_healMediaPath(t *testing.T) {
|
||||
srv := NewStreamServer(0)
|
||||
|
||||
// No resolver installed → identity (preserves the pre-fix 404 behaviour).
|
||||
if got := srv.healMediaPath("/mnt/nas/peliculas/a/b/c.mkv"); got != "/mnt/nas/peliculas/a/b/c.mkv" {
|
||||
t.Errorf("nil resolver should be identity, got %q", got)
|
||||
}
|
||||
|
||||
// Resolver locates the file under a current root → use the healed path.
|
||||
srv.SetPathResolver(func(p string) string {
|
||||
if p == "/mnt/nas/peliculas/a/b/c.mkv" {
|
||||
return "/downloads/a/b/c.mkv"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
if got := srv.healMediaPath("/mnt/nas/peliculas/a/b/c.mkv"); got != "/downloads/a/b/c.mkv" {
|
||||
t.Errorf("resolver remap: got %q want /downloads/a/b/c.mkv", got)
|
||||
}
|
||||
|
||||
// Resolver can't locate it ("") → keep the original so os.Stat 404s as before.
|
||||
if got := srv.healMediaPath("/elsewhere/x.mkv"); got != "/elsewhere/x.mkv" {
|
||||
t.Errorf("unlocatable path should stay unchanged, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStreamServer_Listen_BindsPort verifica que Listen() enlaza a un puerto
|
||||
// y URL() devuelve una URL accesible.
|
||||
func TestStreamServer_Listen_BindsPort(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue