mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-17 16:26:47 +03:00
server: keep hijacked reader out of pool (#2184)
When KeepHijackedConns is enabled, the hijacked connection may outlive the HijackHandler. The wrapper continues reading through the buffered reader after the handler returns, so returning that reader to the pool can let another connection reset it while the hijacked connection is still in use. Keep the buffered reader owned by the escaped hijacked connection in keep-open mode. Add a regression test that forces reader-pool reuse and verifies buffered data remains available after the handler returns.
This commit is contained in:
@@ -2667,7 +2667,9 @@ func hijackConnHandler(ctx *RequestCtx, r io.Reader, c net.Conn, s *Server, h Hi
|
||||
hjc := s.acquireHijackConn(r, c)
|
||||
h(hjc)
|
||||
|
||||
if br, ok := r.(*bufio.Reader); ok {
|
||||
// When the caller keeps using the hijacked connection after return,
|
||||
// the buffered reader must remain owned by that escaped connection.
|
||||
if br, ok := r.(*bufio.Reader); ok && !s.KeepHijackedConns {
|
||||
releaseReader(s, br)
|
||||
}
|
||||
if !s.KeepHijackedConns {
|
||||
|
||||
@@ -2933,6 +2933,51 @@ func TestRequestCtxHijackReduceMemoryUsage(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestCtxHijackKeepHijackedConnsKeepsReaderOutOfPool(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := &Server{
|
||||
KeepHijackedConns: true,
|
||||
}
|
||||
ctx := &RequestCtx{s: s}
|
||||
|
||||
firstConn := &readWriter{}
|
||||
firstConn.r.WriteString("first")
|
||||
secondConn := &readWriter{}
|
||||
secondConn.r.WriteString("second")
|
||||
|
||||
br := bufio.NewReaderSize(firstConn, 1)
|
||||
|
||||
var hijacked net.Conn
|
||||
hijackConnHandler(ctx, br, firstConn, s, func(c net.Conn) {
|
||||
hijacked = c
|
||||
})
|
||||
|
||||
if hijacked == nil {
|
||||
t.Fatal("expected hijacked connection")
|
||||
}
|
||||
|
||||
if v := s.readerPool.Get(); v != nil {
|
||||
v.(*bufio.Reader).Reset(secondConn)
|
||||
}
|
||||
|
||||
buf := make([]byte, len("first"))
|
||||
if _, err := io.ReadFull(hijacked, buf); err != nil {
|
||||
t.Fatalf("unexpected read error from hijacked connection: %v", err)
|
||||
}
|
||||
if string(buf) != "first" {
|
||||
t.Fatalf("unexpected hijacked data %q. Expecting %q", buf, "first")
|
||||
}
|
||||
|
||||
if err := hijacked.Close(); err != nil {
|
||||
t.Fatalf("unexpected close error from hijacked connection: %v", err)
|
||||
}
|
||||
|
||||
if v := s.readerPool.Get(); v != nil {
|
||||
t.Fatal("did not expect hijacked reader to be released after close")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestCtxHijackNoResponse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user