aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/handlers_media.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/web/handlers_media.go')
-rw-r--r--internal/web/handlers_media.go13
1 files changed, 13 insertions, 0 deletions
diff --git a/internal/web/handlers_media.go b/internal/web/handlers_media.go
index 7c4b739..99fdb64 100644
--- a/internal/web/handlers_media.go
+++ b/internal/web/handlers_media.go
@@ -4,6 +4,7 @@ import (
"errors"
"io/fs"
"net/http"
+ "strings"
"github.com/google/uuid"
"github.com/gramanas/blogspace/internal/store"
@@ -38,9 +39,21 @@ func (s *Server) handleMedia(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
w.Header().Set("ETag", etag)
w.Header().Set("X-Content-Type-Options", "nosniff")
+ singleRangeOnly(r)
http.ServeContent(w, r, f.Filename, f.CreatedAt, blogStore(r).FileReader(r.Context(), f))
}
+// singleRangeOnly drops a multi-range request so the file is served whole.
+// ServeContent honours any number of ranges and chunkReader caches one slice,
+// so a header alternating between two chunks would cost a substring() query
+// per range — tens of thousands per request. Browsers and download managers
+// only ever ask for one range.
+func singleRangeOnly(r *http.Request) {
+ if strings.Contains(r.Header.Get("Range"), ",") {
+ r.Header.Del("Range")
+ }
+}
+
// handleFavicon answers the browsers that ask for /favicon.ico regardless of
// the <link rel="icon"> tags: the blog's own icon if it set one, else the
// Blogspace default. Without this the request would render the 404 page.