diff options
Diffstat (limited to 'internal/proxy/tee.go')
| -rw-r--r-- | internal/proxy/tee.go | 29 |
1 files changed, 21 insertions, 8 deletions
diff --git a/internal/proxy/tee.go b/internal/proxy/tee.go index f6a385a..bd34d19 100644 --- a/internal/proxy/tee.go +++ b/internal/proxy/tee.go @@ -22,8 +22,9 @@ const maxCaptureBytes = 10 << 20 // 10 MiB // here (call sites synchronize on the request/response boundary itself). type teeConn struct { net.Conn - mu sync.Mutex - buf []byte + mu sync.Mutex + buf []byte + truncated bool } func newTeeConn(c net.Conn) *teeConn { @@ -34,12 +35,18 @@ func (c *teeConn) Read(p []byte) (int, error) { n, err := c.Conn.Read(p) if n > 0 { c.mu.Lock() - if room := maxCaptureBytes - len(c.buf); room > 0 { + room := maxCaptureBytes - len(c.buf) + if room > 0 { end := n if end > room { end = room } c.buf = append(c.buf, p[:end]...) + if end < n { + c.truncated = true + } + } else { + c.truncated = true } c.mu.Unlock() } @@ -47,13 +54,19 @@ func (c *teeConn) Read(p []byte) (int, error) { } // Take returns the bytes read since the last Take call (or since the -// connection was created) and resets the buffer. -func (c *teeConn) Take() []byte { +// connection was created), whether that capture hit maxCaptureBytes and +// had to drop bytes off the end, and resets both. A caller storing this +// as an "exact" capture must fold truncated into that decision - bytes +// silently missing from the tail is exactly the kind of gap "exact" +// promises doesn't exist, and callers finding it separately or not at +// all is how a request smuggling investigation loses the one thing it +// was looking for. +func (c *teeConn) Take() (data []byte, truncated bool) { c.mu.Lock() defer c.mu.Unlock() - out := c.buf - c.buf = nil - return out + out, trunc := c.buf, c.truncated + c.buf, c.truncated = nil, false + return out, trunc } // teeListener wraps a net.Listener so every accepted connection is |