aboutsummaryrefslogtreecommitdiffhomepage
path: root/admin.go
diff options
context:
space:
mode:
authorjhwz <[email protected]>2022-07-07 07:50:07 +1200
committerGitHub <[email protected]>2022-07-06 13:50:07 -0600
commitf259ed52bb3764ce4fd5d88f1712cb43247c2639 (patch)
treea0f7e7a68117b81d757e1a6daddc3e257b97f29b /admin.go
parent8bac134f26874b25aa22f8fee325a05d39157a4c (diff)
downloadcaddy-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.go23
1 files changed, 20 insertions, 3 deletions
diff --git a/admin.go b/admin.go
index 214b734d8..e37caf83a 100644
--- a/admin.go
+++ b/admin.go
@@ -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
}