Skip to content

Commit 4ef87ea

Browse files
authored
middleware: fix httpFancyWriter.ReadFrom double-counting bytes with Tee (#1085)
When a Tee writer is set, ReadFrom calls io.Copy(&f.basicWriter, r), which routes each write through basicWriter.Write. Write already increments basicWriter.bytes, so the line f.basicWriter.bytes += int(n) that followed the io.Copy double-counted every byte, causing BytesWritten() to return twice the actual value. Remove the redundant addition. The non-tee path is unaffected because it delegates to the underlying ResponseWriter's ReaderFrom (bypassing Write) and must still add n itself. Fixes #1067 Signed-off-by: alliasgher <alliasgher123@gmail.com>
1 parent a54874f commit 4ef87ea

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

middleware/wrap_writer.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,10 @@ func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error {
208208

209209
func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) {
210210
if f.basicWriter.tee != nil {
211+
// Route through basicWriter.Write so that data is also written to the
212+
// tee writer. basicWriter.Write already increments basicWriter.bytes,
213+
// so we must NOT add n again here (that would double-count).
211214
n, err := io.Copy(&f.basicWriter, r)
212-
f.basicWriter.bytes += int(n)
213215
return n, err
214216
}
215217
rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)

middleware/wrap_writer_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"net/http"
66
"net/http/httptest"
7+
"strings"
78
"testing"
89
)
910

@@ -84,3 +85,27 @@ func TestBasicWriterDiscardsWritesToOriginalResponseWriter(t *testing.T) {
8485
assertEqual(t, 11, wrap.BytesWritten())
8586
})
8687
}
88+
89+
// TestHttpFancyWriterReadFromByteCountWithTee is a regression test for
90+
// https://github.com/go-chi/chi/issues/1067.
91+
// httpFancyWriter.ReadFrom was adding n to basicWriter.bytes even when the
92+
// write went through basicWriter.Write (which already increments the counter),
93+
// resulting in double-counting the bytes when a Tee writer was set.
94+
func TestHttpFancyWriterReadFromByteCountWithTee(t *testing.T) {
95+
original := &httptest.ResponseRecorder{
96+
HeaderMap: make(http.Header),
97+
Body: new(bytes.Buffer),
98+
}
99+
f := &httpFancyWriter{basicWriter: basicWriter{ResponseWriter: original}}
100+
101+
var teeBuf bytes.Buffer
102+
f.Tee(&teeBuf)
103+
104+
const input = "hello world"
105+
n, err := f.ReadFrom(strings.NewReader(input))
106+
assertNoError(t, err)
107+
assertEqual(t, int64(len(input)), n)
108+
// Before the fix, BytesWritten() returned 22 (double-counted).
109+
assertEqual(t, len(input), f.BytesWritten())
110+
assertEqual(t, []byte(input), teeBuf.Bytes())
111+
}

0 commit comments

Comments
 (0)