-
-
Notifications
You must be signed in to change notification settings - Fork 7
Closed
Closed
Copy link
Description
Bug: WriteHeader before RenderJSON causes Content-Type header to be ignored
Problem
Several places in the library call w.WriteHeader() before RenderJSON(). This causes the Content-Type: application/json header set by RenderJSON to be ignored, resulting in responses with text/plain content type instead of application/json.
In Go's http.ResponseWriter, once WriteHeader() is called, subsequent Header().Set() calls are ignored. RenderJSON sets the Content-Type header after the body encoding succeeds:
func RenderJSON(w http.ResponseWriter, data interface{}) {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(true)
if err := enc.Encode(data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8") // ignored if WriteHeader was called
_, _ = w.Write(buf.Bytes())
}Affected Locations
middleware.go - Health middleware (lines ~86-90)
if anyError {
w.WriteHeader(http.StatusServiceUnavailable)
} else {
w.WriteHeader(http.StatusOK)
}
RenderJSON(w, resp)blackwords.go (lines ~23-24)
w.WriteHeader(http.StatusForbidden)
RenderJSON(w, JSON{"error": "one of blacklisted words detected"})onlyfrom.go (lines ~24-25, 34-35)
w.WriteHeader(http.StatusInternalServerError)
RenderJSON(w, JSON{"error": fmt.Sprintf("can't get realip: %s", err)})w.WriteHeader(http.StatusForbidden)
RenderJSON(w, JSON{"error": fmt.Sprintf("ip %q rejected", ip)})metrics.go (lines ~16-17)
w.WriteHeader(http.StatusForbidden)
RenderJSON(w, JSON{"error": fmt.Sprintf("ip %s rejected", ip)})Suggested Fix
Replace WriteHeader + RenderJSON pattern with EncodeJSON:
// Before
w.WriteHeader(http.StatusForbidden)
RenderJSON(w, JSON{"error": "message"})
// After
_ = EncodeJSON(w, http.StatusForbidden, JSON{"error": "message"})EncodeJSON properly sets the Content-Type header before calling WriteHeader:
func EncodeJSON[T any](w http.ResponseWriter, status int, v T) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(v); err != nil {
return fmt.Errorf("encode json: %w", err)
}
return nil
}Impact
Clients expecting JSON responses may fail to parse the response correctly because the Content-Type header is text/plain instead of application/json.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels