diff --git a/caddyhttp/httpserver/recorder.go b/caddyhttp/httpserver/recorder.go index 2651549d..bd9dd2af 100644 --- a/caddyhttp/httpserver/recorder.go +++ b/caddyhttp/httpserver/recorder.go @@ -195,6 +195,28 @@ func (rb *ResponseBuffer) ReadFrom(src io.Reader) (int64, error) { return rb.Buffer.ReadFrom(src) } +// StatusCodeWriter returns an http.ResponseWriter that always +// writes the status code stored in rb from when a response +// was buffered to it. +func (rb *ResponseBuffer) StatusCodeWriter(w http.ResponseWriter) http.ResponseWriter { + return forcedStatusCodeWriter{w, rb} +} + +// forcedStatusCodeWriter is used to force a status code when +// writing the header. It uses the status code saved on rb. +// This is useful if passing a http.ResponseWriter into +// http.ServeContent because ServeContent hard-codes 2xx status +// codes. If we buffered the response, we force that status code +// instead. +type forcedStatusCodeWriter struct { + http.ResponseWriter + rb *ResponseBuffer +} + +func (fscw forcedStatusCodeWriter) WriteHeader(int) { + fscw.ResponseWriter.WriteHeader(fscw.rb.status) +} + // respBufPool is used for io.CopyBuffer when ResponseBuffer // is configured to stream a response. var respBufPool = &sync.Pool{ diff --git a/caddyhttp/templates/templates.go b/caddyhttp/templates/templates.go index 53ead8f3..a7f10c16 100644 --- a/caddyhttp/templates/templates.go +++ b/caddyhttp/templates/templates.go @@ -40,7 +40,7 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error if reqExt == "" { // request has no extension, so check response Content-Type ct := mime.TypeByExtension(ext) - if strings.Contains(header.Get("Content-Type"), ct) { + if ct != "" && strings.Contains(header.Get("Content-Type"), ct) { return true } } else if reqExt == ext { @@ -96,13 +96,14 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error // set the actual content length now that the template was executed w.Header().Set("Content-Length", strconv.Itoa(buf.Len())) - // get the modification time in preparation to ServeContent + // get the modification time in preparation for http.ServeContent modTime, _ := time.Parse(http.TimeFormat, w.Header().Get("Last-Modified")) - // at last, write the rendered template to the response - http.ServeContent(w, r, templateName, modTime, bytes.NewReader(buf.Bytes())) + // at last, write the rendered template to the response; make sure to use + // use the proper status code, since ServeContent hard-codes 2xx codes... + http.ServeContent(rb.StatusCodeWriter(w), r, templateName, modTime, bytes.NewReader(buf.Bytes())) - return http.StatusOK, nil + return 0, nil } return t.Next.ServeHTTP(w, r)