Files
T

56 lines
1.5 KiB
Go

package netconn
import (
"context"
"io"
"net"
"sync"
"time"
)
// BiCopy закрывает оба conn, как только копирование в любую сторону завершится.
func BiCopy(ctx context.Context, c1, c2 net.Conn, errf func(format string, v ...any)) (int64, int64) {
ctx2, cancel := context.WithCancel(ctx)
setDeadline := func(t time.Time, what string) {
if err := c1.SetDeadline(t); err != nil && errf != nil {
errf("BiCopy: c1 %s: %v", what, err)
}
if err := c2.SetDeadline(t); err != nil && errf != nil {
errf("BiCopy: c2 %s: %v", what, err)
}
}
hookDone := make(chan struct{})
stopOnCancel := context.AfterFunc(ctx2, func() {
defer close(hookDone)
setDeadline(time.Now(), "SetDeadline")
})
var wg sync.WaitGroup
var c1FromC2, c2FromC1 int64
wg.Go(func() {
defer cancel()
n, err := io.Copy(c1, c2)
c1FromC2 = n
if err != nil && errf != nil {
errf("BiCopy: c1<-c2: %v", err)
}
})
wg.Go(func() {
defer cancel()
n, err := io.Copy(c2, c1)
c2FromC1 = n
if err != nil && errf != nil {
errf("BiCopy: c2<-c1: %v", err)
}
})
wg.Wait()
// Дедлайны сбрасываются только после того, как хук отменён или доработал - иначе
// он взвёлся бы следом и оставил conn'ы просроченными навсегда.
if !stopOnCancel() {
<-hookDone
}
setDeadline(time.Time{}, "clear deadline")
return c1FromC2, c2FromC1
}