mirror of
https://github.com/gin-gonic/gin.git
synced 2026-07-13 22:52:20 +08:00
Compare commits
7 Commits
a3a385712b
...
e10089305d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e10089305d | ||
|
|
bc46a09535 | ||
|
|
5c00df8afa | ||
|
|
efdb77ebbd | ||
|
|
4d32cf970e | ||
|
|
dc128b7291 | ||
|
|
8eb015d2bc |
@ -1157,6 +1157,15 @@ func (c *Context) Render(code int, r render.Render) {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Writer.Written() && IsDebugging() {
|
||||
// Skip warning for SSE and streaming responses (status code -1)
|
||||
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)
|
||||
|
||||
@ -1396,6 +1396,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 := os.Getenv("GIN_MODE")
|
||||
defer os.Setenv("GIN_MODE", 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 := os.Getenv("GIN_MODE")
|
||||
defer os.Setenv("GIN_MODE", 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)
|
||||
|
||||
@ -4,7 +4,10 @@
|
||||
|
||||
package render
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Data contains ContentType and bytes data.
|
||||
type Data struct {
|
||||
@ -15,6 +18,9 @@ type Data struct {
|
||||
// Render (Data) writes data with custom ContentType.
|
||||
func (r Data) Render(w http.ResponseWriter) (err error) {
|
||||
r.WriteContentType(w)
|
||||
if len(r.Data) > 0 {
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(r.Data)))
|
||||
}
|
||||
_, err = w.Write(r.Data)
|
||||
return
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -453,6 +454,36 @@ func TestRenderData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "#!PNG some raw data", w.Body.String())
|
||||
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "19", w.Header().Get("Content-Length"))
|
||||
}
|
||||
|
||||
func TestRenderDataContentLength(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
size, err := strconv.Atoi(r.URL.Query().Get("size"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
data := Data{
|
||||
ContentType: "application/octet-stream",
|
||||
Data: make([]byte, size),
|
||||
}
|
||||
assert.NoError(t, data.Render(w))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
for _, size := range []int{0, 1, 100, 100_000} {
|
||||
t.Run(strconv.Itoa(size), func(t *testing.T) {
|
||||
resp, err := http.Get(srv.URL + "?size=" + strconv.Itoa(size))
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, "application/octet-stream", resp.Header.Get("Content-Type"))
|
||||
assert.Equal(t, strconv.Itoa(size), resp.Header.Get("Content-Length"))
|
||||
|
||||
actual, err := io.Copy(io.Discard, resp.Body)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, size, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderString(t *testing.T) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user