From 660c59b6f3c138ce1a143ea864225ba177005e8e Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Tue, 28 Jun 2022 22:43:57 -0600 Subject: [PATCH] admin: Implement /adapt endpoint (close #4465) (#4846) --- caddyconfig/load.go | 47 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/caddyconfig/load.go b/caddyconfig/load.go index 7a390d0b520..78aaba2b7d6 100644 --- a/caddyconfig/load.go +++ b/caddyconfig/load.go @@ -58,6 +58,10 @@ func (al adminLoad) Routes() []caddy.AdminRoute { Pattern: "/load", Handler: caddy.AdminHandlerFunc(al.handleLoad), }, + { + Pattern: "/adapt", + Handler: caddy.AdminHandlerFunc(al.handleAdapt), + }, } } @@ -122,7 +126,48 @@ func (adminLoad) handleLoad(w http.ResponseWriter, r *http.Request) error { return nil } -// adaptByContentType adapts body to Caddy JSON using the adapter specified by contenType. +// handleAdapt adapts the given Caddy config to JSON and responds with the result. +func (adminLoad) handleAdapt(w http.ResponseWriter, r *http.Request) error { + if r.Method != http.MethodPost { + return caddy.APIError{ + HTTPStatus: http.StatusMethodNotAllowed, + Err: fmt.Errorf("method not allowed"), + } + } + + buf := bufPool.Get().(*bytes.Buffer) + buf.Reset() + defer bufPool.Put(buf) + + _, err := io.Copy(buf, r.Body) + if err != nil { + return caddy.APIError{ + HTTPStatus: http.StatusBadRequest, + Err: fmt.Errorf("reading request body: %v", err), + } + } + + result, warnings, err := adaptByContentType(r.Header.Get("Content-Type"), buf.Bytes()) + if err != nil { + return caddy.APIError{ + HTTPStatus: http.StatusBadRequest, + Err: err, + } + } + + out := struct { + Warnings []Warning `json:"warnings,omitempty"` + Result json.RawMessage `json:"result"` + }{ + Warnings: warnings, + Result: result, + } + + w.Header().Set("Content-Type", "application/json") + return json.NewEncoder(w).Encode(out) +} + +// adaptByContentType adapts body to Caddy JSON using the adapter specified by contentType. // If contentType is empty or ends with "/json", the input will be returned, as a no-op. func adaptByContentType(contentType string, body []byte) ([]byte, []Warning, error) { // assume JSON as the default