summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-11-27 11:49:49 -0700
committerMatthew Holt <mholt@users.noreply.github.com>2019-11-27 11:49:49 -0700
commit6e10586303fd90f0298c8086e754b35dfc3f01ba (patch)
tree68dc2ba044debe5f4a75cebfc29d044a6064d1bd
parent8de1a762273323608d9d9080c42057d1814070b6 (diff)
admin: Preserve "@id" fields through partial changes (fixes #2902)
-rw-r--r--admin.go7
-rw-r--r--caddy.go37
2 files changed, 33 insertions, 11 deletions
diff --git a/admin.go b/admin.go
index b1ced18..981f2dc 100644
--- a/admin.go
+++ b/admin.go
@@ -27,6 +27,7 @@ import (
"net/url"
"os"
"path"
+ "regexp"
"strconv"
"strings"
"sync"
@@ -764,6 +765,12 @@ var (
}
)
+// idRegexp is used to match ID fields and their associated values
+// in the config. It also matches adjacent commas so that syntax
+// can be preserved no matter where in the object the field appears.
+// It supports string and most numeric values.
+var idRegexp = regexp.MustCompile(`(?m),?\s*"` + idKey + `":\s?(-?[0-9]+(\.[0-9]+)?|(?U)".*")\s*,?`)
+
const (
rawConfigKey = "config"
idKey = "@id"
diff --git a/caddy.go b/caddy.go
index b4acde3..d8609d2 100644
--- a/caddy.go
+++ b/caddy.go
@@ -102,16 +102,6 @@ func changeConfig(method, path string, input []byte, forceReload bool) error {
return err
}
- // find any IDs in this config and index them
- idx := make(map[string]string)
- err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
- if err != nil {
- return APIError{
- Code: http.StatusInternalServerError,
- Err: fmt.Errorf("indexing config: %v", err),
- }
- }
-
// the mutation is complete, so encode the entire config as JSON
newCfg, err := json.Marshal(rawCfg[rawConfigKey])
if err != nil {
@@ -127,6 +117,32 @@ func changeConfig(method, path string, input []byte, forceReload bool) error {
return nil
}
+ // find any IDs in this config and index them
+ idx := make(map[string]string)
+ err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
+ if err != nil {
+ return APIError{
+ Code: http.StatusInternalServerError,
+ Err: fmt.Errorf("indexing config: %v", err),
+ }
+ }
+
+ // remove any @id fields from the JSON, which would cause
+ // loading to break since the field wouldn't be recognized
+ // (an alternate way to do this would be to delete them from
+ // rawCfg as they are indexed, then iterate the index we made
+ // and add them back after encoding as JSON)
+ newCfg = idRegexp.ReplaceAllFunc(newCfg, func(in []byte) []byte {
+ // matches with a comma on both sides (when "@id" property is
+ // not the first or last in the object) need to keep exactly
+ // one comma for correct JSON syntax
+ comma := []byte{','}
+ if bytes.HasPrefix(in, comma) && bytes.HasSuffix(in, comma) {
+ return comma
+ }
+ return []byte{}
+ })
+
// load this new config; if it fails, we need to revert to
// our old representation of caddy's actual config
err = unsyncedDecodeAndRun(newCfg)
@@ -183,7 +199,6 @@ func indexConfigObjects(ptr interface{}, configPath string, index map[string]str
default:
return fmt.Errorf("%s: %s field must be a string or number", configPath, idKey)
}
- delete(val, idKey) // field is no longer needed, and will break config if not removed
continue
}
// traverse this object property recursively