Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 12, 14:30 UTC
fileserver: read etags from precomputed files (#6222)
Aziz Rmadi committed
Apr 13, 2024 at 05:49 UTC
567d96c6245fbca6086687daa40d3f37530b4f36
3 files changed
+84
-1
caddytest/integration/caddyfile_adapt/file_server_etag_file_extensions.caddyfiletest
new
+40
@@ -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
+}
\ No newline at end of file
modules/caddyhttp/fileserver/caddyfile.go
+7
@@ -164,6 +164,13 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
164
}
165
fsrv.PassThru = true
166
167
+ case "etag_file_extensions":
168
+ etagFileExtensions := d.RemainingArgs()
169
+ if len(etagFileExtensions) == 0 {
170
+ return d.ArgErr()
171
+ }
172
+ fsrv.EtagFileExtensions = etagFileExtensions
173
+
174
default:
175
return d.Errf("unknown subdirective '%s'", d.Val())
176
}
modules/caddyhttp/fileserver/staticfiles.go
+37
-1
@@ -161,6 +161,12 @@ type FileServer struct {
161
PrecompressedOrder []string `json:"precompressed_order,omitempty"`
162
precompressors map[string]encode.Precompressed
163
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
+
170
fsmap caddy.FileSystems
171
172
logger *zap.Logger
@@ -396,6 +402,14 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
402
w.Header().Del("Accept-Ranges")
403
w.Header().Add("Vary", "Accept-Encoding")
404
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
+
413
// don't assign info = compressedInfo because sidecars are kind
414
// of transparent; however we do need to set the Etag:
415
// 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
434
return err // error is already structured
435
}
436
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
+ }
444
if etag == "" {
445
etag = calculateEtag(info)
446
}
@@ -639,6 +659,22 @@ func calculateEtag(d os.FileInfo) string {
659
return `"` + t + s + `"`
660
}
661
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
+
678
// redirect performs a redirect to a given path. The 'toPath' parameter
679
// MUST be solely a path, and MUST NOT include a query.
680
func redirect(w http.ResponseWriter, r *http.Request, toPath string) error {