From 79a5b81292bc27f9703b0e73d83fd858c2fd7502 Mon Sep 17 00:00:00 2001 From: Team Humaki Date: Wed, 16 Sep 2026 16:35:46 -0700 Subject: [PATCH] responder: skip status lookup when request is nil Fixes #28 --- responder.go | 31 +++++++++++---------- responder_test.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 responder_test.go diff --git a/responder.go b/responder.go index f38807d..403ea33 100644 --- a/responder.go +++ b/responder.go @@ -59,13 +59,22 @@ func DefaultResponder(w http.ResponseWriter, r *http.Request, v interface{}) { } } +// writeStatus writes a previously stored Status() hint. A nil request is +// allowed so wrappers can call JSON/XML/etc. after writing the header themselves. +func writeStatus(w http.ResponseWriter, r *http.Request) { + if r == nil { + return + } + if status, ok := r.Context().Value(StatusCtxKey).(int); ok { + w.WriteHeader(status) + } +} + // PlainText writes a string to the response, setting the Content-Type as // text/plain. func PlainText(w http.ResponseWriter, r *http.Request, v string) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } + writeStatus(w, r) w.Write([]byte(v)) //nolint:errcheck } @@ -73,18 +82,14 @@ func PlainText(w http.ResponseWriter, r *http.Request, v string) { // application/octet-stream. func Data(w http.ResponseWriter, r *http.Request, v []byte) { w.Header().Set("Content-Type", "application/octet-stream") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } + writeStatus(w, r) w.Write(v) //nolint:errcheck } // HTML writes a string to the response, setting the Content-Type as text/html. func HTML(w http.ResponseWriter, r *http.Request, v string) { w.Header().Set("Content-Type", "text/html; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } + writeStatus(w, r) w.Write([]byte(v)) //nolint:errcheck } @@ -100,9 +105,7 @@ func JSON(w http.ResponseWriter, r *http.Request, v interface{}) { } w.Header().Set("Content-Type", "application/json") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } + writeStatus(w, r) w.Write(buf.Bytes()) //nolint:errcheck } @@ -117,9 +120,7 @@ func XML(w http.ResponseWriter, r *http.Request, v interface{}) { } w.Header().Set("Content-Type", "application/xml; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } + writeStatus(w, r) // Try to find test") { + t.Fatalf("body=%q", w.Body.String()) + } +} + +func TestPlainTextDataHTMLNilRequest(t *testing.T) { + w := httptest.NewRecorder() + PlainText(w, nil, "hello") + if w.Body.String() != "hello" { + t.Fatalf("plaintext body=%q", w.Body.String()) + } + + w = httptest.NewRecorder() + Data(w, nil, []byte("raw")) + if w.Body.String() != "raw" { + t.Fatalf("data body=%q", w.Body.String()) + } + + w = httptest.NewRecorder() + HTML(w, nil, "

ok

") + if w.Body.String() != "

ok

" { + t.Fatalf("html body=%q", w.Body.String()) + } +} + +func TestJSONStillHonorsStatusHint(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + Status(r, http.StatusAccepted) + JSON(w, r, map[string]string{"ok": "yes"}) + if w.Code != http.StatusAccepted { + t.Fatalf("status=%d want %d", w.Code, http.StatusAccepted) + } +}