mirror of
https://github.com/gin-gonic/gin.git
synced 2026-06-07 04:38:19 +08:00
Avoid panics for unsupported optional response interfaces
Wrapped response writers such as http.TimeoutHandler may not implement Hijacker or CloseNotifier. Match the safe optional-interface checks used by Flush and Pusher before calling through. Tested: docker run --rm -v "$PWD":/src -w /src golang:1.25 go test -run 'TestResponseWriterHijack|TestResponseWriterHijackAfterWrite|TestResponseWriterHijackAfterWriteHeaderNow' ./... Tested: docker run --rm -v "$PWD":/src -w /src golang:1.25 go test ./...
This commit is contained in:
parent
d3ffc99852
commit
46da485481
@ -114,15 +114,22 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|||||||
if w.size > 0 {
|
if w.size > 0 {
|
||||||
return nil, nil, errHijackAlreadyWritten
|
return nil, nil, errHijackAlreadyWritten
|
||||||
}
|
}
|
||||||
|
hijacker, ok := w.ResponseWriter.(http.Hijacker)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, http.ErrNotSupported
|
||||||
|
}
|
||||||
if w.size < 0 {
|
if w.size < 0 {
|
||||||
w.size = 0
|
w.size = 0
|
||||||
}
|
}
|
||||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
return hijacker.Hijack()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseNotify implements the http.CloseNotifier interface.
|
// CloseNotify implements the http.CloseNotifier interface.
|
||||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
if notifier, ok := w.ResponseWriter.(http.CloseNotifier); ok {
|
||||||
|
return notifier.CloseNotify()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush implements the http.Flusher interface.
|
// Flush implements the http.Flusher interface.
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -113,19 +114,36 @@ func TestResponseWriterHijack(t *testing.T) {
|
|||||||
writer.reset(testWriter)
|
writer.reset(testWriter)
|
||||||
w := ResponseWriter(writer)
|
w := ResponseWriter(writer)
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
_, _, err := w.Hijack()
|
||||||
_, _, err := w.Hijack()
|
require.ErrorIs(t, err, http.ErrNotSupported)
|
||||||
require.NoError(t, err)
|
assert.False(t, w.Written())
|
||||||
})
|
|
||||||
assert.True(t, w.Written())
|
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
assert.Nil(t, w.CloseNotify())
|
||||||
w.CloseNotify()
|
|
||||||
})
|
|
||||||
|
|
||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResponseWriterHijackWithTimeoutHandler(t *testing.T) {
|
||||||
|
var hijackErr error
|
||||||
|
var closeNotify <-chan bool
|
||||||
|
var written bool
|
||||||
|
|
||||||
|
handler := http.TimeoutHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
writer := &responseWriter{}
|
||||||
|
writer.reset(w)
|
||||||
|
|
||||||
|
_, _, hijackErr = writer.Hijack()
|
||||||
|
closeNotify = writer.CloseNotify()
|
||||||
|
written = writer.Written()
|
||||||
|
}), time.Second, "")
|
||||||
|
|
||||||
|
handler.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil))
|
||||||
|
|
||||||
|
require.ErrorIs(t, hijackErr, http.ErrNotSupported)
|
||||||
|
assert.Nil(t, closeNotify)
|
||||||
|
assert.False(t, written)
|
||||||
|
}
|
||||||
|
|
||||||
type mockHijacker struct {
|
type mockHijacker struct {
|
||||||
*httptest.ResponseRecorder
|
*httptest.ResponseRecorder
|
||||||
hijacked bool
|
hijacked bool
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user