From 6bc4d3d2f8bb9ad88c2586c3b50af787eb6f39e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesko=20Ansch=C3=BCtz?= Date: Mon, 23 Mar 2026 15:21:26 +0100 Subject: [PATCH] Fix: Protokoll-relative URLs, PDF-Fragment-Merge, Startup-Token-Cache, Test-Nil-Deref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - URL-Normalisierung überspringt jetzt //protocol-relative URLs - PDF-Viewer-Parameter werden mit bestehenden Fragments gemerged statt blind angehängt - /api/startup-token setzt Cache-Control: no-store (Server + Client) - Tote Goroutine mit ignoriertem net.Listen-Error aus TestAssetsServed entfernt Co-Authored-By: Claude Opus 4.6 --- player/agent/internal/app/app.go | 7 +++++-- player/agent/internal/playerserver/server.go | 21 +++++++++++++++++-- .../internal/playerserver/server_test.go | 14 ------------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/player/agent/internal/app/app.go b/player/agent/internal/app/app.go index 010fc39..45f7e68 100644 --- a/player/agent/internal/app/app.go +++ b/player/agent/internal/app/app.go @@ -349,8 +349,11 @@ func (a *App) fetchPlaylist(ctx context.Context) { } for i := range pr.Items { - if strings.HasPrefix(pr.Items[i].Src, "/") { - pr.Items[i].Src = a.Config.ServerBaseURL + pr.Items[i].Src + src := pr.Items[i].Src + // Nur echte relative Pfade prefixen (einzelnes /), nicht protokoll-relative + // URLs (//cdn.example.com/...) und keine absoluten URLs (http://, https://). + if strings.HasPrefix(src, "/") && !strings.HasPrefix(src, "//") { + pr.Items[i].Src = a.Config.ServerBaseURL + src } } diff --git a/player/agent/internal/playerserver/server.go b/player/agent/internal/playerserver/server.go index 6771cc7..ac4028c 100644 --- a/player/agent/internal/playerserver/server.go +++ b/player/agent/internal/playerserver/server.go @@ -108,6 +108,7 @@ func (s *Server) handleNowPlaying(w http.ResponseWriter, _ *http.Request) { // wurde und lädt die Seite automatisch neu. func (s *Server) handleStartupToken(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") + w.Header().Set("Cache-Control", "no-store") json.NewEncoder(w).Encode(map[string]string{"token": s.startupToken}) //nolint:errcheck } @@ -435,7 +436,23 @@ const playerHTML = ` } else { // type === 'web', 'pdf' oder unbekannt → iframe if (type === 'pdf') { - frame.src = item.src + '#toolbar=0&navpanes=0&scrollbar=0&view=Fit&page=1'; + frame.src = (function pdfUrl(src) { + var defaults = {toolbar: '0', navpanes: '0', scrollbar: '0', view: 'Fit', page: '1'}; + var hashIdx = src.indexOf('#'); + var base = hashIdx >= 0 ? src.substring(0, hashIdx) : src; + var existing = hashIdx >= 0 ? src.substring(hashIdx + 1) : ''; + var params = {}; + existing.split('&').forEach(function(p) { + var kv = p.split('='); + if (kv[0]) params[kv[0]] = kv[1] || ''; + }); + for (var k in defaults) { + if (!(k in params)) params[k] = defaults[k]; + } + var parts = []; + for (var k in params) parts.push(k + '=' + params[k]); + return base + '#' + parts.join('&'); + })(item.src); } else { if (frame.src !== item.src) { frame.src = item.src; } } @@ -603,7 +620,7 @@ const playerHTML = ` var knownStartupToken = null; function pollStartupToken() { - fetch('/api/startup-token') + fetch('/api/startup-token', {cache: 'no-store'}) .then(function(r) { return r.json(); }) .then(function(d) { if (!d || !d.token) return; diff --git a/player/agent/internal/playerserver/server_test.go b/player/agent/internal/playerserver/server_test.go index c97b1e5..13d7ee3 100644 --- a/player/agent/internal/playerserver/server_test.go +++ b/player/agent/internal/playerserver/server_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "io/fs" - "net" "net/http" "net/http/httptest" "strings" @@ -98,17 +97,6 @@ func TestHandleSysInfoReturnsItems(t *testing.T) { } func TestAssetsServed(t *testing.T) { - s := New("127.0.0.1:0", func() NowPlaying { return NowPlaying{} }) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - ready := make(chan string, 1) - go func() { - ln, _ := net.Listen("tcp", "127.0.0.1:0") - ready <- ln.Addr().String() - ln.Close() - }() - // Use httptest recorder to test asset handler directly via the embed FS. sub, err := fs.Sub(assetsFS, "assets") if err != nil { @@ -127,8 +115,6 @@ func TestAssetsServed(t *testing.T) { t.Errorf("GET /assets/%s Content-Type = %q, want image/png", name, ct) } } - _ = s - _ = ctx } func TestServerRunAndStop(t *testing.T) {