sing/common/canceler/packet_timeout.go
2023-04-10 12:23:24 +08:00

74 lines
1.7 KiB
Go

package canceler
import (
"context"
"time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type TimeoutPacketConn struct {
N.PacketConn
timeout time.Duration
cancel common.ContextCancelCauseFunc
active time.Time
}
func NewTimeoutPacketConn(ctx context.Context, conn N.PacketConn, timeout time.Duration) (context.Context, PacketConn) {
ctx, cancel := common.ContextWithCancelCause(ctx)
return ctx, &TimeoutPacketConn{
PacketConn: conn,
timeout: timeout,
cancel: cancel,
}
}
func (c *TimeoutPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
for {
err = c.PacketConn.SetReadDeadline(time.Now().Add(c.timeout))
if err != nil {
return M.Socksaddr{}, err
}
destination, err = c.PacketConn.ReadPacket(buffer)
if err == nil {
c.active = time.Now()
return
} else if E.IsTimeout(err) {
if time.Since(c.active) > c.timeout {
c.cancel(err)
return
}
} else {
return M.Socksaddr{}, err
}
}
}
func (c *TimeoutPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
err := c.PacketConn.WritePacket(buffer, destination)
if err == nil {
c.active = time.Now()
}
return err
}
func (c *TimeoutPacketConn) Timeout() time.Duration {
return c.timeout
}
func (c *TimeoutPacketConn) SetTimeout(timeout time.Duration) {
c.timeout = timeout
c.PacketConn.SetReadDeadline(time.Now())
}
func (c *TimeoutPacketConn) Close() error {
return c.PacketConn.Close()
}
func (c *TimeoutPacketConn) Upstream() any {
return c.PacketConn
}