@marcel91200 / caddy Mirror · read-only / commits / 567d96c6
Fork 0

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 {