diff --git a/context.go b/context.go index a2e28e5b..e631c31f 100644 --- a/context.go +++ b/context.go @@ -1185,6 +1185,18 @@ func (c *Context) Render(code int, r render.Render) { return } + if c.Writer.Size() > 0 && IsDebugging() { + // Skip this warning when: + // - status code is -1, which is used for special/streaming-style renders (including some + // non-streaming helpers like redirects) where multiple writes are expected, and + // - the renderer is an SSE event, since Server-Sent Events are sent as a stream. + if code != -1 { + if _, ok := r.(sse.Event); !ok { + debugPrint("[WARNING] Response body already written. Attempting to write again with status code %d", code) + } + } + } + if err := r.Render(c.Writer); err != nil { // Pushing error to c.Errors _ = c.Error(err) diff --git a/context_test.go b/context_test.go index 364a92ae..687d923f 100644 --- a/context_test.go +++ b/context_test.go @@ -1425,6 +1425,43 @@ func TestContextRenderNoContentData(t *testing.T) { assert.Equal(t, "text/csv", w.Header().Get("Content-Type")) } +// Test multiple JSON writes in debug mode +func TestContextRenderMultipleJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + oldMode := Mode() + defer SetMode(oldMode) + SetMode(DebugMode) + + output := captureOutput(t, func() { + c.JSON(http.StatusOK, H{"foo": "bar"}) + c.JSON(http.StatusOK, H{"baz": "qux"}) + }) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Contains(t, output, "[WARNING] Response body already written") + assert.Contains(t, output, "status code 200") +} + +// Test multiple SSE writes in debug mode +func TestContextRenderMultipleSSE(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + oldMode := Mode() + defer SetMode(oldMode) + SetMode(DebugMode) + + output := captureOutput(t, func() { + c.SSEvent("message", "test1") + c.SSEvent("message", "test2") + }) + + assert.Equal(t, http.StatusOK, w.Code) + assert.NotContains(t, output, "[WARNING] Response body already written") +} + func TestContextRenderSSE(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w)