From f02730dc6b062243963dd26dfd49c02aed997101 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Mon, 5 Jan 2026 01:17:57 +1300 Subject: [PATCH 1/4] server: set Content-Length for tarballs --- server/server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/server.go b/server/server.go index fc95364..606649e 100644 --- a/server/server.go +++ b/server/server.go @@ -1,6 +1,7 @@ package server import ( + "fmt" "log" "os" "regexp" @@ -171,6 +172,8 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { } w.Header().Set("Content-Disposition", `attachment; filename="` + strings.ReplaceAll(album.Name, `"`, `'`) + `.tar.gz"`) + w.Header().Set("Content-Length", + fmt.Sprintf("%d", album.TarballSize)) w.Write(contents) return } From 06f296639edc176881c62eaf4888f822dbada4b6 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Mon, 5 Jan 2026 01:23:47 +1300 Subject: [PATCH 2/4] util: DoChunks: accept custom chunk sizes --- server/server.go | 2 +- util/util.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/server.go b/server/server.go index 606649e..f5059b9 100644 --- a/server/server.go +++ b/server/server.go @@ -108,7 +108,7 @@ func serveMediaDirectory(w http.ResponseWriter, req *http.Request) { http.Error(w, "not found", http.StatusNotFound) return } - util.DoChunks(f, func (buf []byte) { w.Write(buf) }) + util.DoChunks(f, 4, func (buf []byte) { w.Write(buf) }) } func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { diff --git a/util/util.go b/util/util.go index 79c32f6..55d3667 100644 --- a/util/util.go +++ b/util/util.go @@ -39,11 +39,11 @@ func Dirents(dir string) (entries []string, err error) { return } -func DoChunks(f io.Reader, fun func (buf []byte)) (err error) { +func DoChunks(f io.Reader, kib uint, fun func (buf []byte)) (err error) { err = nil bytes, chunks := int64(0), int64(0) r := bufio.NewReader(f) - buf := make([]byte, 0, 4*1024) + buf := make([]byte, 0, kib * 1024) for { var n int From 58b6b1618d54c4d4cbff8780357fa57b7848aaf1 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Mon, 5 Jan 2026 01:28:24 +1300 Subject: [PATCH 3/4] add missing File.Close() calls --- server/server.go | 2 ++ util/util.go | 1 + 2 files changed, 3 insertions(+) diff --git a/server/server.go b/server/server.go index f5059b9..f4f8000 100644 --- a/server/server.go +++ b/server/server.go @@ -103,11 +103,13 @@ func serveMediaDirectory(w http.ResponseWriter, req *http.Request) { http.Error(w, "is a directory; maybe you are looking for " + path, http.StatusForbidden) return } + f, err := os.Open(filePath) if err != nil { http.Error(w, "not found", http.StatusNotFound) return } + defer f.Close() util.DoChunks(f, 4, func (buf []byte) { w.Write(buf) }) } diff --git a/util/util.go b/util/util.go index 55d3667..77149eb 100644 --- a/util/util.go +++ b/util/util.go @@ -26,6 +26,7 @@ func Dirents(dir string) (entries []string, err error) { if err != nil { return nil, errors.New(err.Error()) } + defer d.Close() names, err := d.Readdirnames(0) if err != nil { From cf8959b1d738ecf47b8cc86bbaef41ac5c358045 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Mon, 5 Jan 2026 01:32:55 +1300 Subject: [PATCH 4/4] server: write tarball in 4 MiB chunks --- server/server.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/server.go b/server/server.go index f4f8000..ea8040d 100644 --- a/server/server.go +++ b/server/server.go @@ -166,17 +166,19 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { if musicindex.ArtistExists(artist) { album, ok := musicindex.FindArtist(artist).Albums[albumName] if ok { - contents, err := os.ReadFile(album.Tarball) - + f, err := os.Open(album.Tarball) if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) + http.Error(w, "internal server error: " + err.Error(), + http.StatusInternalServerError) return } + defer f.Close() + w.Header().Set("Content-Disposition", `attachment; filename="` + strings.ReplaceAll(album.Name, `"`, `'`) + `.tar.gz"`) w.Header().Set("Content-Length", fmt.Sprintf("%d", album.TarballSize)) - w.Write(contents) + util.DoChunks(f, 4 * 1024, func (buf []byte) { w.Write(buf) }) return } }