fix(stream): keep the headroom in the estimated content length

5686c352c changed the estimate from bitRate / 8 * 1024 to * 1000, reading the
1024 as a unit mistake. It was headroom: the body also carries the ID3 tag, the
Xing frame and frame padding, so a nominal-size estimate is short, and net/http
aborts a response that writes past its declared Content-Length. Measured on a
live server, a 429s track at 64kbps was estimated at 3,439,840 bytes against a
3,441,569-byte body, and the stream was cut with 'http: wrote more than the
declared Content-Length'.

Restores the 1024, says why next to it, and turns the test into one that
asserts the headroom rather than a formula.
This commit is contained in:
Deluan 2026-09-24 17:05:05 -04:00
commit 79fc6fed60
2 changed files with 9 additions and 3 deletions

View file

@ -146,8 +146,11 @@ func (s *Stream) Duration() float32 { return s.mf.Duration }
func (s *Stream) ContentType() string { return mime.TypeByExtension("." + s.format) }
func (s *Stream) Name() string { return s.mf.Title + "." + s.format }
func (s *Stream) ModTime() time.Time { return s.mf.UpdatedAt }
// EstimatedContentLength deliberately overshoots by 2.4%: the body also carries tags and
// padding, and net/http aborts a response that writes past its declared length.
func (s *Stream) EstimatedContentLength() int {
return int(s.mf.Duration * float32(s.bitRate) / 8 * 1000)
return int(s.mf.Duration * float32(s.bitRate) / 8 * 1024)
}
// Serve writes the stream to the HTTP response. For seekable streams it uses http.ServeContent

View file

@ -8,6 +8,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"strconv"
"testing/iotest"
"time"
@ -184,7 +185,7 @@ var _ = Describe("MediaStreamer", func() {
Expect(err).To(HaveOccurred())
})
It("estimates the content length from a bitrate in kilobits of 1000 bits", func() {
It("estimates a content length above the nominal size, so the body never outruns it", func() {
hundredSeconds := *mf
hundredSeconds.Duration = 100
s := stream.NewStream(&hundredSeconds, "mp3", 128, io.NopCloser(bytes.NewReader(nil)))
@ -193,7 +194,9 @@ var _ = Describe("MediaStreamer", func() {
_, _ = s.Serve(ctx, w, r)
Expect(w.Header().Get("Content-Length")).To(Equal("1600000"))
length, err := strconv.Atoi(w.Header().Get("Content-Length"))
Expect(err).ToNot(HaveOccurred())
Expect(length).To(BeNumerically(">", 100*128*1000/8))
})
})
})