Address PR review: cancel on non-EOF copy errors, stricter cap test

- netrelay: only propagate CloseWrite on clean io.EOF; cancel both sides
  on any other copy error so a short write, reset, or broken pipe can't
  leave the opposite direction blocked.
- TestTCPCapPrefersTombstonedForEviction: assert both live pre-cap
  entries survive, not just that the tombstone is gone, so a regression
  that evicts a live entry instead of the tombstone is caught.
This commit is contained in:
Viktor Liu
2026-04-21 14:15:04 +02:00
parent 10da236dae
commit be434e1eb2
2 changed files with 19 additions and 2 deletions

View File

@@ -108,7 +108,7 @@ func Relay(ctx context.Context, a, b io.ReadWriteCloser, opts Options) (aToB, bT
go func() {
defer wg.Done()
aToB, errAToB = copyTracked(b, a, &lastActivity)
if halfCloseSupported {
if halfCloseSupported && isCleanEOF(errAToB) {
halfClose(b)
} else {
cancel()
@@ -118,7 +118,7 @@ func Relay(ctx context.Context, a, b io.ReadWriteCloser, opts Options) (aToB, bT
go func() {
defer wg.Done()
bToA, errBToA = copyTracked(a, b, &lastActivity)
if halfCloseSupported {
if halfCloseSupported && isCleanEOF(errBToA) {
halfClose(a)
} else {
cancel()
@@ -209,6 +209,14 @@ func halfClose(conn io.ReadWriteCloser) {
}
}
// isCleanEOF reports whether a copy terminated on a graceful end-of-stream.
// Only in that case is it correct to propagate the EOF via CloseWrite on the
// peer; any other error means the flow is broken and both directions should
// tear down.
func isCleanEOF(err error) bool {
return err == nil || errors.Is(err, io.EOF)
}
func isExpectedCopyError(err error) bool {
return errors.Is(err, net.ErrClosed) ||
errors.Is(err, context.Canceled) ||