From 2d5a30b908d022d76c246a09154d1fc611695a17 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Wed, 31 Aug 2022 09:43:46 -0600 Subject: caddyhttp: Set Content-Type for static response (#4999) --- modules/caddyhttp/staticresp.go | 42 ++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) (limited to 'modules/caddyhttp/staticresp.go') diff --git a/modules/caddyhttp/staticresp.go b/modules/caddyhttp/staticresp.go index f0aea03..f429692 100644 --- a/modules/caddyhttp/staticresp.go +++ b/modules/caddyhttp/staticresp.go @@ -88,10 +88,18 @@ type StaticResponse struct { // if needing to use a placeholder, a string. StatusCode WeakString `json:"status_code,omitempty"` - // Header fields to set on the response. + // Header fields to set on the response; overwrites any existing + // header fields of the same names after normalization. Headers http.Header `json:"headers,omitempty"` - // The response body. + // The response body. If non-empty, the Content-Type header may + // be added automatically if it is not explicitly configured nor + // already set on the response; the default value is + // "text/plain; charset=utf-8" unless the body is a valid JSON object + // or array, in which case the value will be "application/json". + // Other than those common special cases the Content-Type header + // should be set explicitly if it is desired because MIME sniffing + // is disabled for safety. Body string `json:"body,omitempty"` // If true, the server will close the client's connection @@ -114,10 +122,10 @@ func (StaticResponse) CaddyModule() caddy.ModuleInfo { // UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax: // -// respond [] | [] { -// body -// close -// } +// respond [] | [] { +// body +// close +// } // // If there is just one argument (other than the matcher), it is considered // to be a status code if it's a valid positive integer of 3 digits. @@ -186,7 +194,23 @@ func (s StaticResponse) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Hand w.Header()[field] = newVals } - // do not allow Go to sniff the content-type + // implicitly set Content-Type header if we can do so safely + // (this allows templates handler to eval templates successfully + // or for clients to render JSON properly which is very common) + body := repl.ReplaceKnown(s.Body, "") + if body != "" && w.Header().Get("Content-Type") == "" { + content := strings.TrimSpace(s.Body) + if len(content) > 2 && + (content[0] == '{' && content[len(content)-1] == '}' || + (content[0] == '[' && content[len(content)-1] == ']')) && + json.Valid([]byte(content)) { + w.Header().Set("Content-Type", "application/json") + } else { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + } + } + + // do not allow Go to sniff the content-type, for safety if w.Header().Get("Content-Type") == "" { w.Header()["Content-Type"] = nil } @@ -213,8 +237,8 @@ func (s StaticResponse) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Hand w.WriteHeader(statusCode) // write response body - if s.Body != "" { - fmt.Fprint(w, repl.ReplaceKnown(s.Body, "")) + if body != "" { + fmt.Fprint(w, body) } return nil -- cgit v1.2.3