Skip to content

Commit 567d96c

Browse files
authored
fileserver: read etags from precomputed files (#6222)
1 parent 5d8b45c commit 567d96c

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
:8080 {
2+
root * ./
3+
file_server {
4+
etag_file_extensions .b3sum .sha256
5+
}
6+
}
7+
----------
8+
{
9+
"apps": {
10+
"http": {
11+
"servers": {
12+
"srv0": {
13+
"listen": [
14+
":8080"
15+
],
16+
"routes": [
17+
{
18+
"handle": [
19+
{
20+
"handler": "vars",
21+
"root": "./"
22+
},
23+
{
24+
"etag_file_extensions": [
25+
".b3sum",
26+
".sha256"
27+
],
28+
"handler": "file_server",
29+
"hide": [
30+
"./Caddyfile"
31+
]
32+
}
33+
]
34+
}
35+
]
36+
}
37+
}
38+
}
39+
}
40+
}

modules/caddyhttp/fileserver/caddyfile.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
164164
}
165165
fsrv.PassThru = true
166166

167+
case "etag_file_extensions":
168+
etagFileExtensions := d.RemainingArgs()
169+
if len(etagFileExtensions) == 0 {
170+
return d.ArgErr()
171+
}
172+
fsrv.EtagFileExtensions = etagFileExtensions
173+
167174
default:
168175
return d.Errf("unknown subdirective '%s'", d.Val())
169176
}

modules/caddyhttp/fileserver/staticfiles.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ type FileServer struct {
161161
PrecompressedOrder []string `json:"precompressed_order,omitempty"`
162162
precompressors map[string]encode.Precompressed
163163

164+
// List of file extensions to try to read Etags from.
165+
// If set, file Etags will be read from sidecar files
166+
// with any of these suffixes, instead of generating
167+
// our own Etag.
168+
EtagFileExtensions []string `json:"etag_file_extensions,omitempty"`
169+
164170
fsmap caddy.FileSystems
165171

166172
logger *zap.Logger
@@ -396,6 +402,14 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
396402
w.Header().Del("Accept-Ranges")
397403
w.Header().Add("Vary", "Accept-Encoding")
398404

405+
// try to get the etag from pre computed files if an etag suffix list was provided
406+
if etag == "" && fsrv.EtagFileExtensions != nil {
407+
etag, err = fsrv.getEtagFromFile(fileSystem, compressedFilename)
408+
if err != nil {
409+
return err
410+
}
411+
}
412+
399413
// don't assign info = compressedInfo because sidecars are kind
400414
// of transparent; however we do need to set the Etag:
401415
// https://caddy.community/t/gzipped-sidecar-file-wrong-same-etag/16793
@@ -420,7 +434,13 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
420434
return err // error is already structured
421435
}
422436
defer file.Close()
423-
437+
// try to get the etag from pre computed files if an etag suffix list was provided
438+
if etag == "" && fsrv.EtagFileExtensions != nil {
439+
etag, err = fsrv.getEtagFromFile(fileSystem, filename)
440+
if err != nil {
441+
return err
442+
}
443+
}
424444
if etag == "" {
425445
etag = calculateEtag(info)
426446
}
@@ -639,6 +659,22 @@ func calculateEtag(d os.FileInfo) string {
639659
return `"` + t + s + `"`
640660
}
641661

662+
// Finds the first corresponding etag file for a given file in the file system and return its content
663+
func (fsrv *FileServer) getEtagFromFile(fileSystem fs.FS, filename string) (string, error) {
664+
for _, suffix := range fsrv.EtagFileExtensions {
665+
etagFilename := filename + suffix
666+
etag, err := fs.ReadFile(fileSystem, etagFilename)
667+
if errors.Is(err, fs.ErrNotExist) {
668+
continue
669+
}
670+
if err != nil {
671+
return "", fmt.Errorf("cannot read etag from file %s: %v", etagFilename, err)
672+
}
673+
return string(etag), nil
674+
}
675+
return "", nil
676+
}
677+
642678
// redirect performs a redirect to a given path. The 'toPath' parameter
643679
// MUST be solely a path, and MUST NOT include a query.
644680
func redirect(w http.ResponseWriter, r *http.Request, toPath string) error {

0 commit comments

Comments
 (0)