Files
EasyTier/easytier-go/internal/engine/conn.go
T
KKRainbow 3d0c9c3ca5 chore(go): use lowercase module import path (#2560)
* chore(go): use lowercase module import path
* ci: scope checks for Go module changes
2026-09-10 15:13:55 +08:00

206 lines
4.1 KiB
Go

package engine
import (
"context"
"errors"
"io"
"net"
"net/netip"
"os"
"sync"
"sync/atomic"
"time"
"github.com/easytier/easytier/easytier-go/internal/coreabi"
)
const maxDataPlaneTransfer = 1024 * 1024
type streamConn struct {
instance *Instance
resource coreabi.ResourceID
local netip.AddrPort
peer netip.AddrPort
readMu sync.Mutex
writeMu sync.Mutex
closed atomic.Bool
closeOnce sync.Once
closeDone chan struct{}
closeErr error
}
func newStreamConn(
instance *Instance,
result coreabi.OperationResult,
) *streamConn {
return &streamConn{
instance: instance,
resource: result.Resource,
local: result.Local,
peer: result.Peer,
closeDone: make(chan struct{}),
}
}
func (conn *streamConn) Read(buffer []byte) (int, error) {
if len(buffer) == 0 {
return 0, nil
}
conn.readMu.Lock()
defer conn.readMu.Unlock()
if conn.closed.Load() {
return 0, net.ErrClosed
}
maximum := len(buffer)
if maximum > maxDataPlaneTransfer {
maximum = maxDataPlaneTransfer
}
result, err := conn.instance.performOperation(
context.Background(),
coreabi.OperationTCPRead,
func(
callCtx context.Context,
core dataPlaneCore,
) (coreabi.OperationID, error) {
return core.SubmitTCPRead(
callCtx,
conn.resource,
uint32(maximum),
)
},
)
if err != nil {
return 0, normalizeDeadlineError(err)
}
n := copy(buffer, result.Data)
if n == 0 && result.EOF {
return 0, io.EOF
}
return n, nil
}
func (conn *streamConn) Write(buffer []byte) (int, error) {
if len(buffer) == 0 {
return 0, nil
}
conn.writeMu.Lock()
defer conn.writeMu.Unlock()
if conn.closed.Load() {
return 0, net.ErrClosed
}
written := 0
for written < len(buffer) {
end := written + maxDataPlaneTransfer
if end > len(buffer) {
end = len(buffer)
}
chunk := buffer[written:end]
result, err := conn.instance.performOperation(
context.Background(),
coreabi.OperationTCPWrite,
func(
callCtx context.Context,
core dataPlaneCore,
) (coreabi.OperationID, error) {
return core.SubmitTCPWrite(
callCtx,
conn.resource,
chunk,
)
},
)
if err != nil {
return written, normalizeDeadlineError(err)
}
if result.Length <= 0 || result.Length > len(chunk) {
return written, io.ErrShortWrite
}
written += result.Length
}
return written, nil
}
func (conn *streamConn) Close() error {
conn.closeOnce.Do(func() {
conn.closed.Store(true)
conn.closeErr = conn.instance.closeDataPlaneResource(conn.resource)
close(conn.closeDone)
})
<-conn.closeDone
return conn.closeErr
}
func (conn *streamConn) LocalAddr() net.Addr {
return net.TCPAddrFromAddrPort(conn.local)
}
func (conn *streamConn) RemoteAddr() net.Addr {
return net.TCPAddrFromAddrPort(conn.peer)
}
func (conn *streamConn) SetDeadline(deadline time.Time) error {
if conn.closed.Load() {
return net.ErrClosed
}
return conn.instance.setDataPlaneResourceDeadline(
conn.resource,
coreabi.DeadlineRead|coreabi.DeadlineWrite,
deadline,
)
}
func (conn *streamConn) SetReadDeadline(deadline time.Time) error {
if conn.closed.Load() {
return net.ErrClosed
}
return conn.instance.setDataPlaneResourceDeadline(
conn.resource,
coreabi.DeadlineRead,
deadline,
)
}
func (conn *streamConn) SetWriteDeadline(deadline time.Time) error {
if conn.closed.Load() {
return net.ErrClosed
}
return conn.instance.setDataPlaneResourceDeadline(
conn.resource,
coreabi.DeadlineWrite,
deadline,
)
}
func normalizeDeadlineError(err error) error {
if errors.Is(err, context.DeadlineExceeded) {
return os.ErrDeadlineExceeded
}
return err
}
func (instance *Instance) Dial(
ctx context.Context,
peer netip.AddrPort,
) (net.Conn, error) {
timeout, err := contextTimeoutMillis(ctx)
if err != nil {
return nil, normalizeDeadlineError(err)
}
result, err := instance.performOperation(
ctx,
coreabi.OperationTCPConnect,
func(
callCtx context.Context,
core dataPlaneCore,
) (coreabi.OperationID, error) {
return core.SubmitTCPConnect(callCtx, peer, timeout)
},
)
if err != nil {
return nil, normalizeDeadlineError(err)
}
return newStreamConn(instance, result), nil
}