fileserver: read etags from precomputed files (#6222)

This commit is contained in:
Aziz Rmadi 2024-04-13 05:49:55 -05:00 committed by GitHub
parent 5d8b45c9fb
commit 567d96c624
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 84 additions and 1 deletions

View File

@ -0,0 +1,40 @@
:8080 {
root * ./
file_server {
etag_file_extensions .b3sum .sha256
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handler": "vars",
"root": "./"
},
{
"etag_file_extensions": [
".b3sum",
".sha256"
],
"handler": "file_server",
"hide": [
"./Caddyfile"
]
}
]
}
]
}
}
}
}
}

View File

@ -164,6 +164,13 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
} }
fsrv.PassThru = true fsrv.PassThru = true
case "etag_file_extensions":
etagFileExtensions := d.RemainingArgs()
if len(etagFileExtensions) == 0 {
return d.ArgErr()
}
fsrv.EtagFileExtensions = etagFileExtensions
default: default:
return d.Errf("unknown subdirective '%s'", d.Val()) return d.Errf("unknown subdirective '%s'", d.Val())
} }

View File

@ -161,6 +161,12 @@ type FileServer struct {
PrecompressedOrder []string `json:"precompressed_order,omitempty"` PrecompressedOrder []string `json:"precompressed_order,omitempty"`
precompressors map[string]encode.Precompressed precompressors map[string]encode.Precompressed
// List of file extensions to try to read Etags from.
// If set, file Etags will be read from sidecar files
// with any of these suffixes, instead of generating
// our own Etag.
EtagFileExtensions []string `json:"etag_file_extensions,omitempty"`
fsmap caddy.FileSystems fsmap caddy.FileSystems
logger *zap.Logger logger *zap.Logger
@ -396,6 +402,14 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
w.Header().Del("Accept-Ranges") w.Header().Del("Accept-Ranges")
w.Header().Add("Vary", "Accept-Encoding") w.Header().Add("Vary", "Accept-Encoding")
// try to get the etag from pre computed files if an etag suffix list was provided
if etag == "" && fsrv.EtagFileExtensions != nil {
etag, err = fsrv.getEtagFromFile(fileSystem, compressedFilename)
if err != nil {
return err
}
}
// don't assign info = compressedInfo because sidecars are kind // don't assign info = compressedInfo because sidecars are kind
// of transparent; however we do need to set the Etag: // of transparent; however we do need to set the Etag:
// https://caddy.community/t/gzipped-sidecar-file-wrong-same-etag/16793 // 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
return err // error is already structured return err // error is already structured
} }
defer file.Close() defer file.Close()
// try to get the etag from pre computed files if an etag suffix list was provided
if etag == "" && fsrv.EtagFileExtensions != nil {
etag, err = fsrv.getEtagFromFile(fileSystem, filename)
if err != nil {
return err
}
}
if etag == "" { if etag == "" {
etag = calculateEtag(info) etag = calculateEtag(info)
} }
@ -639,6 +659,22 @@ func calculateEtag(d os.FileInfo) string {
return `"` + t + s + `"` return `"` + t + s + `"`
} }
// Finds the first corresponding etag file for a given file in the file system and return its content
func (fsrv *FileServer) getEtagFromFile(fileSystem fs.FS, filename string) (string, error) {
for _, suffix := range fsrv.EtagFileExtensions {
etagFilename := filename + suffix
etag, err := fs.ReadFile(fileSystem, etagFilename)
if errors.Is(err, fs.ErrNotExist) {
continue
}
if err != nil {
return "", fmt.Errorf("cannot read etag from file %s: %v", etagFilename, err)
}
return string(etag), nil
}
return "", nil
}
// redirect performs a redirect to a given path. The 'toPath' parameter // redirect performs a redirect to a given path. The 'toPath' parameter
// MUST be solely a path, and MUST NOT include a query. // MUST be solely a path, and MUST NOT include a query.
func redirect(w http.ResponseWriter, r *http.Request, toPath string) error { func redirect(w http.ResponseWriter, r *http.Request, toPath string) error {