mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-11 05:27:47 +00:00
fix(pools): return pooled gzip reader on Reset error
GetGzipReader got a reader from the pool, Reset it, and on error returned a fresh gzip.NewReader while never returning the pooled reader to the pool — one leaked reader per reset error (non-gzip input), silently draining the pool. Return the pooled reader without Close (Close panics because the decompressor is nil until a header parses); the next caller's Reset reinitialises it. Test: TestGetGzipReader_ResetErrorPath (error path leaves the pool usable for subsequent valid gzip reads).
This commit is contained in:
+6
-1
@@ -78,7 +78,12 @@ func PutGzipWriter(gz *gzip.Writer) {
|
|||||||
func GetGzipReader(r io.Reader) (*gzip.Reader, error) {
|
func GetGzipReader(r io.Reader) (*gzip.Reader, error) {
|
||||||
gr := gzipReaderPool.Get().(*gzip.Reader)
|
gr := gzipReaderPool.Get().(*gzip.Reader)
|
||||||
if err := gr.Reset(r); err != nil {
|
if err := gr.Reset(r); err != nil {
|
||||||
// If reset fails, create a new reader
|
// Reset failed (e.g. non-gzip data): return the pooled reader so it
|
||||||
|
// isn't leaked, and hand back a fresh one for this call. We must not
|
||||||
|
// call gr.Close() here — the decompressor is nil until a header
|
||||||
|
// parses, so Close would panic. The next GetGzipReader's Reset
|
||||||
|
// reinitialises it.
|
||||||
|
gzipReaderPool.Put(gr)
|
||||||
return gzip.NewReader(r)
|
return gzip.NewReader(r)
|
||||||
}
|
}
|
||||||
return gr, nil
|
return gr, nil
|
||||||
|
|||||||
@@ -415,3 +415,31 @@ func BenchmarkWithoutPool(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGetGzipReader_ResetErrorPath exercises the path where the pooled reader
|
||||||
|
// fails to Reset (non-gzip input). The pooled reader must be returned to the
|
||||||
|
// pool and a usable reader handed back; a subsequent valid gzip read must
|
||||||
|
// still succeed so the pool is not left in a broken state.
|
||||||
|
func (suite *BufferPoolTestSuite) TestGetGzipReader_ResetErrorPath() {
|
||||||
|
t := suite.T()
|
||||||
|
// Non-gzip input -> Reset fails -> fresh reader returned (with error).
|
||||||
|
gr1, err := GetGzipReader(strings.NewReader("not gzip data at all"))
|
||||||
|
assert.Error(t, err, "expected error decoding non-gzip data")
|
||||||
|
if gr1 != nil {
|
||||||
|
PutGzipReader(gr1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid gzip input afterwards must still work, proving the pool is usable.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
zw := gzip.NewWriter(&buf)
|
||||||
|
_, werr := zw.Write([]byte("hello world"))
|
||||||
|
assert.NoError(t, werr)
|
||||||
|
assert.NoError(t, zw.Close())
|
||||||
|
|
||||||
|
gr2, err := GetGzipReader(bytes.NewReader(buf.Bytes()))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer PutGzipReader(gr2)
|
||||||
|
decoded, rerr := io.ReadAll(gr2)
|
||||||
|
assert.NoError(t, rerr)
|
||||||
|
assert.Equal(t, "hello world", string(decoded))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user