@marcel91200 / caddy Mirror · read-only / commits / 6e105863
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 14, 05:15 UTC

admin: Preserve "@id" fields through partial changes (fixes #2902)

Matthew Holt committed Nov 27, 2019 at 11:49 UTC 6e10586303fd90f0298c8086e754b35dfc3f01ba
2 files changed +33 -11
admin.go
+7
@@ -27,6 +27,7 @@ import (
27 "net/url"
28 "os"
29 "path"
30 + "regexp"
31 "strconv"
32 "strings"
33 "sync"
@@ -764,6 +765,12 @@ var (
765 }
766 )
767
768 +// idRegexp is used to match ID fields and their associated values
769 +// in the config. It also matches adjacent commas so that syntax
770 +// can be preserved no matter where in the object the field appears.
771 +// It supports string and most numeric values.
772 +var idRegexp = regexp.MustCompile(`(?m),?\s*"` + idKey + `":\s?(-?[0-9]+(\.[0-9]+)?|(?U)".*")\s*,?`)
773 +
774 const (
775 rawConfigKey = "config"
776 idKey = "@id"
caddy.go
+26 -11
@@ -102,16 +102,6 @@ func changeConfig(method, path string, input []byte, forceReload bool) error {
102 return err
103 }
104
105 - // find any IDs in this config and index them
106 - idx := make(map[string]string)
107 - err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
108 - if err != nil {
109 - return APIError{
110 - Code: http.StatusInternalServerError,
111 - Err: fmt.Errorf("indexing config: %v", err),
112 - }
113 - }
114 -
105 // the mutation is complete, so encode the entire config as JSON
106 newCfg, err := json.Marshal(rawCfg[rawConfigKey])
107 if err != nil {
@@ -127,6 +117,32 @@ func changeConfig(method, path string, input []byte, forceReload bool) error {
117 return nil
118 }
119
120 + // find any IDs in this config and index them
121 + idx := make(map[string]string)
122 + err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
123 + if err != nil {
124 + return APIError{
125 + Code: http.StatusInternalServerError,
126 + Err: fmt.Errorf("indexing config: %v", err),
127 + }
128 + }
129 +
130 + // remove any @id fields from the JSON, which would cause
131 + // loading to break since the field wouldn't be recognized
132 + // (an alternate way to do this would be to delete them from
133 + // rawCfg as they are indexed, then iterate the index we made
134 + // and add them back after encoding as JSON)
135 + newCfg = idRegexp.ReplaceAllFunc(newCfg, func(in []byte) []byte {
136 + // matches with a comma on both sides (when "@id" property is
137 + // not the first or last in the object) need to keep exactly
138 + // one comma for correct JSON syntax
139 + comma := []byte{','}
140 + if bytes.HasPrefix(in, comma) && bytes.HasSuffix(in, comma) {
141 + return comma
142 + }
143 + return []byte{}
144 + })
145 +
146 // load this new config; if it fails, we need to revert to
147 // our old representation of caddy's actual config
148 err = unsyncedDecodeAndRun(newCfg)
@@ -183,7 +199,6 @@ func indexConfigObjects(ptr interface{}, configPath string, index map[string]str
199 default:
200 return fmt.Errorf("%s: %s field must be a string or number", configPath, idKey)
201 }
186 - delete(val, idKey) // field is no longer needed, and will break config if not removed
202 continue
203 }
204 // traverse this object property recursively