77 lines
1.2 KiB
Go
77 lines
1.2 KiB
Go
package net
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/go-gost/core/common/bufpool"
|
|
xio "github.com/go-gost/x/internal/io"
|
|
)
|
|
|
|
const (
|
|
// tcpWaitTimeout implements a TCP half-close timeout.
|
|
tcpWaitTimeout = 30 * time.Second
|
|
)
|
|
|
|
func Pipe(ctx context.Context, rw1, rw2 io.ReadWriter) error {
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(2)
|
|
|
|
ch := make(chan error, 2)
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
if err := pipeBuffer(rw1, rw2, bufferSize/2); err != nil {
|
|
ch <- err
|
|
}
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
if err := pipeBuffer(rw2, rw1, bufferSize/2); err != nil {
|
|
ch <- err
|
|
}
|
|
}()
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
wg.Wait()
|
|
close(done)
|
|
}()
|
|
|
|
select {
|
|
case <-done:
|
|
case <-ctx.Done():
|
|
return nil
|
|
}
|
|
|
|
select {
|
|
case err := <-ch:
|
|
return err
|
|
default:
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func pipeBuffer(dst io.ReadWriter, src io.ReadWriter, bufferSize int) error {
|
|
buf := bufpool.Get(bufferSize)
|
|
defer bufpool.Put(buf)
|
|
|
|
_, err := io.CopyBuffer(dst, src, buf)
|
|
|
|
// Do the upload/download side TCP half-close.
|
|
if cr, ok := src.(xio.CloseRead); ok {
|
|
cr.CloseRead()
|
|
}
|
|
if cw, ok := dst.(xio.CloseWrite); ok {
|
|
cw.CloseWrite()
|
|
}
|
|
|
|
// Set TCP half-close timeout.
|
|
xio.SetReadDeadline(dst, time.Now().Add(tcpWaitTimeout))
|
|
|
|
return err
|
|
}
|