diff options
author | jhwz <[email protected]> | 2022-07-07 07:50:07 +1200 |
---|---|---|
committer | GitHub <[email protected]> | 2022-07-06 13:50:07 -0600 |
commit | f259ed52bb3764ce4fd5d88f1712cb43247c2639 (patch) | |
tree | a0f7e7a68117b81d757e1a6daddc3e257b97f29b /admin.go | |
parent | 8bac134f26874b25aa22f8fee325a05d39157a4c (diff) | |
download | caddy-f259ed52bb3764ce4fd5d88f1712cb43247c2639.tar.gz caddy-f259ed52bb3764ce4fd5d88f1712cb43247c2639.zip |
admin: support ETag on config endpoints (#4579)
* admin: support ETags
* support etags
Co-authored-by: Matt Holt <[email protected]>
Diffstat (limited to 'admin.go')
-rw-r--r-- | admin.go | 23 |
1 files changed, 20 insertions, 3 deletions
@@ -21,10 +21,13 @@ import ( "crypto/tls" "crypto/x509" "encoding/base64" + "encoding/hex" "encoding/json" "errors" "expvar" "fmt" + "hash" + "hash/fnv" "io" "net" "net/http" @@ -894,16 +897,30 @@ func (h adminHandler) originAllowed(origin *url.URL) bool { return false } +// etagHasher returns a the hasher we used on the config to both +// produce and verify ETags. +func etagHasher() hash.Hash32 { return fnv.New32a() } + func handleConfig(w http.ResponseWriter, r *http.Request) error { switch r.Method { case http.MethodGet: w.Header().Set("Content-Type", "application/json") - - err := readConfig(r.URL.Path, w) + // Set the ETag as a trailer header. + // The alternative is to write the config to a buffer, and + // then hash that. + w.Header().Set("Trailer", "ETag") + + hash := etagHasher() + configWriter := io.MultiWriter(w, hash) + err := readConfig(r.URL.Path, configWriter) if err != nil { return APIError{HTTPStatus: http.StatusBadRequest, Err: err} } + // we could consider setting up a sync.Pool for the summed + // hashes to reduce GC pressure. + w.Header().Set("ETag", r.URL.Path+" "+hex.EncodeToString(hash.Sum(nil))) + return nil case http.MethodPost, @@ -937,7 +954,7 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error { forceReload := r.Header.Get("Cache-Control") == "must-revalidate" - err := changeConfig(r.Method, r.URL.Path, body, forceReload) + err := changeConfig(r.Method, r.URL.Path, body, r.Header.Get("If-Match"), forceReload) if err != nil && !errors.Is(err, errSameConfig) { return err } |