From afd154119a24786be44996990028d177569f4a58 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Thu, 22 Aug 2019 14:52:39 -0600 Subject: [PATCH] admin: Support config adapters at /load endpoint Based on Content-Type --- admin.go | 43 +++++++++++++++++++++++++++++++---- caddyconfig/configadapters.go | 4 ++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/admin.go b/admin.go index 77999130..95a32769 100644 --- a/admin.go +++ b/admin.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "log" "net" "net/http" @@ -29,6 +30,7 @@ import ( "sync" "time" + "github.com/caddyserver/caddy/caddyconfig" "github.com/mholt/certmagic" "github.com/rs/cors" ) @@ -164,12 +166,45 @@ func handleLoadConfig(w http.ResponseWriter, r *http.Request) { return } - if !strings.Contains(r.Header.Get("Content-Type"), "/json") { - http.Error(w, "unacceptable Content-Type", http.StatusBadRequest) - return + var payload io.Reader = r.Body + + // if the config is formatted other than Caddy's native + // JSON, we need to adapt it before loading it + ct := r.Header.Get("Content-Type") + if !strings.Contains(ct, "/json") { + slashIdx := strings.Index(ct, "/") + if slashIdx < 0 { + http.Error(w, "Malformed Content-Type", http.StatusBadRequest) + return + } + adapterName := ct[slashIdx+1:] + cfgAdapter := caddyconfig.GetAdapter(adapterName) + if cfgAdapter == nil { + http.Error(w, "Unrecognized config adapter: "+adapterName, http.StatusBadRequest) + return + } + body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, 1024*1024)) + if err != nil { + http.Error(w, "Error reading request body: "+err.Error(), http.StatusBadRequest) + return + } + result, warnings, err := cfgAdapter.Adapt(body, nil) + if err != nil { + log.Printf("[ADMIN][ERROR] adapting config from %s: %v", adapterName, err) + http.Error(w, fmt.Sprintf("Adapting config from %s: %v", adapterName, err), http.StatusBadRequest) + return + } + if len(warnings) > 0 { + respBody, err := json.Marshal(warnings) + if err != nil { + log.Printf("[ADMIN][ERROR] marshaling warnings: %v", err) + } + w.Write(respBody) + } + payload = bytes.NewReader(result) } - err := Load(r.Body) + err := Load(payload) if err != nil { log.Printf("[ADMIN][ERROR] loading config: %v", err) http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/caddyconfig/configadapters.go b/caddyconfig/configadapters.go index c5391766..1a0801fb 100644 --- a/caddyconfig/configadapters.go +++ b/caddyconfig/configadapters.go @@ -98,6 +98,8 @@ func JSONIndent(val interface{}) ([]byte, error) { return json.MarshalIndent(val, "", "\t") } +// RegisterAdapter registers a config adapter with the given name. +// This should usually be done at init-time. func RegisterAdapter(name string, adapter Adapter) error { if _, ok := configAdapters[name]; ok { return fmt.Errorf("%s: already registered", name) @@ -106,6 +108,8 @@ func RegisterAdapter(name string, adapter Adapter) error { return nil } +// GetAdapter returns the adapter with the given name, +// or nil if one with that name is not registered. func GetAdapter(name string) Adapter { return configAdapters[name] }