Commitd82862eaRecorded10 Jul 2026Repositorysigil-web
Wire serve-file to honor request Range headers
Message
serve-file takes an optional request; when it carries a Range header the file is served via http-response/file (206 Partial Content / 416), otherwise the whole file is returned as before, now advertising Accept-Ranges: bytes. make-static-handler threads the request through.
Requires sigil-http with http-response/file range: support (the release carrying T2) and a corresponding sigil-http pin bump before it can ship.
Changed
src/sigil/web/static.sgl | 43 +++++++++++++++++++++++++++++--------------
1 file changed, 29 insertions(+), 14 deletions(-)Diff
src/sigil/web/static.sglmodified
@@ -46,20 +46,35 @@
46
;; File Serving 47
;; ============================================================ 48
−49
;;; Serve a single file with appropriate headers−50
;;; Returns http-response or #f if file doesn't exist−51
(define (serve-file file-path)+49
;;; Serve a single file with appropriate headers.+50
;;; Returns an http-response, or #f if the file doesn't exist.+51
;;;+52
;;; An optional `request` may be passed as a second argument. When it+53
;;; carries a `Range:` header, the file is served with byte-range support+54
;;; via `http-response/file` — a satisfiable range yields `206 Partial+55
;;; Content`, an unsatisfiable one `416 Range Not Satisfiable`. Without a+56
;;; Range header (or request) the whole file is returned as `200 OK`, now+57
;;; advertising `Accept-Ranges: bytes` so clients know ranges are honored.+58
(define (serve-file file-path . rest) 59
(: string? -> any?) 60
(if (file-exists? file-path)−54
(let* ((mime-type (mime-type-for-file file-path))−55
(content-type (or mime-type "application/octet-stream"))−56
(content (if (text-mime-type? mime-type)−57
(read-file-string file-path)−58
(read-file-bytes file-path))))−59
(http-response−60
status: 200−61
headers: (dict-set #{} content-type: content-type)−62
body: content))+61
(let* ((request (if (pair? rest) (car rest) #f))+62
(range (and request (http-request-header request "Range"))))+63
(if range+64
;; Honor byte ranges by streaming through http-response/file.+65
(http-response/file file-path range: range)+66
;; No Range: whole-file response (unchanged body handling),+67
;; but advertise range support for future requests.+68
(let* ((mime-type (mime-type-for-file file-path))+69
(content-type (or mime-type "application/octet-stream"))+70
(content (if (text-mime-type? mime-type)+71
(read-file-string file-path)+72
(read-file-bytes file-path))))+73
(http-response+74
status: 200+75
headers: (dict-set (dict-set #{} content-type: content-type)+76
accept-ranges: "bytes")+77
body: content)))) 78
#f)) 79
80
;;; Check if MIME type is text-based@@ -108,9 +123,9 @@
123
;; If it's a directory, try index file 124
((directory? file-path) 125
(let ((index-path (path-join file-path index-file)))−111
(serve-file index-path)))+126
(serve-file index-path request))) 127
;; Try to serve the file 128
(else−114
(serve-file file-path)))))))))+129
(serve-file file-path request))))))))) 130
131
))